diff --git a/LICENSES b/LICENSES index 64b56064..5e043234 100644 --- a/LICENSES +++ b/LICENSES @@ -20,19 +20,25 @@ b) Cassowary constraint solving algorithm ( examples ) SPDX-License-Identifier: BSD 3-Clause "New" or "Revised" License Copyright (c) 2013, Nucleic Development Team -fonts ( only for running the examples ): +fonts ( needed for the Material3/Fluent2 skin ) a) Material3 Icons Code: https://github.com/google/material-design-icons SPDX-License-Identifier: Apache License 2.0 -b) Fluent2 Icons +b) Roboto Fonts + + Code: https://github.com/googlefonts/roboto-classic + SPDX-License-Identifier: SIL Open Font License 1.1 + Copyright 2011 The Roboto Project Authors + +c) Fluent2 Icons Code: https://github.com/microsoft/fluentui-system-icons SPDX-License-Identifier: MIT License -c) Segoe-UI Fonts +d) Segoe-UI Fonts Code: https://github.com/mrbvrz/segoe-ui-linux License: https://github.com/mrbvrz/segoe-ui-linux/blob/master/license.txt diff --git a/designsystems/fluent2/QskFluent2Skin.cpp b/designsystems/fluent2/QskFluent2Skin.cpp index 64bd1cc6..a696d15c 100644 --- a/designsystems/fluent2/QskFluent2Skin.cpp +++ b/designsystems/fluent2/QskFluent2Skin.cpp @@ -703,9 +703,10 @@ void Editor::setupListViewMetrics() for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) setBoxBorderMetrics( Q::Cell | state | Q::Selected, { 3_px, 0, 0, 0 } ); + #if 1 - // taken from M3 - what are the actual values, TODO ... - setPadding( Q::Cell, { 16_px, 12_px, 16_px, 12_px } ); + setPadding( Q::Cell, 4_px ); + setStrutSize( Q::Cell, -1, 40_px ); #endif } @@ -1561,9 +1562,6 @@ void Editor::setupSpinBoxMetrics() setSymbol( Q::UpIndicator, symbol( "chevron_up" ) ); setSymbol( Q::DownIndicator, symbol( "chevron_down" ) ); - setPadding( Q::UpPanel, { 0, 1_px, 0, 0 } ); - setPadding( Q::DownPanel, { 0, 0, 0, 1_px } ); - #if 0 // QskSpinBox::Pressed is missing yet setBoxBorderMetrics( Q::Panel | Q::Pressed, { 1_px, 1_px, 1_px, 2_px } ); @@ -1880,7 +1878,6 @@ void Editor::setupTextFieldColors( { using Q = QskTextField; using A = QskAspect; - using W = QskFluent2Skin; const auto& pal = theme.palette; diff --git a/designsystems/fluent2/README b/designsystems/fluent2/README new file mode 100644 index 00000000..81de77c0 --- /dev/null +++ b/designsystems/fluent2/README @@ -0,0 +1,26 @@ +Definitions ( where possible ) taken from: + - https://www.figma.com/file/NAWMapFlXnoOb86Q2H5GKr/Windows-UI-(Community) + +The Figma model uses the "Segoe Fluent Icons" [1]. Unfortunately its license is +too restrictive for being included in the QSkinny project. Fortunately similar +icons are also available from the "Fluent UI System Icons" project [2]. + +However glyph names/numbers and the corresponding unicode values do not match +and can't be taken 1:1 from the Figma model. + +"Fluent UI System Icons" offers different icon packages ( as font ): + + - FluentSystemIcons-Light.ttf + - FluentSystemIcons-Regular.ttf + - FluentSystemIcons-Filled.ttf + - FluentSystemIcons-Resizable.ttf + +For some reason ( might be a bug in the downloaded version ) the checkmark +glyph offered from the "Regular" font ( pixelsize 12 ) is different +( works better ) to the one from the "Resizable" font. + +That's why we decided to use the "Regular" font for now. + +[1] https://learn.microsoft.com/en-us/windows/apps/design/style/segoe-fluent-icons-font +[2] https://github.com/microsoft/fluentui-system-icons + diff --git a/designsystems/fluent2/icons/FluentSystemIcons-Regular.ttf b/designsystems/fluent2/icons/FluentSystemIcons-Regular.ttf new file mode 100644 index 00000000..fc3c648a Binary files /dev/null and b/designsystems/fluent2/icons/FluentSystemIcons-Regular.ttf differ diff --git a/designsystems/fluent2/icons/README b/designsystems/fluent2/icons/README deleted file mode 100644 index a45cea97..00000000 --- a/designsystems/fluent2/icons/README +++ /dev/null @@ -1,10 +0,0 @@ -SVGs have been taken from https://github.com/microsoft/fluentui-system-icons/tree/main/assets. - -Icons are available in different sizes. As SVGs can be scaled we only need -one version of them - chosing the '12'. - -As we are replacing the colors of the SVGs using graphic filters we set the -color in the SVGs manually to black ( instead of #212121 ). So they are in -line with icons coming from somewhere else. - -Names have been shortened ( ic_fluent_xyz_16_regular.svg -> xyz.svg ) diff --git a/designsystems/fluent2/icons/checkmark.svg b/designsystems/fluent2/icons/checkmark.svg deleted file mode 100644 index f87de9aa..00000000 --- a/designsystems/fluent2/icons/checkmark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/designsystems/fluent2/icons/chevron_down.svg b/designsystems/fluent2/icons/chevron_down.svg deleted file mode 100644 index 8ac9ed11..00000000 --- a/designsystems/fluent2/icons/chevron_down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/designsystems/fluent2/icons/chevron_up.svg b/designsystems/fluent2/icons/chevron_up.svg deleted file mode 100644 index 28426e1d..00000000 --- a/designsystems/fluent2/icons/chevron_up.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/designsystems/fluent2/icons/icon2qvg.sh b/designsystems/fluent2/icons/icon2qvg.sh deleted file mode 100755 index c562ca17..00000000 --- a/designsystems/fluent2/icons/icon2qvg.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /bin/sh - -if [ $# -eq 0 ]; then - echo "Usage $0 file ..." - exit 1 -fi - -for file in $* -do - base=`basename -s .svg $file` - echo "${base}.svg -> qvg/${base}.qvg" - svg2qvg ${base}.svg qvg/${base}.qvg -done - -exit $status diff --git a/designsystems/fluent2/icons/qvg/checkmark.qvg b/designsystems/fluent2/icons/qvg/checkmark.qvg index 40ab0c75..e1e03bf0 100644 Binary files a/designsystems/fluent2/icons/qvg/checkmark.qvg and b/designsystems/fluent2/icons/qvg/checkmark.qvg differ diff --git a/designsystems/fluent2/icons/qvg/chevron_down.qvg b/designsystems/fluent2/icons/qvg/chevron_down.qvg index a8d90d49..d7957b36 100644 Binary files a/designsystems/fluent2/icons/qvg/chevron_down.qvg and b/designsystems/fluent2/icons/qvg/chevron_down.qvg differ diff --git a/designsystems/fluent2/icons/qvg/chevron_up.qvg b/designsystems/fluent2/icons/qvg/chevron_up.qvg index 11f3abc4..f5446475 100644 Binary files a/designsystems/fluent2/icons/qvg/chevron_up.qvg and b/designsystems/fluent2/icons/qvg/chevron_up.qvg differ diff --git a/designsystems/fluent2/icons/symbol2qvg.sh b/designsystems/fluent2/icons/symbol2qvg.sh new file mode 100755 index 00000000..21be8a82 --- /dev/null +++ b/designsystems/fluent2/icons/symbol2qvg.sh @@ -0,0 +1,11 @@ +#! /bin/sh + +function symbol2qvg { + fontfile=FluentSystemIcons-Regular.ttf + glyph2qvg ${fontfile} 12 $1 qvg/$2.qvg +} + + +symbol2qvg 1724 checkmark +symbol2qvg 1792 chevron_down +symbol2qvg 1816 chevron_up diff --git a/designsystems/fusion/QskFusionSkin.cpp b/designsystems/fusion/QskFusionSkin.cpp index 4dd17a66..1879782a 100644 --- a/designsystems/fusion/QskFusionSkin.cpp +++ b/designsystems/fusion/QskFusionSkin.cpp @@ -1338,7 +1338,7 @@ void Editor::setupListView() using P = QPalette; // padding for each cell - setPadding( Q::Cell, QskMargins( 4, 0 ) ); + setPadding( Q::Cell, QskMargins( 4, 4 ) ); for ( auto state : { A::NoState, Q::Disabled } ) { diff --git a/designsystems/material3/QskMaterial3Icons.qrc b/designsystems/material3/QskMaterial3Icons.qrc index 72725b7a..d30484f9 100644 --- a/designsystems/material3/QskMaterial3Icons.qrc +++ b/designsystems/material3/QskMaterial3Icons.qrc @@ -1,21 +1,10 @@ - - - - icons/qvg/check_small.qvg - icons/qvg/combo-box-arrow-closed.qvg - icons/qvg/combo-box-arrow-open.qvg - icons/qvg/segmented-button-check.qvg - icons/qvg/switchbutton-checked.qvg - icons/qvg/switchbutton-unchecked.qvg - - - icons/qvg/add.qvg icons/qvg/arrow_drop_down.qvg icons/qvg/arrow_drop_up.qvg icons/qvg/check.qvg + icons/qvg/close.qvg icons/qvg/remove.qvg diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 0d3b8f19..1c811a1c 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -227,7 +227,6 @@ void Editor::setupCheckBox() setStrutSize( Q::Box, 18_px, 18_px ); setBoxBorderMetrics( Q::Box, 2_px ); setBoxShape( Q::Box, 2_px ); - setPadding( Q::Box, 3_px ); // "icon size" QskShadowMetrics shadowMetrics( 12_px, 0.0 ); shadowMetrics.setShapeMode( QskShadowMetrics::Ellipse ); @@ -240,7 +239,7 @@ void Editor::setupCheckBox() setGraphicRole( Q::Indicator | Q::Disabled | Q::Checked, QskMaterial3Skin::GraphicRoleSurface ); - const auto checkMark = symbol( "check_small" ); + const auto checkMark = symbol( "check" ); for ( auto state : { A::NoState, Q::Disabled } ) { const auto aspect = Q::Indicator | Q::Checked | state; @@ -337,7 +336,7 @@ void Editor::setupComboBox() setColor( Q::Text, m_pal.onSurface ); setFontRole( Q::Text, BodyLarge ); - setStrutSize( Q::StatusIndicator, 12_px, 12_px ); + setStrutSize( Q::StatusIndicator, 24_px, 24_px ); setGraphicRole( Q::StatusIndicator, QskMaterial3Skin::GraphicRoleOnSurface ); setAlignment( Q::StatusIndicator, Qt::AlignRight | Qt::AlignVCenter ); @@ -346,14 +345,13 @@ void Editor::setupComboBox() setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 ); - setGraphicRole( Q::Icon, QskMaterial3Skin::GraphicRoleOnSurface38 ); - setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); - setGraphicRole( Q::StatusIndicator, QskMaterial3Skin::GraphicRoleOnSurface38 ); + setGraphicRole( Q::StatusIndicator | Q::Disabled, QskMaterial3Skin::GraphicRoleOnSurface38 ); + setGraphicRole( Q::Icon | Q::Disabled, QskMaterial3Skin::GraphicRoleOnSurface38 ); - setSymbol( Q::StatusIndicator, symbol( "combo-box-arrow-closed" ) ); - setSymbol( Q::StatusIndicator | Q::PopupOpen, symbol( "combo-box-arrow-open" ) ); + setSymbol( Q::StatusIndicator, symbol( "arrow_drop_down" ) ); + setSymbol( Q::StatusIndicator | Q::PopupOpen, symbol( "arrow_drop_up" ) ); } void Editor::setupBox() @@ -1135,8 +1133,8 @@ void Editor::setupSwitchButton() setStrutSize( Q::Icon, { 16_px, 16_px } ); setPadding( Q::Icon, 6_px ); - setSymbol( Q::Icon, symbol( "switchbutton-unchecked" ) ); - setSymbol( Q::Icon | Q::Checked, symbol( "switchbutton-checked" ) ); + setSymbol( Q::Icon, symbol( "close" ) ); + setSymbol( Q::Icon | Q::Checked, symbol( "check" ) ); setGraphicRole( Q::Icon, QskMaterial3Skin::GraphicRoleSurfaceContainerHighest ); diff --git a/designsystems/material3/icons/LICENSE b/designsystems/material3/icons/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/designsystems/material3/icons/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/designsystems/material3/icons/MaterialSymbolsOutlined.ttf b/designsystems/material3/icons/MaterialSymbolsOutlined.ttf new file mode 100644 index 00000000..d446767b Binary files /dev/null and b/designsystems/material3/icons/MaterialSymbolsOutlined.ttf differ diff --git a/designsystems/material3/icons/add.svg b/designsystems/material3/icons/add.svg deleted file mode 100644 index b0570646..00000000 --- a/designsystems/material3/icons/add.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/designsystems/material3/icons/arrow_drop_down.svg b/designsystems/material3/icons/arrow_drop_down.svg deleted file mode 100644 index 37d90c90..00000000 --- a/designsystems/material3/icons/arrow_drop_down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/designsystems/material3/icons/arrow_drop_up.svg b/designsystems/material3/icons/arrow_drop_up.svg deleted file mode 100644 index 24f0831a..00000000 --- a/designsystems/material3/icons/arrow_drop_up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/designsystems/material3/icons/check.svg b/designsystems/material3/icons/check.svg deleted file mode 100644 index 3be223be..00000000 --- a/designsystems/material3/icons/check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/designsystems/material3/icons/check_small.svg b/designsystems/material3/icons/check_small.svg deleted file mode 100644 index e7a84071..00000000 --- a/designsystems/material3/icons/check_small.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/material3/icons/combo-box-arrow-closed.svg b/designsystems/material3/icons/combo-box-arrow-closed.svg deleted file mode 100644 index c288b426..00000000 --- a/designsystems/material3/icons/combo-box-arrow-closed.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/material3/icons/combo-box-arrow-open.svg b/designsystems/material3/icons/combo-box-arrow-open.svg deleted file mode 100644 index 4138c2cd..00000000 --- a/designsystems/material3/icons/combo-box-arrow-open.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/material3/icons/icon2qvg.sh b/designsystems/material3/icons/icon2qvg.sh deleted file mode 100755 index c562ca17..00000000 --- a/designsystems/material3/icons/icon2qvg.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /bin/sh - -if [ $# -eq 0 ]; then - echo "Usage $0 file ..." - exit 1 -fi - -for file in $* -do - base=`basename -s .svg $file` - echo "${base}.svg -> qvg/${base}.qvg" - svg2qvg ${base}.svg qvg/${base}.qvg -done - -exit $status diff --git a/designsystems/material3/icons/qvg/add.qvg b/designsystems/material3/icons/qvg/add.qvg index de7e62c6..51de8ec1 100644 Binary files a/designsystems/material3/icons/qvg/add.qvg and b/designsystems/material3/icons/qvg/add.qvg differ diff --git a/designsystems/material3/icons/qvg/arrow_drop_down.qvg b/designsystems/material3/icons/qvg/arrow_drop_down.qvg index 496378d6..69e0301c 100644 Binary files a/designsystems/material3/icons/qvg/arrow_drop_down.qvg and b/designsystems/material3/icons/qvg/arrow_drop_down.qvg differ diff --git a/designsystems/material3/icons/qvg/arrow_drop_up.qvg b/designsystems/material3/icons/qvg/arrow_drop_up.qvg index 09b1976e..d48d7e7a 100644 Binary files a/designsystems/material3/icons/qvg/arrow_drop_up.qvg and b/designsystems/material3/icons/qvg/arrow_drop_up.qvg differ diff --git a/designsystems/material3/icons/qvg/check.qvg b/designsystems/material3/icons/qvg/check.qvg index 8fb73515..e72e8896 100644 Binary files a/designsystems/material3/icons/qvg/check.qvg and b/designsystems/material3/icons/qvg/check.qvg differ diff --git a/designsystems/material3/icons/qvg/check_small.qvg b/designsystems/material3/icons/qvg/check_small.qvg deleted file mode 100644 index 4b92ce5b..00000000 Binary files a/designsystems/material3/icons/qvg/check_small.qvg and /dev/null differ diff --git a/designsystems/material3/icons/qvg/close.qvg b/designsystems/material3/icons/qvg/close.qvg new file mode 100644 index 00000000..fa97264e Binary files /dev/null and b/designsystems/material3/icons/qvg/close.qvg differ diff --git a/designsystems/material3/icons/qvg/combo-box-arrow-closed.qvg b/designsystems/material3/icons/qvg/combo-box-arrow-closed.qvg deleted file mode 100644 index 83f33a45..00000000 Binary files a/designsystems/material3/icons/qvg/combo-box-arrow-closed.qvg and /dev/null differ diff --git a/designsystems/material3/icons/qvg/combo-box-arrow-open.qvg b/designsystems/material3/icons/qvg/combo-box-arrow-open.qvg deleted file mode 100644 index 1e757789..00000000 Binary files a/designsystems/material3/icons/qvg/combo-box-arrow-open.qvg and /dev/null differ diff --git a/designsystems/material3/icons/qvg/remove.qvg b/designsystems/material3/icons/qvg/remove.qvg index 6598d5b0..b038b3fa 100644 Binary files a/designsystems/material3/icons/qvg/remove.qvg and b/designsystems/material3/icons/qvg/remove.qvg differ diff --git a/designsystems/material3/icons/qvg/segmented-button-check.qvg b/designsystems/material3/icons/qvg/segmented-button-check.qvg deleted file mode 100644 index 1b0148e3..00000000 Binary files a/designsystems/material3/icons/qvg/segmented-button-check.qvg and /dev/null differ diff --git a/designsystems/material3/icons/qvg/switchbutton-checked.qvg b/designsystems/material3/icons/qvg/switchbutton-checked.qvg deleted file mode 100644 index c927ddb3..00000000 Binary files a/designsystems/material3/icons/qvg/switchbutton-checked.qvg and /dev/null differ diff --git a/designsystems/material3/icons/qvg/switchbutton-unchecked.qvg b/designsystems/material3/icons/qvg/switchbutton-unchecked.qvg deleted file mode 100644 index b33f61e4..00000000 Binary files a/designsystems/material3/icons/qvg/switchbutton-unchecked.qvg and /dev/null differ diff --git a/designsystems/material3/icons/remove.svg b/designsystems/material3/icons/remove.svg deleted file mode 100644 index 9fa856a5..00000000 --- a/designsystems/material3/icons/remove.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/designsystems/material3/icons/segmented-button-check.svg b/designsystems/material3/icons/segmented-button-check.svg deleted file mode 100644 index b0b66ac9..00000000 --- a/designsystems/material3/icons/segmented-button-check.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/material3/icons/switchbutton-checked.svg b/designsystems/material3/icons/switchbutton-checked.svg deleted file mode 100644 index 7f4d9275..00000000 --- a/designsystems/material3/icons/switchbutton-checked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/material3/icons/switchbutton-unchecked.svg b/designsystems/material3/icons/switchbutton-unchecked.svg deleted file mode 100644 index a9e2ee7e..00000000 --- a/designsystems/material3/icons/switchbutton-unchecked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/material3/icons/symbol2qvg.sh b/designsystems/material3/icons/symbol2qvg.sh new file mode 100755 index 00000000..46597425 --- /dev/null +++ b/designsystems/material3/icons/symbol2qvg.sh @@ -0,0 +1,15 @@ +#! /bin/sh + +function symbol2qvg { + fontfile=MaterialSymbolsOutlined.ttf + glyph2qvg ${fontfile} 24 $1 qvg/$2.qvg +} + +symbol2qvg 3067 check +symbol2qvg 3084 close + +symbol2qvg 5022 arrow_drop_down +symbol2qvg 5023 arrow_drop_up + +symbol2qvg 3082 add +symbol2qvg 3080 remove diff --git a/examples/iotdashboard/MainItem.cpp b/examples/iotdashboard/MainItem.cpp index d8b7c46f..25db2176 100644 --- a/examples/iotdashboard/MainItem.cpp +++ b/examples/iotdashboard/MainItem.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/examples/qvgviewer/MainWindow.cpp b/examples/qvgviewer/MainWindow.cpp index 001c6c87..5d63002e 100644 --- a/examples/qvgviewer/MainWindow.cpp +++ b/examples/qvgviewer/MainWindow.cpp @@ -31,8 +31,8 @@ class GraphicLabel : public QskGraphicLabel Inverted }; - GraphicLabel( const QskGraphic& graphic, QQuickItem* parent = nullptr ) - : QskGraphicLabel( graphic, parent ) + GraphicLabel( QQuickItem* parent = nullptr ) + : QskGraphicLabel( parent ) { setMargins( 10 ); setPanel( true ); @@ -74,7 +74,7 @@ class GraphicLabel : public QskGraphicLabel MainWindow::MainWindow() { - auto label = new GraphicLabel( QskGraphicIO::read( QString( ":/qvg/Tux.qvg" ) ) ); + auto label = new GraphicLabel(); auto invertButton = new QskPushButton( "Inverted" ); invertButton->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); @@ -98,6 +98,12 @@ MainWindow::MainWindow() setGraphicRoles( qskSkinManager->skin() ); } +void MainWindow::setGraphic( const QString& path ) +{ + if ( auto label = findChild< QskGraphicLabel* >() ) + label->setGraphic( QskGraphicIO::read( path ) ); +} + void MainWindow::setGraphicRoles( QskSkin* skin ) { // substituting black diff --git a/examples/qvgviewer/MainWindow.h b/examples/qvgviewer/MainWindow.h index 2d8eb8b5..f486af93 100644 --- a/examples/qvgviewer/MainWindow.h +++ b/examples/qvgviewer/MainWindow.h @@ -15,6 +15,7 @@ class MainWindow : public QskWindow public: MainWindow(); + void setGraphic( const QString& ); private Q_SLOTS: void setGraphicRoles( QskSkin* ); diff --git a/examples/qvgviewer/main.cpp b/examples/qvgviewer/main.cpp index 9dc3ede2..fc1421bb 100644 --- a/examples/qvgviewer/main.cpp +++ b/examples/qvgviewer/main.cpp @@ -27,6 +27,7 @@ int main( int argc, char* argv[] ) focusIndicator->setObjectName( "FocusIndicator" ); MainWindow window; + window.setGraphic( ( argc > 1 ) ? argv[1] : ":/qvg/Tux.qvg" ); window.resize( 600, 400 ); window.addItem( focusIndicator ); window.show(); diff --git a/playground/CMakeLists.txt b/playground/CMakeLists.txt index 30ddc7d1..592c4938 100644 --- a/playground/CMakeLists.txt +++ b/playground/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(dials) add_subdirectory(dialogbuttons) add_subdirectory(fonts) add_subdirectory(gradients) +add_subdirectory(iconbrowser) add_subdirectory(invoker) add_subdirectory(shadows) add_subdirectory(shapes) diff --git a/playground/iconbrowser/CMakeLists.txt b/playground/iconbrowser/CMakeLists.txt new file mode 100644 index 00000000..945f9ef4 --- /dev/null +++ b/playground/iconbrowser/CMakeLists.txt @@ -0,0 +1,15 @@ +############################################################################ +# QSkinny - Copyright (C) The authors +# SPDX-License-Identifier: BSD-3-Clause +############################################################################ + +set(SOURCES + GlyphListView.h GlyphListView.cpp + main.cpp +) + +qt_add_resources(SOURCES + fonts.qrc +) + +qsk_add_example(iconbrowser ${SOURCES}) diff --git a/playground/iconbrowser/GlyphListView.cpp b/playground/iconbrowser/GlyphListView.cpp new file mode 100644 index 00000000..b93e1e01 --- /dev/null +++ b/playground/iconbrowser/GlyphListView.cpp @@ -0,0 +1,130 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "GlyphListView.h" + +#include +#include +#include + +#include +#include + +constexpr int glyphSize = 20; + +GlyphListView::GlyphListView( QQuickItem* parentItem ) + : Inherited( parentItem ) +{ +} + +GlyphListView::GlyphListView( const QString& fontName, QQuickItem* parentItem ) + : GlyphListView( parentItem ) +{ + setFont( QRawFont( fontName, 16 ) ); + setFontRoleHint( Text, QskFontRole::Title ); +} + +void GlyphListView::setFontPath( const QString& fontPath ) +{ + setFont( QRawFont( fontPath, 16 ) ); +} + +void GlyphListView::setFont( const QRawFont& font ) +{ + m_glyphTable.setIconFont( font ); + + const auto names = m_glyphTable.nameTable(); + + m_nameTable.clear(); + m_nameTable.reserve( names.size() ); + + m_maxNameWidth = 0; + + { + const QFontMetricsF fm( effectiveFont( Text ) ); + + for ( auto it = names.constBegin(); it != names.constEnd(); ++it ) + { + m_nameTable.insert( it.value(), it.key() ); + + const qreal w = qskHorizontalAdvance( fm, it.key() ); + if ( w > m_maxNameWidth ) + m_maxNameWidth = w; + } + } + + updateScrollableSize(); + update(); +} + +QRawFont GlyphListView::font() const +{ + return m_glyphTable.iconFont(); +} + +int GlyphListView::rowCount() const +{ + return m_glyphTable.glyphCount() - 1; +} + +int GlyphListView::columnCount() const +{ + return 3; +} + +qreal GlyphListView::columnWidth( int col ) const +{ + switch( col ) + { + case 0: + { + const QFontMetricsF fm( effectiveFont( Text ) ); + return qskHorizontalAdvance( fm, "999999" ); + } + + case 1: + { + const auto hint = strutSizeHint( Cell ); + const auto padding = paddingHint( Cell ); + + const qreal w = glyphSize + padding.left() + padding.right(); + return qMax( w, hint.width() ); + } + + case 2: + return m_maxNameWidth; + } + + return 0; +} + +qreal GlyphListView::rowHeight() const +{ + const auto hint = strutSizeHint( Cell ); + const auto padding = paddingHint( Cell ); + + const qreal h = glyphSize + padding.top() + padding.bottom(); + return qMax( h, hint.height() ); +} + +QVariant GlyphListView::valueAt( int row, int col ) const +{ + const auto glyphIndex = row + 1; + switch( col ) + { + case 0: + return QVariant::fromValue( QString::number( glyphIndex ) ); + + case 1: + return QVariant::fromValue( m_glyphTable.glyphGraphic( glyphIndex ) ); + + case 2: + return QVariant::fromValue( m_nameTable.value( glyphIndex ) ); + } + + return QVariant(); +} + +#include "moc_GlyphListView.cpp" diff --git a/playground/iconbrowser/GlyphListView.h b/playground/iconbrowser/GlyphListView.h new file mode 100644 index 00000000..8e9239b6 --- /dev/null +++ b/playground/iconbrowser/GlyphListView.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include +#include +#include + +class GlyphListView : public QskListView +{ + Q_OBJECT + using Inherited = QskListView; + + public: + GlyphListView( QQuickItem* = nullptr); + GlyphListView( const QString&, QQuickItem* = nullptr); + + void setFontPath( const QString& ); + void setFont( const QRawFont& ); + QRawFont font() const; + + int rowCount() const override; + int columnCount() const override; + + virtual qreal columnWidth( int col ) const override; + virtual qreal rowHeight() const override; + + QVariant valueAt( int row, int col ) const override; + + private: + QskGlyphTable m_glyphTable; + + QHash< uint, QString > m_nameTable; + int m_maxNameWidth = 0; +}; diff --git a/playground/iconbrowser/fonts.qrc b/playground/iconbrowser/fonts.qrc new file mode 100644 index 00000000..1bcb1237 --- /dev/null +++ b/playground/iconbrowser/fonts.qrc @@ -0,0 +1,11 @@ + + + + ../../designsystems/fluent2/icons/FluentSystemIcons-Regular.ttf + fonts/Font Awesome 6 Free-Regular-400.otf + fonts/IcoMoon-Free.ttf + ../../designsystems/material3/icons/MaterialSymbolsOutlined.ttf + fonts/Octicons.ttf + + + diff --git a/playground/iconbrowser/fonts/Font Awesome 6 Free-Regular-400.otf b/playground/iconbrowser/fonts/Font Awesome 6 Free-Regular-400.otf new file mode 100644 index 00000000..17a83506 Binary files /dev/null and b/playground/iconbrowser/fonts/Font Awesome 6 Free-Regular-400.otf differ diff --git a/playground/iconbrowser/fonts/IcoMoon-Free.ttf b/playground/iconbrowser/fonts/IcoMoon-Free.ttf new file mode 100644 index 00000000..56919449 Binary files /dev/null and b/playground/iconbrowser/fonts/IcoMoon-Free.ttf differ diff --git a/playground/iconbrowser/fonts/LICENSES b/playground/iconbrowser/fonts/LICENSES new file mode 100644 index 00000000..12faa7bf --- /dev/null +++ b/playground/iconbrowser/fonts/LICENSES @@ -0,0 +1,16 @@ +a) Font Awesome + + Code: https://fontawesome.com/ + SPDX-License-Identifier: OFL-1.1-RFN + Copyright (c) 2024 Fonticons, Inc. + +b) IcoMoon + + Code: https://github.com/Keyamoon/IcoMoon-Free + SPDX-License-Identifier: CC-BY-4.0 + +c) Octicons + + Code: https://github.com/primer/octicons + SPDX-License-Identifier: MIT License + diff --git a/playground/iconbrowser/fonts/Octicons.ttf b/playground/iconbrowser/fonts/Octicons.ttf new file mode 100755 index 00000000..9e091053 Binary files /dev/null and b/playground/iconbrowser/fonts/Octicons.ttf differ diff --git a/playground/iconbrowser/main.cpp b/playground/iconbrowser/main.cpp new file mode 100644 index 00000000..2a9ad9ad --- /dev/null +++ b/playground/iconbrowser/main.cpp @@ -0,0 +1,100 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "GlyphListView.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace +{ + class Header : public QskLinearBox + { + Q_OBJECT + + public: + Header( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, parent ) + { + setPaddingHint( QskBox::Panel, 5 ); + + initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Fixed ); + setPanel( true ); + + m_comboBox = new QskComboBox( this ); + + const auto entries = QDir( ":iconfonts" ).entryInfoList(); + for ( const auto& entry : entries ) + m_comboBox->addOption( QUrl(), entry.absoluteFilePath() ); + + m_comboBox->setCurrentIndex( 0 ); + + connect( m_comboBox, &QskComboBox::currentIndexChanged, + this, &Header::comboBoxChanged ); + } + + QString fontPath() const { return m_comboBox->currentText(); } + + Q_SIGNALS: + void fontChanged( const QString& ); + + private: + void comboBoxChanged() { Q_EMIT fontChanged( fontPath() ); } + + QskComboBox* m_comboBox; + }; + + class MainView : public QskMainView + { + public: + MainView( QQuickItem* parent = nullptr ) + : QskMainView( parent ) + { + auto listView = new GlyphListView( this ); + auto header = new Header( this ); + + setHeader( header ); + setBody( listView ); + + listView->setFontPath( header->fontPath() ); + + connect( header, &Header::fontChanged, + listView, &GlyphListView::setFontPath ); + } + }; +} + +int main( int argc, char* argv[] ) +{ +#ifdef ITEM_STATISTICS + QskObjectCounter counter( true ); +#endif + + QGuiApplication app( argc, argv ); + + SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); + + auto mainView = new QskMainView(); + mainView->setBody( new GlyphListView( ":/fonts/Octicons.ttf" ) ); + + QskWindow window; + window.addItem( new MainView() ); + window.addItem( new QskFocusIndicator() ); + window.resize( 600, 400 ); + window.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7fde8b5e..c86f6783 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,6 +77,8 @@ list(APPEND PRIVATE_HEADERS list(APPEND HEADERS graphic/QskColorFilter.h + graphic/QskGlyphGraphicProvider.h + graphic/QskGlyphTable.h graphic/QskGraphic.h graphic/QskGraphicImageProvider.h graphic/QskGraphicIO.h @@ -91,6 +93,8 @@ list(APPEND HEADERS list(APPEND SOURCES graphic/QskColorFilter.cpp + graphic/QskGlyphGraphicProvider.cpp + graphic/QskGlyphTable.cpp graphic/QskGraphic.cpp graphic/QskGraphicImageProvider.cpp graphic/QskGraphicIO.cpp diff --git a/src/controls/QskItem.cpp b/src/controls/QskItem.cpp index c0b83dde..5a5a0940 100644 --- a/src/controls/QskItem.cpp +++ b/src/controls/QskItem.cpp @@ -1007,6 +1007,10 @@ void QskItem::itemChange( QQuickItem::ItemChange change, case QQuickItem::ItemRotationHasChanged: case QQuickItem::ItemAntialiasingHasChanged: case QQuickItem::ItemDevicePixelRatioHasChanged: +#if QT_VERSION >= QT_VERSION_CHECK( 6, 9, 0 ) + case QQuickItem::ItemScaleHasChanged: + case QQuickItem::ItemTransformHasChanged: +#endif { break; } diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index e252ff5e..d988820c 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -29,6 +29,25 @@ QRhi* qskRenderingHardwareInterface( const QQuickWindow* window ) return nullptr; } +bool qskIsOpenGLWindow( const QQuickWindow* window ) +{ + if ( window == nullptr ) + return false; + + const auto renderer = window->rendererInterface(); + switch( renderer->graphicsApi() ) + { + case QSGRendererInterface::OpenGL: +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + case QSGRendererInterface::OpenGLRhi: +#endif + return true; + + default: + return false; + } +} + QRectF qskItemRect( const QQuickItem* item ) { auto d = QQuickItemPrivate::get( item ); diff --git a/src/controls/QskQuick.h b/src/controls/QskQuick.h index 165bec4b..7befde16 100644 --- a/src/controls/QskQuick.h +++ b/src/controls/QskQuick.h @@ -27,6 +27,7 @@ template< typename T > class QList; */ QSK_EXPORT QRhi* qskRenderingHardwareInterface( const QQuickWindow* ); +QSK_EXPORT bool qskIsOpenGLWindow( const QQuickWindow* ); QSK_EXPORT bool qskIsItemInDestructor( const QQuickItem* ); QSK_EXPORT bool qskIsItemComplete( const QQuickItem* ); diff --git a/src/graphic/QskGlyphGraphicProvider.cpp b/src/graphic/QskGlyphGraphicProvider.cpp new file mode 100644 index 00000000..9986476c --- /dev/null +++ b/src/graphic/QskGlyphGraphicProvider.cpp @@ -0,0 +1,84 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskGlyphGraphicProvider.h" +#include "QskGraphic.h" +#include "QskGlyphTable.h" + +#include +#include +#include + +class QskGlyphGraphicProvider::PrivateData +{ + public: + QskGlyphTable glyphTable; +}; + +QskGlyphGraphicProvider::QskGlyphGraphicProvider( QObject* parent ) + : QskGraphicProvider( parent ) + , m_data( new PrivateData ) +{ +} + +QskGlyphGraphicProvider::~QskGlyphGraphicProvider() +{ +} + +void QskGlyphGraphicProvider::setIconFont( const QRawFont& font ) +{ + m_data->glyphTable.setIconFont( font ); +} + +QRawFont QskGlyphGraphicProvider::iconFont() const +{ + return m_data->glyphTable.iconFont(); +} + +QskGraphic QskGlyphGraphicProvider::glyphGraphic( uint index ) const +{ + return m_data->glyphTable.glyphGraphic( index ); +} + +const QskGraphic* QskGlyphGraphicProvider::loadGraphic( const QString& key ) const +{ + if ( const auto index = glyphIndex( key ) ) + { + const auto graphic = glyphGraphic( index ); + if ( !graphic.isNull() ) + return new QskGraphic( graphic ); + } + + return nullptr; +} + +uint QskGlyphGraphicProvider::glyphIndex( const QString& key ) const +{ + const auto& table = m_data->glyphTable; + + if ( ( table.glyphCount() > 0 ) && !key.isEmpty() ) + { + if ( key.startsWith( '#' ) ) + { + bool ok; + const auto glyphIndex = key.toUInt( &ok ); + if ( ok ) + return glyphIndex; + } + else if ( key.startsWith( "U+" ) ) + { + bool ok; + const char32_t code = key.mid( 2 ).toUInt( &ok, 16 ); + if ( ok ) + return table.codeToIndex( code ); + } + else + { + return table.nameToIndex( key ); + } + } + + return 0; +} diff --git a/src/graphic/QskGlyphGraphicProvider.h b/src/graphic/QskGlyphGraphicProvider.h new file mode 100644 index 00000000..15523093 --- /dev/null +++ b/src/graphic/QskGlyphGraphicProvider.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_GLYPH_GRAPHIC_PROVIDER_H +#define QSK_GLYPH_GRAPHIC_PROVIDER_H + +#include "QskGraphicProvider.h" + +class QRawFont; + +class QSK_EXPORT QskGlyphGraphicProvider : public QskGraphicProvider +{ + using Inherited = QskGraphicProvider; + + public: + QskGlyphGraphicProvider( QObject* parent = nullptr ); + ~QskGlyphGraphicProvider() override; + + void setIconFont( const QRawFont& ); + QRawFont iconFont() const; + + QskGraphic glyphGraphic( uint glyphIndex ) const; + + protected: + const QskGraphic* loadGraphic( const QString& ) const override final; + virtual uint glyphIndex( const QString& ) const; + + private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/graphic/QskGlyphTable.cpp b/src/graphic/QskGlyphTable.cpp new file mode 100644 index 00000000..13cd7607 --- /dev/null +++ b/src/graphic/QskGlyphTable.cpp @@ -0,0 +1,502 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskGlyphTable.h" +#include "QskGraphic.h" +#include "QskInternalMacros.h" + +#include +#include +#include +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +typedef QHash< QString, uint > GlyphNameTable; + +/* + The "parsers" below do no validation checks as the font has + already been validated by QRawFont + + Hope QRawFont will extend its API some day ( the underlying + font libraries do support glyph names ) and we can remove the nasty + code below. see https://bugreports.qt.io/browse/QTBUG-132629 + */ +namespace PostTableParser +{ + // https://learn.microsoft.com/en-us/typography/opentype/spec/post + + static inline QString toString( const uint8_t* s ) + { + // Pascal string. Valid characters are: [A–Z] [a–z] [0–9] '.' '_' + return QString::fromUtf8( + reinterpret_cast< const char* > ( s + 1 ), *s ); + } + + static GlyphNameTable glyphNames( const QByteArray& blob ) + { + GlyphNameTable names; + + const auto* glyphData = reinterpret_cast< const uint16_t* >( blob.constData() + 32 ); + if ( const auto nglyphs = qFromBigEndian( *glyphData++ ) ) + { + QVarLengthArray< const uint8_t* > strings; + strings.reserve( nglyphs ); + + const auto from = reinterpret_cast< const uint8_t* >( glyphData + nglyphs ); + const auto to = reinterpret_cast< const uint8_t* >( blob.data() + blob.size() ); + + if ( to > from ) + { + for ( auto s = from; s < to; s += *s + 1 ) + strings += s; + + for ( int i = 0; i < nglyphs; i++ ) + { + const int idx = qFromBigEndian( glyphData[i] ) - 258; + + if ( idx >= 0 && idx < strings.size() ) + names.insert( toString( strings[idx] ), i ); + } + } + } + + return names; + } +} + +namespace CFFTableParser +{ + // https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf + + static uint32_t offsetAt( const uint8_t* d, int size ) + { + switch (size) + { + case 0: + return 0; + case 1: + return d[0]; + case 2: + return ( d[0] << 8 ) | d[1]; + case 3: + return ( d[0] << 16 ) | ( d[1] << 8 ) | d[2]; + default: + return ( d[0] << 24 ) | ( d[1] << 16 ) | ( d[2] << 8 ) | d[3]; + } + } + + static int indexDataSize( const uint8_t* d ) + { + const int nitems = ( d[0] << 8 ) | d[1]; + if ( nitems == 0 ) + return 2; + + const int size = d[2]; + + const uint8_t* lx = d + 3 + nitems * size; + return lx + size - 1 + offsetAt( lx, size ) - d; + } + + static const uint8_t* indexData( const uint8_t* d ) + { + const int nitems = ( d[0] << 8 ) | d[1]; + d += 2; + + if ( nitems == 0 ) + return d; + + const int size = d[0]; + + const uint8_t* _contents = d + ( nitems + 1 ) * size; + return _contents + offsetAt(d + 1, size); + } + + static const uint8_t* skipHeader( const uint8_t* d ) + { + return d + d[2]; + } + + static const uint8_t* skipIndex( const uint8_t* d ) + { + return d + indexDataSize( d ); + } + + static QVector< int > stringIds( const uint8_t* data, int nglyphs ) + { + const int format = data[0]; + + QVector< int > _sids; + _sids += 0; + + const uint8_t* p = data + 1; + switch( format ) + { + case 0: + { + for (; _sids.size() < nglyphs; p += 2) + { + int sid = ( p[0] << 8 ) | p[1]; + _sids += sid; + } + break; + } + case 1: + { + for (; _sids.size() < nglyphs; p += 3) + { + const int sid = ( p[0] << 8 ) | p[1]; + const int n = p[2]; + + for ( int i = 0; i <= n; i++ ) + _sids += sid + i; + } + break; + } + case 2: + { + for (; _sids.size() < nglyphs; p += 4) + { + const int sid = ( p[0] << 8 ) | p[1]; + const int n = ( p[2] << 8 ) | p[3]; + + for ( int i = 0; i <= n; i++ ) + _sids += sid + i; + } + break; + } + } + + return _sids; + } + + static int dictValue( const uint8_t* d, int op ) + { + /* + see https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf + We are intersted in the offset ( operator 15 ). + */ + + const uint8_t* data = indexData( d ); + + int value = 0; + + Q_FOREVER + { + // operand + const auto valueType = data[0]; + + if ( valueType == 28 ) + { + value = ( data[1] << 8 ) | data[2]; + data += 3; + } + else if ( valueType == 29 ) + { + value = ( data[1] << 24 ) | ( data[2] << 16 ) + | ( data[3] << 8 ) | data[4]; + + data += 5; + } + else if ( valueType == 30 ) + { + /* + Assuming, that an offset is never a double + we skip the operand without reading it + */ + while( ++data ) + { + const int b = *data; + if ( ( b & 0x0f ) == 0x0f || ( b & 0xf0 ) == 0xf0 ) + break; // 0xf nibble indicating the end + } + + data++; + } + else if ( valueType >= 31 && valueType <= 246 ) + { + value = data[0] - 139; + data++; + } + else if ( valueType >= 247 && valueType <= 250 ) + { + value = ( ( data[0] - 247 ) << 8 ) + data[1] + 108; + data += 2; + } + else if ( valueType >= 251 && valueType <= 254 ) + { + value = -( ( data[0] - 251 ) << 8 ) - data[1] - 108; + data += 2; + } + + // operator + + if ( op == data[0] ) + return value; + + data += ( data[0] == 12 ) ? 2 : 1; + } + + return 0; // default value + } + + class StringTable + { + public: + StringTable( const uint8_t* data ) + { + const int nitems = ( data[0] << 8 ) | data[1]; + if ( nitems == 0 ) + { + _contents = data + 2; + _offset = nullptr; + _offsize = 0; + } + else + { + _offsize = data[2]; + _offset = data + 3; + + _contents = _offset + nitems * _offsize + _offsize - 1; + } + } + + inline QString operator[]( int which ) const + { + const auto x = _offset + which * _offsize; + + const auto d1 = _contents + offsetAt(x, _offsize); + const auto d2 = _contents + offsetAt(x + _offsize, _offsize); + + return QString::fromUtf8( + reinterpret_cast< const char* >( d1 ), d2 - d1 ); + } + + private: + const uint8_t* _contents = nullptr; + const uint8_t* _offset = nullptr; + int _offsize = -1; + }; + + static GlyphNameTable glyphNames( const QByteArray& blob, int glyphCount ) + { + auto data = reinterpret_cast< const uint8_t* >( blob.constData() ); + + auto nameIndex = skipHeader( data ); + auto dictIndex = skipIndex( nameIndex ); + auto stringIndex = skipIndex( dictIndex ); + + auto charset = data + dictValue( dictIndex, 15 ); // 15: charset offset + + const QVector< int > sids = stringIds( charset, glyphCount ); + + const StringTable table( stringIndex ); + + GlyphNameTable names; + + for ( int i = 0; i < glyphCount; i++ ) + { + /* + The first 391 SIDs are reserved for standard strings + ( Appendix A ) that are not from the charset table. + */ + + const auto idx = sids[i] - 391; + + if ( idx >= 0 ) + names.insert( table[idx], i ); + } + + return names; + } +} + +static uint qskGlyphCount( const QRawFont& font ) +{ + /* + we could also read the count from the "maxp" table: + https://learn.microsoft.com/en-us/typography/opentype/spec/maxp + */ + + const auto fontEngine = QRawFontPrivate::get( font )->fontEngine; + return fontEngine ? fontEngine->glyphCount() : 0; +} + +static qreal qskPixelSize( const QRawFont& font ) +{ + const auto fontEngine = QRawFontPrivate::get( font )->fontEngine; + return fontEngine ? fontEngine->fontDef.pixelSize : 0.0; +} + +static GlyphNameTable qskGlyphNameTable( const QRawFont& font ) +{ + const auto count = qskGlyphCount( font ); + if ( count > 0 ) + { + /* + The Compact Font Format ( CFF ) table has been introduced with + the OpenType specification. For not complying fonts the names + might be found in the Post table + */ + auto blob = font.fontTable( "CFF "); + if ( !blob.isEmpty() ) + return CFFTableParser::glyphNames( blob, count ); + + blob = font.fontTable( "post" ); + if ( !blob.isEmpty() ) + return PostTableParser::glyphNames( blob ); + } + + return GlyphNameTable(); +} + +static inline quint32 qskGlyphIndex( const QRawFont& font, char32_t ucs4 ) +{ + if ( qskGlyphCount( font ) == 0 ) + return 0; + + const auto idxs = font.glyphIndexesForString( + QString::fromUcs4( &ucs4, 1 ) ); + + // fingers crossed: icon fonts will map code points to single glyphs only + Q_ASSERT( idxs.size() == 1 ); + + return idxs.size() == 1 ? idxs[0] : 0; +} + +class QskGlyphTable::PrivateData +{ + public: + inline const GlyphNameTable& glyphNameTable() const + { + if ( !validNames ) + { + auto that = const_cast< PrivateData* >( this ); + that->nameTable = qskGlyphNameTable( font ); + that->validNames = true; + } + + return nameTable; + } + + QRawFont font; + GlyphNameTable nameTable; + bool validNames = false; +}; + +QskGlyphTable::QskGlyphTable() + : m_data( new PrivateData ) +{ +} + +QskGlyphTable::QskGlyphTable( const QRawFont& font ) + : QskGlyphTable() +{ + m_data->font = font; +} + +QskGlyphTable::QskGlyphTable( const QskGlyphTable& other ) + : QskGlyphTable() +{ + *m_data = *other.m_data; +} + +QskGlyphTable::~QskGlyphTable() +{ +} + +QskGlyphTable& QskGlyphTable::operator=( const QskGlyphTable& other ) +{ + if ( m_data != other.m_data ) + *m_data = *other.m_data; + + return *this; +} + +void QskGlyphTable::setIconFont( const QRawFont& font ) +{ + if ( font != m_data->font ) + { + m_data->font = font; + m_data->nameTable.clear(); + m_data->validNames = false; + } +} + +QRawFont QskGlyphTable::iconFont() const +{ + return m_data->font; +} + +QPainterPath QskGlyphTable::glyphPath( uint glyphIndex ) const +{ + QPainterPath path; + + /* + Unfortunately QRawFont::pathForGlyph runs into failing checks + when being called from a different thread - f.e the scene graph thread. + So we need to bypass QRawFont and retrieve from its fontEngine. + */ + if ( auto fontEngine = QRawFontPrivate::get( m_data->font )->fontEngine ) + { + QFixedPoint position; + quint32 idx = glyphIndex; + + fontEngine->addGlyphsToPath( &idx, &position, 1, &path, {} ); + } + + return path; +} + +QskGraphic QskGlyphTable::glyphGraphic( uint glyphIndex ) const +{ + QskGraphic graphic; + + if ( glyphIndex > 0 && qskGlyphCount( m_data->font ) > 0 ) + { + const auto path = glyphPath( glyphIndex ); + + if ( !path.isEmpty() ) + { + // vertical glyph coordinates are in the range [-sz, 0.0] + const auto sz = qskPixelSize( m_data->font ); + graphic.setViewBox( QRectF( 0.0, -sz, sz, sz ) ); + + QPainter painter( &graphic ); + painter.setRenderHint( QPainter::Antialiasing, true ); + painter.fillPath( path, Qt::black ); + } + } + + return graphic; +} + +uint QskGlyphTable::glyphCount() const +{ + return qskGlyphCount( m_data->font ); +} + +uint QskGlyphTable::codeToIndex( char32_t ucs4 ) const +{ + return qskGlyphIndex( m_data->font, ucs4 ); +} + +uint QskGlyphTable::nameToIndex( const QString& name ) const +{ + /* + Names/Codes that do not correspond to any glyph in should + be mapped to glyph index 0. + + see https://learn.microsoft.com/en-us/typography/opentype/spec/cmap + */ + return m_data->glyphNameTable().value( name.toLatin1(), 0 ); +} + +QHash< QString, uint > QskGlyphTable::nameTable() const +{ + return m_data->glyphNameTable(); +} diff --git a/src/graphic/QskGlyphTable.h b/src/graphic/QskGlyphTable.h new file mode 100644 index 00000000..c218eab0 --- /dev/null +++ b/src/graphic/QskGlyphTable.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_GLYPH_TABLE_H +#define QSK_GLYPH_TABLE_H + +#include "QskGlobal.h" +#include + +class QskGraphic; +class QRawFont; +class QString; +class QPainterPath; +class QByteArray; +class QChar; + +template< typename Key, typename T > class QHash; + +class QSK_EXPORT QskGlyphTable +{ + public: + QskGlyphTable(); + QskGlyphTable( const QRawFont& ); + QskGlyphTable( const QskGlyphTable& ); + + ~QskGlyphTable(); + + QskGlyphTable& operator=( const QskGlyphTable& ); + + bool isValid() const; + + void setIconFont( const QRawFont& ); + QRawFont iconFont() const; + + uint glyphCount() const; + + QPainterPath glyphPath( uint glyphIndex ) const; + QskGraphic glyphGraphic( uint glyphIndex ) const; + + /* + Most icon fonts use code points from the Unicode Private Use Areas (PUA) + ( see https://en.wikipedia.org/wiki/Private_Use_Areas ): + + - [0x00e000, 0x00f8ff] + - [0x0f0000, 0x0ffffd] + - [0x100000, 0x10fffd] + + Note that QChar is 16-bit entity only that does not cover the higher PUA blocks. + */ + uint codeToIndex( char32_t ) const; + + /* + True/OpenType fonts often include a table with glyph names, that can be used + instead of the glyph index + */ + uint nameToIndex( const QString& ) const; + + QHash< QString, uint > nameTable() const; + + private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +inline bool QskGlyphTable::isValid() const +{ + return glyphCount() > 0; +} + +#endif diff --git a/src/graphic/QskGraphic.cpp b/src/graphic/QskGraphic.cpp index 3093cf84..74921db7 100644 --- a/src/graphic/QskGraphic.cpp +++ b/src/graphic/QskGraphic.cpp @@ -907,7 +907,7 @@ void QskGraphic::drawPath( const QPainterPath& path ) if ( painter == nullptr ) return; - m_data->addCommand( QskPainterCommand( path ) ); + addCommand( QskPainterCommand( path ) ); m_data->commandTypes |= QskGraphic::VectorData; if ( !path.isEmpty() ) @@ -938,7 +938,7 @@ void QskGraphic::drawPixmap( const QRectF& rect, if ( painter == nullptr ) return; - m_data->addCommand( QskPainterCommand( rect, pixmap, subRect ) ); + addCommand( QskPainterCommand( rect, pixmap, subRect ) ); m_data->commandTypes |= QskGraphic::RasterData; const QRectF r = painter->transform().mapRect( rect ); @@ -953,7 +953,7 @@ void QskGraphic::drawImage( const QRectF& rect, const QImage& image, if ( painter == nullptr ) return; - m_data->addCommand( QskPainterCommand( rect, image, subRect, flags ) ); + addCommand( QskPainterCommand( rect, image, subRect, flags ) ); m_data->commandTypes |= QskGraphic::RasterData; const QRectF r = painter->transform().mapRect( rect ); @@ -964,7 +964,7 @@ void QskGraphic::drawImage( const QRectF& rect, const QImage& image, void QskGraphic::updateState( const QPaintEngineState& state ) { - m_data->addCommand( QskPainterCommand( state ) ); + addCommand( QskPainterCommand( state ) ); if ( state.state() & QPaintEngine::DirtyTransform ) { @@ -981,6 +981,11 @@ void QskGraphic::updateState( const QPaintEngineState& state ) } } +void QskGraphic::addCommand( const QskPainterCommand& command ) +{ + m_data->addCommand( command ); +} + void QskGraphic::updateBoundingRect( const QRectF& rect ) { QRectF br = rect; diff --git a/src/graphic/QskGraphic.h b/src/graphic/QskGraphic.h index b3834857..882ee59a 100644 --- a/src/graphic/QskGraphic.h +++ b/src/graphic/QskGraphic.h @@ -146,7 +146,9 @@ class QSK_EXPORT QskGraphic : public QPaintDevice virtual void drawImage( const QRectF&, const QImage&, const QRectF&, Qt::ImageConversionFlags ); - virtual void updateState( const QPaintEngineState& state ); + virtual void updateState( const QPaintEngineState& ); + + virtual void addCommand( const QskPainterCommand& ); private: void updateBoundingRect( const QRectF& ); diff --git a/src/graphic/QskGraphicTextureFactory.cpp b/src/graphic/QskGraphicTextureFactory.cpp index 24cff214..efaae47e 100644 --- a/src/graphic/QskGraphicTextureFactory.cpp +++ b/src/graphic/QskGraphicTextureFactory.cpp @@ -5,9 +5,38 @@ #include "QskGraphicTextureFactory.h" #include "QskTextureRenderer.h" +#include "QskQuick.h" +#include "QskInternalMacros.h" #include +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +static QSGTexture* qskCreatePaintedTexture( QQuickWindow* window, + const QSize& size, QskTextureRenderer::PaintHelper* helper ) +{ + using namespace QskTextureRenderer; + + if ( qskIsOpenGLWindow( window ) ) + { + const auto textureId = createTextureGL( window, size, helper ); + + auto texture = new QSGPlainTexture; + texture->setHasAlphaChannel( true ); + texture->setOwnsTexture( true ); + + setTextureId( window, textureId, size, texture ); + + return texture; + } + else + { + return createTextureRaster( window, size, helper ); + } +} + QskGraphicTextureFactory::QskGraphicTextureFactory() { } @@ -77,7 +106,7 @@ QSGTexture* QskGraphicTextureFactory::createTexture( QQuickWindow* window ) cons }; PaintHelper helper( m_graphic, m_colorFilter ); - return QskTextureRenderer::createPaintedTexture( window, m_size, &helper ); + return qskCreatePaintedTexture( window, m_size, &helper ); } QSize QskGraphicTextureFactory::textureSize() const diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index cfdcf6be..56fbfb95 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -7,6 +7,7 @@ #include "QskSGNode.h" #include "QskTextureRenderer.h" #include "QskInternalMacros.h" +#include "QskQuick.h" #include #include @@ -160,7 +161,7 @@ void QskPaintedNode::updateTexture( QQuickWindow* window, { auto imageNode = findImageNode( this ); - if ( ( m_renderHint == OpenGL ) && QskTextureRenderer::isOpenGLWindow( window ) ) + if ( ( m_renderHint == OpenGL ) && qskIsOpenGLWindow( window ) ) { const auto textureId = createTextureGL( window, size, nodeData ); @@ -232,5 +233,5 @@ quint32 QskPaintedNode::createTextureGL( }; PaintHelper helper( this, nodeData ); - return createPaintedTextureGL( window, size, &helper ); + return QskTextureRenderer::createTextureGL( window, size, &helper ); } diff --git a/src/nodes/QskTextureRenderer.cpp b/src/nodes/QskTextureRenderer.cpp index 77ba70cd..2628c5ef 100644 --- a/src/nodes/QskTextureRenderer.cpp +++ b/src/nodes/QskTextureRenderer.cpp @@ -30,8 +30,12 @@ static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) /* See https://bugreports.qt.io/browse/QTBUG-103929 - As we create a FBO for each update of a node we can't live - without having this ( ugly ) workaround. + QOpenGLFramebufferObject::takeTexture produces a memory leak + that can't be accepted as we create an FBO each time we update + a texture. + + The suggested workarond is to call QOpenGLSharedResourceGuard::invalidateResource() + manually. Unfortunately this is a protected method and we need this nasty hack. */ class MyFBO { @@ -64,25 +68,6 @@ static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) return textureId; } -bool QskTextureRenderer::isOpenGLWindow( const QQuickWindow* window ) -{ - if ( window == nullptr ) - return false; - - const auto renderer = window->rendererInterface(); - switch( renderer->graphicsApi() ) - { - case QSGRendererInterface::OpenGL: -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - case QSGRendererInterface::OpenGLRhi: -#endif - return true; - - default: - return false; - } -} - void QskTextureRenderer::setTextureId( QQuickWindow* window, quint32 textureId, const QSize& size, QSGTexture* texture ) { @@ -116,8 +101,8 @@ void QskTextureRenderer::setTextureId( QQuickWindow* window, #endif } -quint32 QskTextureRenderer::createPaintedTextureGL( - QQuickWindow* window, const QSize& size, QskTextureRenderer::PaintHelper* helper ) +quint32 QskTextureRenderer::createTextureGL( + QQuickWindow* window, const QSize& size, PaintHelper* helper ) { /* Binding GL_ARRAY_BUFFER/GL_ELEMENT_ARRAY_BUFFER to 0 seems to be enough. @@ -194,8 +179,8 @@ quint32 QskTextureRenderer::createPaintedTextureGL( return qskTakeTexture( fbo ); } -static QSGTexture* qskCreateTextureRaster( QQuickWindow* window, - const QSize& size, QskTextureRenderer::PaintHelper* helper ) +QSGTexture* QskTextureRenderer::createTextureRaster( QQuickWindow* window, + const QSize& size, PaintHelper* helper ) { const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0; @@ -216,24 +201,3 @@ static QSGTexture* qskCreateTextureRaster( QQuickWindow* window, return window->createTextureFromImage( image, QQuickWindow::TextureHasAlphaChannel ); } - -QSGTexture* QskTextureRenderer::createPaintedTexture( - QQuickWindow* window, const QSize& size, PaintHelper* helper ) -{ - if ( isOpenGLWindow( window ) ) - { - const auto textureId = createPaintedTextureGL( window, size, helper ); - - auto texture = new QSGPlainTexture; - texture->setHasAlphaChannel( true ); - texture->setOwnsTexture( true ); - - setTextureId( window, textureId, size, texture ); - - return texture; - } - else - { - return qskCreateTextureRaster( window, size, helper ); - } -} diff --git a/src/nodes/QskTextureRenderer.h b/src/nodes/QskTextureRenderer.h index 3b907ab2..470c5cc1 100644 --- a/src/nodes/QskTextureRenderer.h +++ b/src/nodes/QskTextureRenderer.h @@ -27,16 +27,11 @@ namespace QskTextureRenderer Q_DISABLE_COPY( PaintHelper ) }; - bool isOpenGLWindow( const QQuickWindow* ); - void setTextureId( QQuickWindow*, quint32 textureId, const QSize&, QSGTexture* ); - quint32 createPaintedTextureGL( - QQuickWindow*, const QSize&, QskTextureRenderer::PaintHelper* ); - - QSK_EXPORT QSGTexture* createPaintedTexture( - QQuickWindow* window, const QSize& size, PaintHelper* helper ); + quint32 createTextureGL( QQuickWindow*, const QSize&, PaintHelper* ); + QSGTexture* createTextureRaster( QQuickWindow*, const QSize&, PaintHelper* ); } #endif diff --git a/tools/glyph2qvg/main.cpp b/tools/glyph2qvg/main.cpp index d86a7bb6..9d1a2cbe 100644 --- a/tools/glyph2qvg/main.cpp +++ b/tools/glyph2qvg/main.cpp @@ -21,51 +21,87 @@ #include #include +enum Options +{ + ViewBox = 1 << 0, + Antialiasing = 1 << 1 +}; + static void usage( const char* appName ) { - qWarning() << "usage: " << appName << " "; + qWarning() << "usage: " << appName << " "; +} + +static QPainterPath glyphPath( const QRawFont& font, + const qreal pixelSize, const uint glyphIndex ) +{ + auto path = font.pathForGlyph( glyphIndex ); + path = path.simplified(); + path = path.translated( 0.0, pixelSize ); + + return path; +} + +static QskGraphic icon( const QPainterPath& path, + const qreal pixelSize, const int options ) +{ + QskGraphic graphic; + + if ( options & ViewBox ) + graphic.setViewBox( QRectF( 0.0, 0.0, pixelSize, pixelSize ) ); + + QPainter painter( &graphic ); + + if ( options & Antialiasing ) + painter.setRenderHint( QPainter::Antialiasing, true ); + + painter.fillPath( path, Qt::black ); + + return graphic; } int main( int argc, char* argv[] ) { - if ( argc != 4 ) + QGuiApplication app( argc, argv ); + + if ( argc != 5 ) { usage( argv[0] ); return -1; } - QGuiApplication app( argc, argv ); + bool ok; - QRawFont font( QString( argv[1] ), 16 ); + const auto pixelSize = QString( argv[2] ).toDouble( &ok ); + if ( !ok || ( pixelSize <= 0 ) ) + { + qWarning() << "invalid pixel size:" << argv[2]; + return -3; + } + + const auto glyphIndex = QString( argv[3] ).toUInt( &ok ); + if ( !ok ) + { + qWarning() << "invalid glyph index:" << argv[3]; + return -3; + } + + QRawFont font( QString( argv[1] ), pixelSize ); if ( !font.isValid() ) { qWarning() << "invalid font name:" << argv[1]; return -2; } - bool ok; - - const auto glyphIndex = QString( argv[2] ).toUInt( &ok ); - if ( !ok ) - { - qWarning() << "invalid glyph index:" << argv[2]; - return -3; - } - - const auto path = font.pathForGlyph( glyphIndex ); + const auto path = glyphPath( font, pixelSize, glyphIndex ); if ( path.isEmpty() ) { - qWarning() << "no glyph for index:" << argv[2]; + qWarning() << "no glyph for index:" << argv[3]; return -3; } - QskGraphic graphic; - - QPainter painter( &graphic ); - painter.setRenderHint( QPainter::Antialiasing, true ); - painter.fillPath( path, Qt::black ); - - QskGraphicIO::write( graphic, argv[3] ); + const auto graphic = icon( path, pixelSize, ViewBox | Antialiasing ); + QskGraphicIO::write( graphic, argv[4] ); return 0; } diff --git a/tools/svg2qvg/main.cpp b/tools/svg2qvg/main.cpp index 2695a80f..c0c0691f 100644 --- a/tools/svg2qvg/main.cpp +++ b/tools/svg2qvg/main.cpp @@ -41,6 +41,28 @@ static QRectF viewBox( QSvgRenderer& renderer ) return hasViewBox ? viewBox : QRectF( 0.0, 0.0, -1.0, -1.0 ); } +class Graphic : public QskGraphic +{ + protected: + void addCommand( const QskPainterCommand& cmd ) override + { +#if 0 + if ( cmd.type() == QskPainterCommand::State ) + { + /* + QSvgRenderer enables QPainter::Antialiasing initially + However this is something we might want to decide, + when replaying the commands. + */ + auto sd = const_cast< QskPainterCommand::StateData* >( cmd.stateData() ); + sd->renderHints &= ~QPainter::Antialiasing; + } +#endif + + QskGraphic::addCommand( cmd ); + } +}; + int main( int argc, char* argv[] ) { if ( argc != 3 ) @@ -71,7 +93,7 @@ int main( int argc, char* argv[] ) if ( !renderer.load( QString( argv[1] ) ) ) return -2; - QskGraphic graphic; + Graphic graphic; graphic.setViewBox( ::viewBox( renderer ) ); QPainter painter( &graphic );