diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 01734143..126abdde 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -175,7 +175,7 @@ jobs: } - { name: "macOS Latest Clang Qt5", - os: macos-latest, + os: macos-13, artifact: "macos_clang.7z", build_type: "Release", cc: "clang", @@ -277,7 +277,8 @@ jobs: - name: Install dependencies on macos if: startsWith(matrix.config.os, 'macos') run: | - brew install p7zip cmake ninja + find /opt/homebrew/ -name EXTERNALLY-MANAGED|xargs rm + brew install cmake ninja ninja --version cmake --version @@ -322,7 +323,7 @@ jobs: - name: Install Qt5 if: endsWith(matrix.config.name, 'Qt5') - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: "5.15.2" target: "desktop" @@ -336,7 +337,7 @@ jobs: - name: Install Qt6 if: endsWith(matrix.config.name, 'Qt6') - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: "6.5.0" target: "desktop" @@ -430,7 +431,7 @@ jobs: if: startsWith(matrix.config.name, 'Ubuntu') run: | killall iotdashboard - killall Xvfb + # killall Xvfb - name: Configure ( CMake Integration Test ) shell: bash @@ -446,6 +447,8 @@ jobs: -DQSkinny_DIR:PATH=$GITHUB_WORKSPACE/qskinny_install/lib/cmake/QSkinny - name: Build ( CMake Integration Test ) + env: + DISPLAY: ":1" shell: bash run: cmake --build qskinny_build_test --config ${{ matrix.config.build_type }} diff --git a/CMakeLists.txt b/CMakeLists.txt index e3978c24..c0f0fac1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,22 @@ install( COMPONENT Devel) + +# install cmake helper scripts +install( + FILES + ${QSK_CMAKE_DIR}/scripts/QSkinnySvg2Qvg.lin.sh + ${QSK_CMAKE_DIR}/scripts/QSkinnySvg2Qvg.mac.sh + ${QSK_CMAKE_DIR}/scripts/QSkinnySvg2Qvg.win.bat + DESTINATION + ${PACKAGE_LOCATION}/scripts + PERMISSIONS + OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + COMPONENT + Devel) + # Build other libraries if(BUILD_QML_EXPORT) add_subdirectory(qmlexport) diff --git a/LICENSES b/LICENSES index af85ab72..6c7da6d7 100644 --- a/LICENSES +++ b/LICENSES @@ -19,3 +19,13 @@ b) Cassowary constraint solving algorithm Code: https://github.com/nucleic/kiwi SPDX-License-Identifier: BSD 3-Clause "New" or "Revised" License Copyright (c) 2013, Nucleic Development Team + +c) Material3 Icons + + Code: https://github.com/marella/material-design-icons + SPDX-License-Identifier: Apache License 2.0 + +d) Fluent2 Icons + + Code: https://github.com/microsoft/fluentui-system-icons + SPDX-License-Identifier: MIT License diff --git a/cmake/QSkinnyConfig.cmake b/cmake/QSkinnyConfig.cmake index 8520eeb3..e8e7f360 100644 --- a/cmake/QSkinnyConfig.cmake +++ b/cmake/QSkinnyConfig.cmake @@ -1,3 +1,4 @@ include("${CMAKE_CURRENT_LIST_DIR}/QSkinnyTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QSkinnyTools.cmake" OPTIONAL) +include("${CMAKE_CURRENT_LIST_DIR}/QSkinnyToolsTargets.cmake" OPTIONAL) include("${CMAKE_CURRENT_LIST_DIR}/QSkinnyQmlExportTargets.cmake" OPTIONAL) diff --git a/cmake/QSkinnyTools.cmake b/cmake/QSkinnyTools.cmake index 119ca7c3..cc29c00c 100644 --- a/cmake/QSkinnyTools.cmake +++ b/cmake/QSkinnyTools.cmake @@ -8,11 +8,41 @@ function(qsk_svg2qvg SVG_FILENAME QVG_FILENAME) get_filename_component(QVG_FILENAME ${QVG_FILENAME} ABSOLUTE) get_filename_component(SVG_FILENAME ${SVG_FILENAME} ABSOLUTE) + + if(TARGET Qt6::Svg) + set(QtSvgTarget Qt6::Svg) + elseif(TARGET Qt5::Svg) + set(QtSvgTarget Qt5::Svg) + endif() + + # find svg2qvg target location + get_target_property(Svg2QvgLocation Qsk::Svg2Qvg LOCATION) + get_filename_component(Svg2QvgDirectory ${Svg2QvgLocation} DIRECTORY) + message(STATUS "Svg2QvgLocation: ${Svg2QvgLocation}") + message(STATUS "Svg2QvgDirectory: ${Svg2QvgDirectory}") + + # find qt svg target location + get_target_property(QtSvgTargetLocation ${QtSvgTarget} LOCATION) + get_filename_component(QtSvgTargetDirectory ${QtSvgTargetLocation} DIRECTORY) + message(STATUS "QtSvgTargetLocation: ${QtSvgTargetLocation}") + message(STATUS "QtSvgTargetDirectory: ${QtSvgTargetDirectory}") + + # select platform specific wrapper script + if (CMAKE_SYSTEM_NAME MATCHES "Windows") + set(script ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/scripts/QSkinnySvg2Qvg.win.bat) + elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") + set(script ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/scripts/QSkinnySvg2Qvg.mac.sh) + elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") + set(script ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/scripts/QSkinnySvg2Qvg.lin.sh) + else() + message(FATAL "Unsupported operating system") + endif() + add_custom_command( - COMMAND svg2qvg ${SVG_FILENAME} ${QVG_FILENAME} + COMMAND ${script} ${Svg2QvgLocation} ${SVG_FILENAME} ${QVG_FILENAME} ${QtSvgTargetDirectory} OUTPUT ${QVG_FILENAME} DEPENDS ${SVG_FILENAME} - WORKING_DIRECTORY $ - COMMENT "Compiling ${SVG_FILENAME} to ${QVG_FILENAME}") + COMMENT "Compiling ${SVG_FILENAME} to ${QVG_FILENAME}" + VERBATIM) endfunction() diff --git a/cmake/scripts/QSkinnySvg2Qvg.lin.sh b/cmake/scripts/QSkinnySvg2Qvg.lin.sh new file mode 100755 index 00000000..67ff94f3 --- /dev/null +++ b/cmake/scripts/QSkinnySvg2Qvg.lin.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +SVG2QVG=$1 +SVG=$2 +QVG=$3 + +LD_LIBRARY_PATH=$4:$LD_LIBRARY_PATH $SVG2QVG $SVG $QVG \ No newline at end of file diff --git a/cmake/scripts/QSkinnySvg2Qvg.mac.sh b/cmake/scripts/QSkinnySvg2Qvg.mac.sh new file mode 100755 index 00000000..c3555db1 --- /dev/null +++ b/cmake/scripts/QSkinnySvg2Qvg.mac.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +SVG2QVG=$1 +SVG=$2 +QVG=$3 + +export DYLD_LIBRARY_PATH=$4:$DYLD_LIBRARY_PATH +otool -L $SVG2QVG + +DYLD_LIBRARY_PATH=$4:$DYLD_LIBRARY_PATH $SVG2QVG $SVG $QVG \ No newline at end of file diff --git a/cmake/scripts/QSkinnySvg2Qvg.win.bat b/cmake/scripts/QSkinnySvg2Qvg.win.bat new file mode 100644 index 00000000..634b826a --- /dev/null +++ b/cmake/scripts/QSkinnySvg2Qvg.win.bat @@ -0,0 +1,6 @@ +set SVG2QVG=%1 +set SVG=%2 +set QVG=%3 +set PATH=%4;%PATH% + +%SVG2QVG% %SVG% %QVG% \ No newline at end of file diff --git a/designsystems/fluent2/CMakeLists.txt b/designsystems/fluent2/CMakeLists.txt index 2441647f..4fb73177 100644 --- a/designsystems/fluent2/CMakeLists.txt +++ b/designsystems/fluent2/CMakeLists.txt @@ -9,7 +9,7 @@ set(SOURCES QskFluent2Skin.h QskFluent2Skin.cpp QskFluent2SkinFactory.h QskFluent2SkinFactory.cpp ) -qt_add_resources(SOURCES icons.qrc) +qt_add_resources(SOURCES QskFluent2Icons.qrc) qsk_add_plugin(fluent2skin skins QskFluent2SkinFactory ${SOURCES}) set_target_properties(fluent2skin PROPERTIES DEFINE_SYMBOL QSK_FLUENT2_MAKEDLL ) diff --git a/designsystems/fluent2/QskFluent2Icons.qrc b/designsystems/fluent2/QskFluent2Icons.qrc new file mode 100644 index 00000000..7587fb4c --- /dev/null +++ b/designsystems/fluent2/QskFluent2Icons.qrc @@ -0,0 +1,7 @@ + + + icons/qvg/checkmark.qvg + icons/qvg/chevron_down.qvg + icons/qvg/chevron_up.qvg + + diff --git a/designsystems/fluent2/QskFluent2Skin.cpp b/designsystems/fluent2/QskFluent2Skin.cpp index 4ba40a5a..3ccb3bc2 100644 --- a/designsystems/fluent2/QskFluent2Skin.cpp +++ b/designsystems/fluent2/QskFluent2Skin.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -94,27 +95,53 @@ #include #include #include +#include #include #include -#include -#include +#include +#include + +static void qskFluent2InitResources() +{ + Q_INIT_RESOURCE( QskFluent2Icons ); +} + +Q_CONSTRUCTOR_FUNCTION( qskFluent2InitResources ) + +namespace Fluent2 +{ + using F = QskFontRole; + + /* + Fluent2/Windows font roles according to: + https://fluent2.microsoft.design/typography + */ + constexpr F Caption = { F::Caption, F::Normal }; + + constexpr F Body = { F::Body, F::Normal }; + constexpr F BodyStrong = { F::Body, F::High }; + constexpr F BodyStronger = { F::Body, F::VeryHigh }; + + constexpr F Subtitle = { F::Subtitle, F::Normal }; + + constexpr F Title = { F::Title, F::Normal }; + + constexpr F LargeTitle = { F::Headline, F::Normal }; + constexpr F Display = { F::Display, F::Normal }; +} namespace { - inline QFont createFont( const QString& name, qreal lineHeight, - qreal size, qreal tracking, QFont::Weight weight ) + Q_DECL_UNUSED inline double operator ""_px( long double value ) { - QFont font( name, qRound( size ) ); - font.setPixelSize( qRound( lineHeight ) ); + return qskPxToPixels( static_cast< qreal >( value ) ); + } - if( !qskFuzzyCompare( tracking, 0.0 ) ) - font.setLetterSpacing( QFont::AbsoluteSpacing, tracking ); - - font.setWeight( weight ); - - return font; + Q_DECL_UNUSED inline double operator ""_px( unsigned long long value ) + { + return qskPxToPixels( value ); } inline constexpr QRgb rgbGray( int value, qreal opacity = 1.0 ) @@ -197,6 +224,7 @@ namespace private: void setupPopup( const QskFluent2Theme& ); void setupSubWindow( const QskFluent2Theme& ); + void setupDialogSubWindow( const QskFluent2Theme& ); void setupBoxMetrics(); void setupBoxColors( QskAspect::Section, const QskFluent2Theme& ); @@ -338,6 +366,7 @@ void Editor::setupColors( QskAspect::Section section, const QskFluent2Theme& the // TODO setupPopup( theme ); setupSubWindow( theme ); + setupDialogSubWindow( theme ); } setupBoxColors( section, theme ); @@ -384,15 +413,14 @@ void Editor::setupCheckBoxMetrics() { using Q = QskCheckBox; - setStrutSize( Q::Panel, 126, 38 ); - setSpacing( Q::Panel, 8 ); + setStrutSize( Q::Panel, 126_px, 38_px ); + setSpacing( Q::Panel, 8_px ); - setStrutSize( Q::Box, { 20, 20 } ); // 18 + 2*1 border - setBoxShape( Q::Box, 4 ); // adapt to us taking the border into account - setBoxBorderMetrics( Q::Box, 1 ); - setPadding( Q::Box, 5 ); // "icon size" + setStrutSize( Q::Box, { 20_px, 20_px } ); // 18 + 2*1 border + setBoxShape( Q::Box, 4_px ); // adapt to us taking the border into account + setBoxBorderMetrics( Q::Box, 1_px ); - setFontRole( Q::Text, QskFluent2Skin::Body ); + setFontRole( Q::Text, Fluent2::Body ); } void Editor::setupCheckBoxColors( @@ -508,23 +536,23 @@ void Editor::setupComboBoxMetrics() { using Q = QskComboBox; - setStrutSize( Q::Panel, { -1, 32 } ); - setBoxBorderMetrics( Q::Panel, 1 ); - setBoxShape( Q::Panel, 3 ); - setPadding( Q::Panel, { 11, 0, 11, 0 } ); + setStrutSize( Q::Panel, { -1, 32_px } ); + setBoxBorderMetrics( Q::Panel, 1_px ); + setBoxShape( Q::Panel, 3_px ); + setPadding( Q::Panel, { 11_px, 0_px, 11_px, 0_px } ); - setStrutSize( Q::Icon, 12, 12 ); - setPadding( Q::Icon, { 0, 0, 8, 0 } ); + setStrutSize( Q::Icon, 12_px, 12_px ); + setPadding( Q::Icon, { 0, 0, 8_px, 0 } ); setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); - setFontRole( Q::Text, QskFluent2Skin::Body ); + setFontRole( Q::Text, Fluent2::Body ); - setStrutSize( Q::StatusIndicator, 12, 12 ); - setSymbol( Q::StatusIndicator, symbol( "spin-box-arrow-down" ) ); - setSymbol( Q::StatusIndicator | Q::PopupOpen, symbol( "spin-box-arrow-up" ) ); + setStrutSize( Q::StatusIndicator, 16_px, 16_px ); + setSymbol( Q::StatusIndicator, symbol( "chevron_down" ) ); + // setSymbol( Q::StatusIndicator | Q::PopupOpen, symbol( "chevron_up" ) ); // Using Focused (Pressed doesn't exist yet): - setBoxBorderMetrics( Q::Panel | Q::Focused, { 1, 1, 1, 2 } ); + setBoxBorderMetrics( Q::Panel | Q::Focused, { 1_px, 1_px, 1_px, 2_px } ); } void Editor::setupComboBoxColors( @@ -608,7 +636,7 @@ void Editor::setupComboBoxColors( void Editor::setupDialogButtonBoxMetrics() { - setPadding( QskDialogButtonBox::Panel, 20 ); + setPadding( QskDialogButtonBox::Panel, 20_px ); } void Editor::setupDialogButtonBoxColors( @@ -637,9 +665,9 @@ void Editor::setupFocusIndicatorMetrics() { using Q = QskFocusIndicator; - setBoxBorderMetrics( Q::Panel, 2 ); - setPadding( Q::Panel, 3 ); - setBoxShape( Q::Panel, 4 ); + setBoxBorderMetrics( Q::Panel, 2_px ); + setPadding( Q::Panel, 3_px ); + setBoxShape( Q::Panel, 4_px ); } void Editor::setupFocusIndicatorColors( @@ -666,10 +694,10 @@ void Editor::setupListViewMetrics() using A = QskAspect; for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) - setBoxBorderMetrics( Q::Cell | state | Q::Selected, { 3, 0, 0, 0 } ); + 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, 12, 16, 12 } ); + setPadding( Q::Cell, { 16_px, 12_px, 16_px, 12_px } ); #endif } @@ -756,18 +784,18 @@ void Editor::setupMenuMetrics() using Q = QskMenu; using A = QskAspect; - setPadding( Q::Panel, { 4, 6, 4, 6 } ); - setBoxBorderMetrics( Q::Panel, 1 ); - setBoxShape( Q::Panel, 7 ); + setPadding( Q::Panel, { 4_px, 6_px, 4_px, 6_px } ); + setBoxBorderMetrics( Q::Panel, 1_px ); + setBoxShape( Q::Panel, 7_px ); - setPadding( Q::Segment, { 0, 10, 10, 10 } ); - setSpacing( Q::Segment, 15 ); - setBoxBorderMetrics( Q::Segment | Q::Selected, { 3, 0, 0, 0 } ); + setPadding( Q::Segment, { 0, 10_px, 10_px, 10_px } ); + setSpacing( Q::Segment, 15_px ); + setBoxBorderMetrics( Q::Segment | Q::Selected, { 3_px, 0, 0, 0 } ); - setFontRole( Q::Text, QskFluent2Skin::Body ); + setFontRole( Q::Text, Fluent2::Body ); - setStrutSize( Q::Icon, 12, 12 ); - setPadding( Q::Icon, { 8, 8, 0, 8 } ); + setStrutSize( Q::Icon, 12_px, 12_px ); + setPadding( Q::Icon, { 8_px, 8_px, 0, 8_px } ); setAnimation( Q::Panel | A::Position, 100 ); } @@ -821,8 +849,8 @@ void Editor::setupPageIndicatorMetrics() using Q = QskPageIndicator; using A = QskAspect; - setPadding( Q::Panel, 5 ); - setSpacing( Q::Panel, 6 ); + setPadding( Q::Panel, 5_px ); + setSpacing( Q::Panel, 6_px ); // circles, without border @@ -839,7 +867,7 @@ void Editor::setupPageIndicatorMetrics() - Q::Hovered | Q::Selected : 7 */ - setStrutSize( Q::Bullet, 6, 6 ); + setStrutSize( Q::Bullet, 6_px, 6_px ); /* Using the margin to adjust sizes is a weired type of implementation. @@ -848,7 +876,7 @@ void Editor::setupPageIndicatorMetrics() for ( auto state : { A::NoState, Q::Disabled } ) { setMargin( Q::Bullet | state | Q::Selected, 0 ); - setMargin( Q::Bullet | state, 1 ); + setMargin( Q::Bullet | state, 1_px ); } } @@ -893,10 +921,10 @@ void Editor::setupProgressBarMetrics() using Q = QskProgressBar; using A = QskAspect; - setMetric( Q::Groove | A::Size, 1 ); + setMetric( Q::Groove | A::Size, 1_px ); setBoxShape( Q::Groove, 100, Qt::RelativeSize ); - setMetric( Q::Fill | A::Size, 3 ); + setMetric( Q::Fill | A::Size, 3_px ); setBoxShape( Q::Fill, 100, Qt::RelativeSize ); } @@ -920,9 +948,9 @@ void Editor::setupProgressRingMetrics() static constexpr QskAspect::Variation NormalSize = A::NoVariation; static constexpr QskAspect::Variation LargeSize = A::Large; - setStrutSize( Q::Fill | SmallSize, { 16, 16 } ); - setStrutSize( Q::Fill | NormalSize, { 32, 32 } ); - setStrutSize( Q::Fill | LargeSize, { 64, 64 } ); + setStrutSize( Q::Fill | SmallSize, { 16_px, 16_px } ); + setStrutSize( Q::Fill | NormalSize, { 32_px, 32_px } ); + setStrutSize( Q::Fill | LargeSize, { 64_px, 64_px } ); const auto startAngle = 90, spanAngle = -360; setArcMetrics( Q::Fill | SmallSize, startAngle, spanAngle, 1.5 ); @@ -943,19 +971,26 @@ void Editor::setupProgressRingColors( void Editor::setupPushButtonMetrics() { using Q = QskPushButton; + using A = QskAspect; using W = QskFluent2Skin; - setStrutSize( Q::Panel, { 120, 32 } ); - setBoxShape( Q::Panel, 4 ); - setBoxBorderMetrics( Q::Panel, 1 ); + const qreal h = 32_px; + setStrutSize( Q::Panel, { 120_px, h } ); + + // 60: not covered by any spec, but 120 is too big for a menu bar + setStrutSize( Q::Panel | A::Header, { 60_px, h } ); + setStrutSize( Q::Panel | A::Footer, { 60_px, h } ); + + setBoxShape( Q::Panel, 4_px ); + setBoxBorderMetrics( Q::Panel, 1_px ); setBoxBorderMetrics( Q::Panel | W::Accent | Q::Disabled, 0 ); // Fluent buttons don't really have icons, - setStrutSize( Q::Icon, 12, 12 ); - setPadding( Q::Icon, { 0, 0, 8, 0 } ); + setStrutSize( Q::Icon, 12_px, 12_px ); + setPadding( Q::Icon, { 0, 0, 8_px, 0 } ); - setFontRole( Q::Text, W::Body ); + setFontRole( Q::Text, Fluent2::Body ); } void Editor::setupPushButtonColors( @@ -1061,8 +1096,8 @@ void Editor::setupRadioBoxMetrics() { using Q = QskRadioBox; - setSpacing( Q::Button, 8 ); - setStrutSize( Q::Button, { 115, 38 } ); + setSpacing( Q::Button, 8_px ); + setStrutSize( Q::Button, { 115_px, 38_px } ); /* We do not have an indicator - states are indicated by the panel border @@ -1075,27 +1110,27 @@ void Editor::setupRadioBoxMetrics() setBoxShape( Q::CheckIndicator, 100, Qt::RelativeSize ); setBoxBorderMetrics( Q::CheckIndicator, 0 ); - setBoxBorderMetrics( Q::CheckIndicator | Q::Selected, 1 ); - setBoxBorderMetrics( Q::CheckIndicator | Q::Pressed | Q::Selected, 1 ); + setBoxBorderMetrics( Q::CheckIndicator | Q::Selected, 1_px ); + setBoxBorderMetrics( Q::CheckIndicator | Q::Pressed | Q::Selected, 1_px ); setBoxShape( Q::CheckIndicatorPanel, 100, Qt::RelativeSize ); - setStrutSize( Q::CheckIndicatorPanel, { 20, 20 } ); + setStrutSize( Q::CheckIndicatorPanel, { 20_px, 20_px } ); - setBoxBorderMetrics( Q::CheckIndicatorPanel, 1 ); + setBoxBorderMetrics( Q::CheckIndicatorPanel, 1_px ); setBoxBorderMetrics( Q::CheckIndicatorPanel | Q::Selected, 0 ); - setPadding( Q::CheckIndicatorPanel | Q::Selected, { 5, 5 } ); // indicator "strut size" + setPadding( Q::CheckIndicatorPanel | Q::Selected, { 5_px, 5_px } ); // indicator "strut size" - setPadding( Q::CheckIndicatorPanel | Q::Hovered | Q::Selected, { 4, 4 } ); // indicator "strut size" - setPadding( Q::CheckIndicatorPanel | Q::Pressed, { 7, 7 } ); // indicator "strut size" + setPadding( Q::CheckIndicatorPanel | Q::Hovered | Q::Selected, { 4_px, 4_px } ); // indicator "strut size" + setPadding( Q::CheckIndicatorPanel | Q::Pressed, { 7_px, 7_px } ); // indicator "strut size" setBoxBorderMetrics( Q::CheckIndicatorPanel | Q::Pressed | Q::Selected, 0 ); - setPadding( Q::CheckIndicatorPanel | Q::Pressed | Q::Selected, { 6, 6 } ); // indicator "strut size" + setPadding( Q::CheckIndicatorPanel | Q::Pressed | Q::Selected, { 6_px, 6_px } ); // indicator "strut size" setBoxBorderMetrics( Q::CheckIndicatorPanel | Q::Disabled | Q::Selected, 0 ); - setPadding( Q::CheckIndicatorPanel | Q::Disabled | Q::Selected, { 6, 6 } ); // indicator "strut size" + setPadding( Q::CheckIndicatorPanel | Q::Disabled | Q::Selected, { 6_px, 6_px } ); // indicator "strut size" - setFontRole( Q::Text, QskFluent2Skin::Body ); + setFontRole( Q::Text, Fluent2::Body ); } void Editor::setupRadioBoxColors( @@ -1189,11 +1224,11 @@ void Editor::setupScrollViewMetrics() for ( auto subControl : { Q::HorizontalScrollBar, Q::VerticalScrollBar } ) { - setMetric( subControl | A::Size, 6 ); + setMetric( subControl | A::Size, 6_px ); // The scrollbar is expanding, when being hovered/pressed - const qreal padding = 4; + const qreal padding = 4_px; if ( subControl == Q::HorizontalScrollBar ) setPadding( Q::HorizontalScrollBar, 0, padding, 0, 0 ); @@ -1217,7 +1252,7 @@ void Editor::setupScrollViewMetrics() setBoxShape( Q::HorizontalScrollHandle, 100, Qt::RelativeSize ); setBoxShape( Q::VerticalScrollHandle, 100, Qt::RelativeSize ); - const auto handleExtent = 40.0; + const auto handleExtent = 40_px; setStrutSize( Q::HorizontalScrollHandle, handleExtent, 0.0 ); setStrutSize( Q::VerticalScrollHandle, 0.0, handleExtent ); } @@ -1270,20 +1305,20 @@ void Editor::setupSegmentedBarMetrics() using Q = QskSegmentedBar; using A = QskAspect; - const QSizeF segmentStrutSize( 120, 32 ); + const QSizeF segmentStrutSize( 120_px, 32_px ); - setBoxBorderMetrics( Q::Panel, 1 ); + setBoxBorderMetrics( Q::Panel, 1_px ); setBoxBorderMetrics( Q::Panel | Q::Selected | Q::Disabled, 0 ); - setSpacing( Q::Panel, 8 ); + setSpacing( Q::Panel, 8_px ); - setStrutSize( Q::Icon, { 12, 12 } ); + setStrutSize( Q::Icon, { 12_px, 12_px } ); - setFontRole( Q::Text, QskFluent2Skin::Body ); + setFontRole( Q::Text, Fluent2::Body ); setStrutSize( Q::Segment | A::Horizontal, segmentStrutSize ); setStrutSize( Q::Segment | A::Vertical, segmentStrutSize.transposed() ); - setBoxShape( Q::Segment, 4 ); - setPadding( Q::Segment, { 8, 0, 8, 0 } ); + setBoxShape( Q::Segment, 4_px ); + setPadding( Q::Segment, { 8_px, 0, 8_px, 0 } ); } void Editor::setupSegmentedBarColors( @@ -1373,7 +1408,7 @@ void Editor::setupSeparatorMetrics() using Q = QskSeparator; using A = QskAspect; - setMetric( Q::Panel | A::Size, 1 ); + setMetric( Q::Panel | A::Size, 1_px ); setBoxShape( Q::Panel, 0 ); setBoxBorderMetrics( Q::Panel, 0 ); } @@ -1392,7 +1427,7 @@ void Editor::setupSliderMetrics() using Q = QskSlider; using A = QskAspect; - const qreal extent = 22; + const qreal extent = 22_px; setMetric( Q::Panel | A::Size, extent ); setBoxShape( Q::Panel, 0 ); setBoxBorderMetrics( Q::Panel, 0 ); @@ -1400,22 +1435,22 @@ void Editor::setupSliderMetrics() setPadding( Q::Panel | A::Horizontal, QskMargins( 0.5 * extent, 0 ) ); setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) ); - setMetric( Q::Groove | A::Size, 4 ); + setMetric( Q::Groove | A::Size, 4_px ); setBoxShape( Q::Groove, 100, Qt::RelativeSize ); - setMetric( Q::Fill | A::Size, 4 ); + setMetric( Q::Fill | A::Size, 4_px ); setBoxShape( Q::Fill, 100, Qt::RelativeSize ); - setStrutSize( Q::Handle, { 22, 22 } ); + setStrutSize( Q::Handle, { 22_px, 22_px } ); setBoxShape( Q::Handle, 100, Qt::RelativeSize ); - setBoxBorderMetrics( Q::Handle, 1 ); + setBoxBorderMetrics( Q::Handle, 1_px ); - setStrutSize( Q::Ripple, { 12, 12 } ); + setStrutSize( Q::Ripple, { 12_px, 12_px } ); setBoxShape( Q::Ripple, 100, Qt::RelativeSize ); - setStrutSize( Q::Ripple | Q::Hovered, { 14, 14 } ); + setStrutSize( Q::Ripple | Q::Hovered, { 14_px, 14_px } ); - setStrutSize( Q::Ripple | Q::Pressed, { 10, 10 } ); + setStrutSize( Q::Ripple | Q::Pressed, { 10_px, 10_px } ); } void Editor::setupSliderColors( @@ -1468,28 +1503,38 @@ void Editor::setupSpinBoxMetrics() { using Q = QskSpinBox; + /* + The F2 NumberBox has 2 modes for the Up/Down panels ( a.k.a Spinner ): + + - compact ( -> QskSpinBox::UpDownControl ) + - inline ( -> QskSpinBox::ButtonsRight ) + + TODO: in compact mode the panels/indicators are blown up, when being hovered + */ setHint( Q::Panel | QskAspect::Style, Q::ButtonsRight ); - setStrutSize( Q::Panel, { -1, 32 } ); - setBoxBorderMetrics( Q::Panel, 1 ); - setBoxShape( Q::Panel, 3 ); - setPadding( Q::Panel, { 11, 0, 11, 0 } ); - setAlignment( Q::Text, Qt::AlignLeft ); - setFontRole( Q::Text, QskFluent2Skin::Body ); + setStrutSize( Q::Panel, { -1, 32_px } ); + setBoxBorderMetrics( Q::Panel, 1_px ); + setBoxShape( Q::Panel, 3_px ); - setPadding( Q::TextPanel, { 11, 5, 0, 0 } ); + setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); + setFontRole( Q::Text, Fluent2::Body ); - setStrutSize( Q::UpPanel, 32, 20 ); - setPadding( Q::UpPanel, { 11, 7, 11, 7 } ); + setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } ); - setStrutSize( Q::DownPanel, 34, 20 ); - setPadding( Q::DownPanel, { 11, 7, 13, 7 } ); + for ( auto panel : { Q::UpPanel, Q::DownPanel } ) + setStrutSize( panel, 32_px, 18_px ); - setSymbol( Q::UpIndicator, symbol( "spin-box-arrow-up" ) ); - setSymbol( Q::DownIndicator, symbol( "spin-box-arrow-down" ) ); + setSymbol( Q::UpIndicator, symbol( "chevron_up" ) ); + setSymbol( Q::DownIndicator, symbol( "chevron_down" ) ); - // Focused (Pressed doesn't exist yet): - setBoxBorderMetrics( Q::Panel | Q::Focused, { 1, 1, 1, 2 } ); + 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 } ); +#endif } void Editor::setupSpinBoxColors( @@ -1552,8 +1597,17 @@ void Editor::setupSpinBoxColors( panelColor = rgbSolid( panelColor, pal.background.solid.base ); setGradient( panel, panelColor ); + setBoxBorderGradient( panel, borderColor1, borderColor2, panelColor ); + if ( state == Q::Focused ) + { + const auto colors = boxBorderColors( panel ); + + setBoxBorderColors( panel | Q::Decreasing, colors ); + setBoxBorderColors( panel | Q::Increasing, colors ); + } + setColor( text, textColor ); setGraphicRole( upIndicator, graphicRole ); @@ -1574,17 +1628,17 @@ void Editor::setupTabButtonMetrics() { using Q = QskTabButton; - setStrutSize( Q::Panel, { -1, 31 } ); - setPadding( Q::Panel, { 7, 0, 7, 0 } ); - setBoxShape( Q::Panel, { 7, 7, 0, 0 } ); + setStrutSize( Q::Panel, { -1, 31_px } ); + setPadding( Q::Panel, { 7_px, 0, 7_px, 0 } ); + setBoxShape( Q::Panel, { 7_px, 7_px, 0, 0 } ); setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); - setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1 } ); - setBoxBorderMetrics( Q::Panel | Q::Checked, { 1, 1, 1, 0 } ); + setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_px } ); + setBoxBorderMetrics( Q::Panel | Q::Checked, { 1_px, 1_px, 1_px, 0 } ); - setFontRole( Q::Text, QskFluent2Skin::Body ); - setFontRole( Q::Text | Q::Checked, QskFluent2Skin::BodyStrong ); + setFontRole( Q::Text, Fluent2::Body ); + setFontRole( Q::Text | Q::Checked, Fluent2::BodyStrong ); } void Editor::setupTabButtonColors( @@ -1654,9 +1708,9 @@ void Editor::setupGraphicLabelMetrics() { using Q = QskGraphicLabel; - setPadding( Q::Panel, 10 ); - setBoxShape( Q::Panel, 3 ); - setBoxBorderMetrics( Q::Panel, 1 ); + setPadding( Q::Panel, 10_px ); + setBoxShape( Q::Panel, 3_px ); + setBoxBorderMetrics( Q::Panel, 1_px ); } void Editor::setupGraphicLabelColors( @@ -1675,11 +1729,11 @@ void Editor::setupTextLabelMetrics() { using Q = QskTextLabel; - setPadding( Q::Panel, 10 ); - setBoxShape( Q::Panel, 3 ); - setBoxBorderMetrics( Q::Panel, 1 ); + setPadding( Q::Panel, 10_px ); + setBoxShape( Q::Panel, 3_px ); + setBoxBorderMetrics( Q::Panel, 1_px ); - setFontRole( Q::Text, QskFluent2Skin::Body ); + setFontRole( Q::Text, Fluent2::Body ); } void Editor::setupTextLabelColors( @@ -1699,17 +1753,17 @@ void Editor::setupTextInputMetrics() { using Q = QskTextInput; - setStrutSize( Q::Panel, { -1, 30 } ); - setPadding( Q::Panel, { 11, 0, 11, 0 } ); + setStrutSize( Q::Panel, { -1, 30_px } ); + setPadding( Q::Panel, { 11_px, 0, 11_px, 0 } ); - setBoxBorderMetrics( Q::Panel, 1 ); + setBoxBorderMetrics( Q::Panel, 1_px ); for( const auto& state : { Q::Focused, Q::Editing } ) - setBoxBorderMetrics( Q::Panel | state, { 1, 1, 1, 2 } ); + setBoxBorderMetrics( Q::Panel | state, { 1_px, 1_px, 1_px, 2_px } ); - setBoxShape( Q::Panel, 3 ); + setBoxShape( Q::Panel, 3_px ); setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); - setFontRole( Q::Text, QskFluent2Skin::Body ); + setFontRole( Q::Text, Fluent2::Body ); } void Editor::setupTextInputColors( @@ -1772,12 +1826,12 @@ void Editor::setupSwitchButtonMetrics() using Q = QskSwitchButton; using A = QskAspect; - const QSizeF strutSize( 38, 18 ); + const QSizeF strutSize( 38_px, 18_px ); setStrutSize( Q::Groove | A::Horizontal, strutSize ); setStrutSize( Q::Groove | A::Vertical, strutSize.transposed() ); setBoxShape( Q::Groove, 100, Qt::RelativeSize ); - setBoxBorderMetrics( Q::Groove, 1 ); + setBoxBorderMetrics( Q::Groove, 1_px ); setBoxBorderMetrics( Q::Groove | Q::Checked, 0 ); setBoxShape( Q::Handle, 100, Qt::RelativeSize ); @@ -1786,15 +1840,15 @@ void Editor::setupSwitchButtonMetrics() { QskStateCombination::CombinationNoState, Q::Disabled } ); setBoxBorderMetrics( Q::Handle, 0 ); - setBoxBorderMetrics( Q::Handle | Q::Checked, 1 ); + setBoxBorderMetrics( Q::Handle | Q::Checked, 1_px ); setBoxBorderMetrics( Q::Handle | Q::Disabled | Q::Checked, 0 ); - setStrutSize( Q::Handle, 12, 12 ); + setStrutSize( Q::Handle, 12_px, 12_px ); - setStrutSize( Q::Handle | Q::Hovered, 14, 14, + setStrutSize( Q::Handle | Q::Hovered, 14_px, 14_px, { QskStateCombination::CombinationNoState, Q::Checked } ); - const QSizeF pressedSize( 17, 14 ); + const QSizeF pressedSize( 17_px, 14_px ); setStrutSize( Q::Handle | Q::Pressed | A::Horizontal, pressedSize, { QskStateCombination::CombinationNoState, Q::Checked } ); @@ -1802,7 +1856,7 @@ void Editor::setupSwitchButtonMetrics() setStrutSize( Q::Handle | Q::Pressed | A::Vertical, pressedSize.transposed(), { QskStateCombination::CombinationNoState, Q::Checked } ); - setStrutSize( Q::Handle | Q::Disabled, 12, 12, + setStrutSize( Q::Handle | Q::Disabled, 12_px, 12_px, { QskStateCombination::CombinationNoState, Q::Checked } ); setAnimation( Q::Handle | A::Metric, 100 ); @@ -1895,18 +1949,17 @@ void Editor::setupSubWindow( const QskFluent2Theme& theme ) const auto& pal = theme.palette; - setPadding( Q::Panel, { 0, 31, 0, 0 } ); - setBoxShape( Q::Panel, 7 ); - setBoxBorderMetrics( Q::Panel, 1 ); + setBoxShape( Q::Panel, 7_px ); + setBoxBorderMetrics( Q::Panel, 1_px ); setBoxBorderColors( Q::Panel, pal.strokeColor.surface.defaultColor ); setGradient( Q::Panel, pal.background.layer.alt ); setShadowMetrics( Q::Panel, theme.shadow.dialog.metrics ); setShadowColor( Q::Panel, theme.shadow.dialog.color ); - setHint( Q::TitleBarPanel | QskAspect::Style, Q::TitleBar | Q::Title ); - setPadding( Q::TitleBarPanel, { 24, 31, 24, 0 } ); + setHint( Q::TitleBarPanel | QskAspect::Style, Q::NoDecoration ); + setPadding( Q::TitleBarPanel, { 24_px, 31_px, 24_px, 0 } ); - setFontRole( Q::TitleBarText, QskFluent2Skin::Subtitle ); + setFontRole( Q::TitleBarText, Fluent2::Subtitle ); setColor( Q::TitleBarText, pal.fillColor.text.primary ); setAlignment( Q::TitleBarText, Qt::AlignLeft ); setTextOptions( Q::TitleBarText, Qt::ElideRight, QskTextOptions::NoWrap ); @@ -1914,13 +1967,22 @@ void Editor::setupSubWindow( const QskFluent2Theme& theme ) setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic ); } +void Editor::setupDialogSubWindow( const QskFluent2Theme& ) +{ + using Q = QskDialogSubWindow; + + setFontRole( Q::DialogTitle, Fluent2::Subtitle ); + setAlignment( Q::DialogTitle, Qt::AlignLeft | Qt::AlignVCenter ); + setTextOptions( Q::DialogTitle, Qt::ElideRight, QskTextOptions::WordWrap ); +} + void Editor::setupVirtualKeyboardMetrics() { using Q = QskVirtualKeyboard; - setMargin( Q::ButtonPanel, 2 ); - setFontRole( Q::ButtonText, QskFluent2Skin::BodyLarge ); - setPadding( Q::Panel, 8 ); + setMargin( Q::ButtonPanel, 2_px ); + setFontRole( Q::ButtonText, Fluent2::BodyStrong ); + setPadding( Q::Panel, 8_px ); } void Editor::setupVirtualKeyboardColors( @@ -1985,13 +2047,13 @@ void QskFluent2Skin::initHints() { colors[0].baseColors = { rgbGray( 32 ), rgbGray( 40 ), rgbGray( 28 ) }; colors[0].accentColors = { 0xff0078d4, 0xff0093f9, 0xff60ccfe, 0xff98ecfe }; - + colors[1].baseColors = { rgbGray( 40 ), rgbGray( 44 ), rgbGray( 28 ) }; colors[1].accentColors = colors[0].accentColors; } setupFonts(); - + Editor editor( &hintTable() ); editor.setupMetrics(); @@ -2006,19 +2068,49 @@ void QskFluent2Skin::initHints() addTheme( QskAspect::Footer, themeHeader ); } +static inline QFont createFont( int size, int lineHeight, QFont::Weight weight ) +{ + Q_UNUSED( lineHeight ); // TODO ... + const int pixelSize = qRound( qskPxToPixels( size ) ); + + QFont font( QStringLiteral( "Segoe UI" ), -1, weight ); + + static bool checkFont = true; + if ( checkFont ) + { + const QFontInfo info( font ); + if ( info.family() != font.family() ) + { + qWarning() << font.family() << + "not found, using" << info.family() << "instead."; + } + checkFont = false; + } + + font.setPixelSize( pixelSize ); + + return font; +} + void QskFluent2Skin::setupFonts() { - static QString fontName( QStringLiteral( "Segoe UI Variable" ) ); - Inherited::setupFonts( fontName ); + // see: https://fluent2.microsoft.design/typography ( Windows ) - setFont( Caption, createFont( fontName, 12, 16, 0.0, QFont::Normal ) ); - setFont( Body, createFont( fontName, 14, 20, 0.0, QFont::Normal ) ); - setFont( BodyStrong, createFont( fontName, 14, 20, 0.0, QFont::DemiBold ) ); - setFont( BodyLarge, createFont( fontName, 18, 24, 0.0, QFont::Medium ) ); - setFont( Subtitle, createFont( fontName, 20, 28, 0.0, QFont::DemiBold ) ); - setFont( Title, createFont( fontName, 28, 36, 0.0, QFont::DemiBold ) ); - setFont( TitleLarge, createFont( fontName, 40, 52, 0.0, QFont::DemiBold ) ); - setFont( Display, createFont( fontName, 68, 92, 0.0, QFont::DemiBold ) ); + setFont( Fluent2::Caption, createFont( 12, 16, QFont::Normal ) ); + + setFont( Fluent2::Body, createFont( 14, 20, QFont::Normal ) ); + setFont( Fluent2::BodyStrong, createFont( 14, 20, QFont::DemiBold ) ); + setFont( Fluent2::BodyStronger, createFont( 18, 24, QFont::Normal ) ); + + setFont( Fluent2::Subtitle, createFont( 20, 28, QFont::DemiBold ) ); + + setFont( Fluent2::Title, createFont( 28, 36, QFont::Normal ) ); + setFont( Fluent2::LargeTitle, createFont( 40, 52, QFont::DemiBold ) ); + + setFont( Fluent2::Display, createFont( 68, 92, QFont::DemiBold ) ); + + // to have something for the unused roles + QskSkin::completeFontTable(); } void QskFluent2Skin::setGraphicColor( GraphicRole role, QRgb rgb ) diff --git a/designsystems/fluent2/QskFluent2Skin.h b/designsystems/fluent2/QskFluent2Skin.h index 239a73a8..5a72c481 100644 --- a/designsystems/fluent2/QskFluent2Skin.h +++ b/designsystems/fluent2/QskFluent2Skin.h @@ -31,18 +31,6 @@ class QSK_FLUENT2_EXPORT QskFluent2Skin : public QskSkin GraphicRoleFillColorTextSecondary, }; - enum FontRole - { - Caption = TinyFont, - Body = DefaultFont, - BodyStrong = SmallFont, - BodyLarge = MediumFont, - Subtitle = LargeFont, - Title = HugeFont, - TitleLarge, - Display, - }; - static constexpr QskAspect::Variation Standard = QskAspect::NoVariation; static constexpr QskAspect::Variation Accent = QskAspect::Large; diff --git a/designsystems/fluent2/icons.qrc b/designsystems/fluent2/icons.qrc deleted file mode 100644 index 4e322fd8..00000000 --- a/designsystems/fluent2/icons.qrc +++ /dev/null @@ -1,10 +0,0 @@ - - - icons/qvg/checkmark.qvg - icons/qvg/combo-box-arrow-closed.qvg - icons/qvg/combo-box-arrow-open.qvg - icons/qvg/segmented-button-check.qvg - icons/qvg/spin-box-arrow-down.qvg - icons/qvg/spin-box-arrow-up.qvg - - diff --git a/designsystems/fluent2/icons/README b/designsystems/fluent2/icons/README new file mode 100644 index 00000000..a45cea97 --- /dev/null +++ b/designsystems/fluent2/icons/README @@ -0,0 +1,10 @@ +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 index 70b15e35..f87de9aa 100644 --- a/designsystems/fluent2/icons/checkmark.svg +++ b/designsystems/fluent2/icons/checkmark.svg @@ -1,4 +1,3 @@ - - + + - diff --git a/designsystems/fluent2/icons/chevron_down.svg b/designsystems/fluent2/icons/chevron_down.svg new file mode 100644 index 00000000..8ac9ed11 --- /dev/null +++ b/designsystems/fluent2/icons/chevron_down.svg @@ -0,0 +1,3 @@ + + + diff --git a/designsystems/fluent2/icons/chevron_up.svg b/designsystems/fluent2/icons/chevron_up.svg new file mode 100644 index 00000000..28426e1d --- /dev/null +++ b/designsystems/fluent2/icons/chevron_up.svg @@ -0,0 +1,3 @@ + + + diff --git a/designsystems/fluent2/icons/combo-box-arrow-closed.svg b/designsystems/fluent2/icons/combo-box-arrow-closed.svg deleted file mode 100644 index c288b426..00000000 --- a/designsystems/fluent2/icons/combo-box-arrow-closed.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/fluent2/icons/combo-box-arrow-open.svg b/designsystems/fluent2/icons/combo-box-arrow-open.svg deleted file mode 100644 index 4138c2cd..00000000 --- a/designsystems/fluent2/icons/combo-box-arrow-open.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/fluent2/icons/icon2qvg.sh b/designsystems/fluent2/icons/icon2qvg.sh new file mode 100755 index 00000000..c562ca17 --- /dev/null +++ b/designsystems/fluent2/icons/icon2qvg.sh @@ -0,0 +1,15 @@ +#! /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 dc894a09..40ab0c75 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 new file mode 100644 index 00000000..a8d90d49 Binary files /dev/null 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 new file mode 100644 index 00000000..11f3abc4 Binary files /dev/null and b/designsystems/fluent2/icons/qvg/chevron_up.qvg differ diff --git a/designsystems/fluent2/icons/qvg/combo-box-arrow-closed.qvg b/designsystems/fluent2/icons/qvg/combo-box-arrow-closed.qvg deleted file mode 100644 index 2585b892..00000000 Binary files a/designsystems/fluent2/icons/qvg/combo-box-arrow-closed.qvg and /dev/null differ diff --git a/designsystems/fluent2/icons/qvg/combo-box-arrow-open.qvg b/designsystems/fluent2/icons/qvg/combo-box-arrow-open.qvg deleted file mode 100644 index 6ddcf6c0..00000000 Binary files a/designsystems/fluent2/icons/qvg/combo-box-arrow-open.qvg and /dev/null differ diff --git a/designsystems/fluent2/icons/qvg/segmented-button-check.qvg b/designsystems/fluent2/icons/qvg/segmented-button-check.qvg deleted file mode 100644 index fa0e1ce2..00000000 Binary files a/designsystems/fluent2/icons/qvg/segmented-button-check.qvg and /dev/null differ diff --git a/designsystems/fluent2/icons/qvg/spin-box-arrow-down.qvg b/designsystems/fluent2/icons/qvg/spin-box-arrow-down.qvg deleted file mode 100644 index 2825e1fb..00000000 Binary files a/designsystems/fluent2/icons/qvg/spin-box-arrow-down.qvg and /dev/null differ diff --git a/designsystems/fluent2/icons/qvg/spin-box-arrow-up.qvg b/designsystems/fluent2/icons/qvg/spin-box-arrow-up.qvg deleted file mode 100644 index 3ae75f32..00000000 Binary files a/designsystems/fluent2/icons/qvg/spin-box-arrow-up.qvg and /dev/null differ diff --git a/designsystems/fluent2/icons/segmented-button-check.svg b/designsystems/fluent2/icons/segmented-button-check.svg deleted file mode 100644 index b0b66ac9..00000000 --- a/designsystems/fluent2/icons/segmented-button-check.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/fluent2/icons/spin-box-arrow-down.svg b/designsystems/fluent2/icons/spin-box-arrow-down.svg deleted file mode 100644 index a3ffcd48..00000000 --- a/designsystems/fluent2/icons/spin-box-arrow-down.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/fluent2/icons/spin-box-arrow-up.svg b/designsystems/fluent2/icons/spin-box-arrow-up.svg deleted file mode 100644 index 34301711..00000000 --- a/designsystems/fluent2/icons/spin-box-arrow-up.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/designsystems/fusion/QskFusionSkin.cpp b/designsystems/fusion/QskFusionSkin.cpp index c26e4215..d637fdfd 100644 --- a/designsystems/fusion/QskFusionSkin.cpp +++ b/designsystems/fusion/QskFusionSkin.cpp @@ -13,8 +13,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -110,6 +112,7 @@ namespace Q_INVOKABLE void setupCheckBox(); Q_INVOKABLE void setupComboBox(); Q_INVOKABLE void setupDialogButtonBox(); + Q_INVOKABLE void setupDialogSubWindow(); Q_INVOKABLE void setupDrawer(); Q_INVOKABLE void setupFocusIndicator(); Q_INVOKABLE void setupGraphicLabel(); @@ -142,13 +145,21 @@ namespace if ( strcmp( name, "checkMark" ) == 0 ) { + graphic.setViewBox( QRectF( 0.0, 0.0, 14.0, 14.0 ) ); + QPainterPath path; - path.moveTo( 0.11, 0.47 ); - path.lineTo( 0.5, 1.0); - path.lineTo( 1.0, 0.0 ); + path.moveTo( 3.5, 7.0 ); + path.lineTo( 6.5, 14.0 ); + path.lineTo( 11.0, 1.0 ); + + QPen pen( Qt::black, 2.8 ); + pen.setCapStyle( Qt::FlatCap ); + pen.setJoinStyle( Qt::BevelJoin ); QPainter painter( &graphic ); - painter.setPen( QPen( Qt::black, 0.25 ) ); + painter.setRenderHint( QPainter::Antialiasing ); + + painter.setPen( pen ); painter.drawPath( path ); } @@ -206,19 +217,12 @@ void Editor::setupCheckBox() } } - setGraphicRole( Q::Indicator, QskFusionSkin::GraphicNormal ); + setGraphicRole( Q::Indicator, QskFusionSkin::GraphicIndicator ); setGraphicRole( Q::Indicator | Q::Disabled, QskFusionSkin::GraphicDisabled ); setGraphicRole( Q::Indicator | Q::Error, QskFusionSkin::GraphicError ); -#if 0 - // aligning/scaling of the symbols needs to be fixed in the skinlet TODO .. - - setPadding( Q::Box, 4_dp ); + setPadding( Q::Box, 3_dp ); const auto checkMark = symbol( "checkMark" ); -#else - setPadding( Q::Box, 2_dp ); - const auto checkMark = QskStandardSymbol::graphic( QskStandardSymbol::CheckMark ); -#endif for ( auto state : { QskAspect::NoState, Q::Disabled } ) { @@ -274,6 +278,7 @@ void Editor::setupComboBox() setGraphicRole( Q::Icon | Q::Disabled, QskFusionSkin::GraphicDisabled ); setStrutSize( Q::StatusIndicator, 10_dp, 10_dp ); + setGraphicRole( Q::StatusIndicator, QskFusionSkin::GraphicIndicator ); setGraphicRole( Q::StatusIndicator | Q::Disabled, QskFusionSkin::GraphicDisabled ); setAlignment( Q::StatusIndicator, Qt::AlignRight | Qt::AlignVCenter ); @@ -363,7 +368,6 @@ void Editor::setupTextLabel() { using Q = QskTextLabel; - setAlignment( Q::Text, Qt::AlignCenter ); setColor( Q::Text, m_pal.color( QPalette::Active, QPalette::Text ) ); setColor( Q::Text | Q::Disabled, m_pal.color( QPalette::Disabled, QPalette::Text ) ); @@ -680,7 +684,7 @@ void Editor::setupPageIndicator() void Editor::setupPushButton() { /* - QC2/Fusion offers flat/highlighted. We could the emphasis variations to + QC2/Fusion offers flat/highlighted. We could the emphasis variations to have the same: TODO ... */ using Q = QskPushButton; @@ -735,6 +739,17 @@ void Editor::setupDialogButtonBox() setGradient( Q::Panel | Q::Disabled, m_pal.disabled( P::Base ) ); } +void Editor::setupDialogSubWindow() +{ + using Q = QskDialogSubWindow; + +#if 1 + setFontRole( Q::DialogTitle, QskFontRole::Headline ); +#endif + setAlignment( Q::DialogTitle, Qt::AlignLeft | Qt::AlignVCenter ); + setTextOptions( Q::DialogTitle, Qt::ElideRight, QskTextOptions::WordWrap ); +} + void Editor::setupDrawer() { using Q = QskDrawer; @@ -846,8 +861,6 @@ void Editor::setupSpinBox() setPadding( Q::TextPanel, 5_dp ); setBoxShape( Q::TextPanel, 2, 0, 2, 0 ); - - setColor( Q::Text, m_pal.active( P::Text ) ); setGradient( Q::TextPanel | Q::Disabled, m_pal.disabled( P::Base ) ); setBoxBorderMetrics( Q::TextPanel, 1_dp ); @@ -859,6 +872,9 @@ void Editor::setupSpinBox() Combination( { Q::Increasing, Q::Decreasing, Q::Hovered } ) ); #endif + setColor( Q::Text, m_pal.active( P::Text ) ); + setAlignment( Q::Text, Qt::AlignCenter ); + setBoxShape( Q::UpPanel, 0, 2_dp, 0, 0 ); setBoxBorderMetrics( Q::UpPanel, 0_dp, 1_dp, 1_dp, 0_dp ); @@ -1272,7 +1288,7 @@ void Editor::setupSubWindow() // TitleBarPanel - setHint( Q::TitleBarPanel | QskAspect::Style, Q::TitleBar | Q::Title ); + setHint( Q::TitleBarPanel | QskAspect::Style, Q::NoDecoration ); setMargin( Q::TitleBarPanel, -1 ); setGradient( Q::TitleBarPanel, m_pal.active( P::Mid ) ); @@ -1312,7 +1328,10 @@ void QskFusionSkin::initHints() using P = QPalette; - setupFonts( QStringLiteral( "Roboto" ) ); +#if 1 + // we should use QApplication::font somehow: TODO ... + setupFontTable( QStringLiteral( "Roboto" ) ); +#endif const QskFusionPalette palette( colorScheme() ); @@ -1322,6 +1341,13 @@ void QskFusionSkin::initHints() setGraphicColor( GraphicError, palette.error ); setGraphicColor( GraphicHighlighted, palette.active( P::HighlightedText ) ); + { + auto rgb = palette.darker( P::Active, P::Text, 120 ); + rgb = QskRgb::toTransparent( rgb, 180 ); + + setGraphicColor( GraphicIndicator, rgb ); + } + Editor editor( palette, &hintTable() ); editor.setup(); } diff --git a/designsystems/fusion/QskFusionSkin.h b/designsystems/fusion/QskFusionSkin.h index c3feb9e7..3ec1f178 100644 --- a/designsystems/fusion/QskFusionSkin.h +++ b/designsystems/fusion/QskFusionSkin.h @@ -24,7 +24,8 @@ class QSK_FUSION_EXPORT QskFusionSkin : public QskSkin GraphicNormal, GraphicDisabled, GraphicHighlighted, - GraphicError + GraphicError, + GraphicIndicator }; protected: diff --git a/designsystems/material3/CMakeLists.txt b/designsystems/material3/CMakeLists.txt index cce742e0..051e8ed9 100644 --- a/designsystems/material3/CMakeLists.txt +++ b/designsystems/material3/CMakeLists.txt @@ -7,7 +7,7 @@ set(SOURCES QskMaterial3Global.h QskMaterial3Skin.h QskMaterial3Skin.cpp QskMaterial3SkinFactory.h QskMaterial3SkinFactory.cpp ) -qt_add_resources(SOURCES icons.qrc) +qt_add_resources(SOURCES QskMaterial3Icons.qrc) qsk_add_plugin(material3skin skins QskMaterial3SkinFactory ${SOURCES}) set_target_properties(material3skin PROPERTIES DEFINE_SYMBOL QSK_MATERIAL3_MAKEDLL ) diff --git a/designsystems/material3/QskMaterial3Icons.qrc b/designsystems/material3/QskMaterial3Icons.qrc new file mode 100644 index 00000000..4e55e3be --- /dev/null +++ b/designsystems/material3/QskMaterial3Icons.qrc @@ -0,0 +1,19 @@ + + + + + + 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/add.qvg + icons/qvg/arrow_drop_down.qvg + icons/qvg/arrow_drop_up.qvg + icons/qvg/check.qvg + icons/qvg/remove.qvg + + diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 2f3ac9e0..b4664475 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -3,6 +3,11 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ +/* + Definitions ( where possible ) taken from + https://www.figma.com/file/O4H724CKmUVPocw6JoSUrd/Material-3-Design-Kit-(Community) + */ + #include "QskMaterial3Skin.h" #include @@ -12,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -50,15 +56,50 @@ #include #include #include +#include #include #include -#include -#include +#include +#include + +static void qskMaterial3InitResources() +{ + Q_INIT_RESOURCE( QskMaterial3Icons ); +} + +Q_CONSTRUCTOR_FUNCTION( qskMaterial3InitResources ) static const int qskDuration = 150; +namespace +{ + using F = QskFontRole; + + // M3 font roles: https://m3.material.io/styles/typography/type-scale-tokens + + constexpr F LabelSmall = { F::Caption, F::Low }; + constexpr F LabelMedium = { F::Caption, F::Normal }; + constexpr F LabelLarge = { F::Caption, F::High }; + + constexpr F BodySmall = { F::Body, F::Low }; + constexpr F BodyMedium = { F::Body, F::Normal }; + constexpr F BodyLarge = { F::Body, F::High }; + + constexpr F TitleSmall = { F::Title, F::Low }; + constexpr F TitleMedium = { F::Title, F::Normal }; + constexpr F TitleLarge = { F::Title, F::High }; + + constexpr F HeadlineSmall = { F::Headline, F::Low }; + constexpr F HeadlineMedium = { F::Headline, F::Normal }; + constexpr F HeadlineLarge = { F::Headline, F::High }; + + constexpr F DisplaySmall = { F::Display, F::Low }; + constexpr F DisplayMedium = { F::Display, F::Normal }; + constexpr F DisplayLarge = { F::Display, F::High }; +} + namespace { Q_DECL_UNUSED inline double operator ""_dp( long double value ) @@ -71,6 +112,15 @@ namespace return qskDpToPixels( value ); } + class Combination : public QskStateCombination + { + public: + constexpr Combination( const QskAspect::States states ) + : QskStateCombination( CombinationNoState, states ) + { + } + }; + class Editor : private QskSkinHintTableEditor { Q_GADGET @@ -93,6 +143,7 @@ namespace Q_INVOKABLE void setupCheckBox(); Q_INVOKABLE void setupComboBox(); Q_INVOKABLE void setupDialogButtonBox(); + Q_INVOKABLE void setupDialogSubWindow(); Q_INVOKABLE void setupDrawer(); Q_INVOKABLE void setupFocusIndicator(); Q_INVOKABLE void setupInputPanel(); @@ -129,20 +180,6 @@ namespace const QskMaterial3Theme& m_pal; }; - QFont createFont( const QString& name, qreal lineHeight, - qreal size, qreal tracking, QFont::Weight weight ) - { - QFont font( name, qRound( size ) ); - font.setPixelSize( qRound( lineHeight ) ); - - if( !qskFuzzyCompare( tracking, 0.0 ) ) - font.setLetterSpacing( QFont::AbsoluteSpacing, tracking ); - - font.setWeight( weight ); - - return font; - } - inline QRgb flattenedColor( QRgb foregroundColor, QRgb backgroundColor, qreal ratio ) { @@ -268,7 +305,7 @@ void Editor::setupComboBox() setGraphicRole( Q::Icon, QskMaterial3Skin::GraphicRoleOnSurface ); setColor( Q::Text, m_pal.onSurface ); - setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); + setFontRole( Q::Text, BodyMedium ); setStrutSize( Q::StatusIndicator, 12_dp, 12_dp ); setGraphicRole( Q::StatusIndicator, QskMaterial3Skin::GraphicRoleOnSurface ); @@ -351,7 +388,7 @@ void Editor::setupMenu() setGraphicRole( Q::Icon, QskMaterial3Skin::GraphicRoleOnSurface ); setColor( Q::Text, m_pal.onSurface ); - setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); + setFontRole( Q::Text, BodyMedium ); setAnimation( Q::Cursor | A::Position | A::Metric, 75, QEasingCurve::OutCubic ); @@ -362,9 +399,7 @@ void Editor::setupTextLabel() { using Q = QskTextLabel; - setAlignment( Q::Text, Qt::AlignCenter ); setColor( Q::Text, m_pal.onSurface ); - setPadding( Q::Panel, 5_dp ); } @@ -392,7 +427,7 @@ void Editor::setupTextInput() // ### Also add a pressed state setColor( Q::Text, m_pal.onSurface ); - setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); + setFontRole( Q::Text, BodyMedium ); setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); @@ -553,7 +588,7 @@ void Editor::setupSegmentedBar() { // Text - setFontRole( Q::Text, QskMaterial3Skin::M3LabelLarge ); + setFontRole( Q::Text, LabelLarge ); setTextOptions( Q::Text, Qt::ElideMiddle, QskTextOptions::NoWrap ); setColor( Q::Text, m_pal.onSurface ); @@ -566,7 +601,7 @@ void Editor::setupSegmentedBar() { // Icon - setSymbol( Q::Icon, symbol( "segmented-button-check" ) ); + setSymbol( Q::Icon, symbol( "check" ) ); setStrutSize( Q::Icon, 18_dp, 18_dp ); setGraphicRole( Q::Icon, QskMaterial3Skin::GraphicRoleOnSurface ); @@ -630,7 +665,7 @@ void Editor::setupPushButton() setPadding( Q::Icon, { 0, 0, 8_dp, 0 } ); setGraphicRole( Q::Icon, QskMaterial3Skin::GraphicRoleOnPrimary ); - setFontRole( Q::Text, QskMaterial3Skin::M3LabelLarge ); + setFontRole( Q::Text, LabelLarge ); setPadding( Q::Text, 0 ); setBoxShape( Q::Splash, 40_dp ); @@ -779,6 +814,17 @@ void Editor::setupDialogButtonBox() setBoxBorderMetrics( Q::Panel, 0 ); } +void Editor::setupDialogSubWindow() +{ + using Q = QskDialogSubWindow; + +#if 1 + setFontRole( Q::DialogTitle, BodyLarge ); +#endif + setAlignment( Q::DialogTitle, Qt::AlignLeft | Qt::AlignVCenter ); + setTextOptions( Q::DialogTitle, Qt::ElideRight, QskTextOptions::WordWrap ); +} + void Editor::setupDrawer() { using Q = QskDrawer; @@ -851,13 +897,21 @@ void Editor::setupSpinBox() using Q = QskSpinBox; setHint( Q::Panel | QskAspect::Style, Q::ButtonsLeftAndRight ); + + setBoxShape( Q::Panel, 4_dp ); + setBoxBorderMetrics( Q::Panel, 1_dp ); + + setBoxBorderColors( Q::Panel, m_pal.outline ); + setBoxBorderColors( Q::Panel | Q::Focused, m_pal.primary, + Combination( { Q::Increasing, Q::Decreasing } ) ); + + setPadding( Q::Panel, 4_dp ); setSpacing( Q::Panel, 4_dp ); setStrutSize( Q::TextPanel, 80_dp, 40_dp ); setStrutSize( Q::UpPanel, 40_dp,40_dp ); setStrutSize( Q::DownPanel, 40_dp, 40_dp ); - setAlignment( Q::Panel, Qt::AlignHCenter ); setAlignment( Q::Text, Qt::AlignCenter ); for( const auto subControl : { Q::DownPanel, Q::UpPanel, Q::TextPanel } ) @@ -868,31 +922,31 @@ void Editor::setupSpinBox() for( const auto subControl : { Q::DownPanel, Q::UpPanel } ) { - setGradient( subControl, m_pal.primary ); - setGradient( subControl | Q::Disabled, m_pal.onSurface12 ); + setGradient( subControl | Q::Hovered, m_pal.primary8 ); setPadding( subControl, 10 ); } { - const auto focusColor = flattenedColor( m_pal.onPrimary, m_pal.primary, 0.12 ); - - setGradient( Q::DownPanel | Q::Decreasing, focusColor ); - setGradient( Q::UpPanel | Q::Increasing, focusColor ); + setGradient( Q::DownPanel | Q::Decreasing, m_pal.primary12 ); + setGradient( Q::UpPanel | Q::Increasing, m_pal.primary12 ); } - setSymbol( Q::UpIndicator, symbol( "combo-box-arrow-open" ) ); - setSymbol( Q::DownIndicator, symbol( "combo-box-arrow-closed" ) ); + setSymbol( Q::UpIndicator, symbol( "add" ) ); + setSymbol( Q::DownIndicator, symbol( "remove" ) ); for( const auto subControl : { Q::DownIndicator, Q::UpIndicator } ) { setAlignment( subControl, Qt::AlignCenter ); +#if 0 setGraphicRole( subControl, QskMaterial3Skin::GraphicRoleOnPrimary ); setGraphicRole( subControl | Q::Disabled, QskMaterial3Skin::GraphicRoleOnSurface38 ); +#endif } setColor( Q::Text, m_pal.onBackground ); setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); +#if 0 setPadding( Q::TextPanel, 5_dp ); setBoxShape( Q::TextPanel, 4_dp, 4_dp, 0, 0 ); setBoxBorderMetrics( Q::TextPanel, 0, 0, 0, 1_dp ); @@ -909,6 +963,7 @@ void Editor::setupSpinBox() setColor( Q::TextPanel | Q::Disabled, m_pal.onSurface38 ); setBoxBorderColors( Q::TextPanel | Q::Disabled, m_pal.onSurface38 ); +#endif } void Editor::setupSwitchButton() @@ -1024,7 +1079,7 @@ void Editor::setupTabButton() setAnimation( Q::Panel | A::Color, qskDuration ); - setFontRole( Q::Text, QskMaterial3Skin::M3LabelLarge ); + setFontRole( Q::Text, LabelLarge ); setAlignment( Q::Text, Qt::AlignCenter ); } @@ -1095,7 +1150,7 @@ void Editor::setupVirtualKeyboard() setAnimation( Q::ButtonPanel | A::Metric, qskDuration ); setColor( Q::ButtonText, m_pal.onBackground ); - setFontRole( Q::ButtonText, QskMaterial3Skin::M3HeadlineSmall ); + setFontRole( Q::ButtonText, HeadlineSmall ); // panel setGradient( Q::Panel, m_pal.background ); @@ -1191,7 +1246,7 @@ void Editor::setupListView() } } - setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); + setFontRole( Q::Text, BodyMedium ); setColor( Q::Text, m_pal.onSurface ); setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); @@ -1220,13 +1275,12 @@ void Editor::setupSubWindow() // TitleBarPanel setBoxShape( Q::TitleBarPanel, { 28_dp, 28_dp, 0, 0 } ); setPadding( Q::TitleBarPanel, { 24_dp, 24_dp, 24_dp, 16_dp } ); - setHint( Q::TitleBarPanel | QskAspect::Style, - Q::TitleBar | Q::Title | Q::Symbol ); + setHint( Q::TitleBarPanel | QskAspect::Style, Q::NoDecoration ); setGradient( Q::TitleBarPanel, m_pal.secondaryContainer ); // TitleBarText - setFontRole( Q::TitleBarText, QskMaterial3Skin::M3HeadlineSmall ); + setFontRole( Q::TitleBarText, HeadlineSmall ); setColor( Q::TitleBarText, m_pal.onSurface ); setAlignment( Q::TitleBarText, Qt::AlignCenter ); @@ -1402,17 +1456,63 @@ QskMaterial3Skin::~QskMaterial3Skin() { } +static inline QFont createFont( int size, int lineHeight, + qreal spacing, QFont::Weight weight ) +{ + Q_UNUSED( lineHeight ); + + const int pixelSize = qRound( qskDpToPixels( size ) ); + + QFont font( QStringLiteral( "Roboto" ), -1, weight ); + + static bool checkFont = true; + if ( checkFont ) + { + const QFontInfo info( font ); + if ( info.family() != font.family() ) + { + qWarning() << font.family() << + "not found, using" << info.family() << "instead."; + } + checkFont = false; + } + + font.setPixelSize( pixelSize ); + + if ( spacing > 0.0 ) + font.setLetterSpacing( QFont::AbsoluteSpacing, spacing ); + + return font; +} + void QskMaterial3Skin::setupFonts() { - Inherited::setupFonts( QStringLiteral( "Roboto" ) ); + setFont( LabelSmall, createFont( 11, 16, 0.5, QFont::Medium ) ); + setFont( LabelMedium, createFont( 12, 16, 0.5, QFont::Medium ) ); + setFont( LabelLarge, createFont( 14, 20, 0.1, QFont::Medium ) ); - setFont( M3BodyMedium, - createFont( QStringLiteral( "Roboto Regular"), 20_dp, 14_dp, 0.25, QFont::Normal ) ); - setFont( M3BodyLarge, - createFont( QStringLiteral( "Roboto Medium" ), 24_dp, 16_dp, 0.5, QFont::Normal ) ); - setFont( M3HeadlineSmall, - createFont( QStringLiteral( "Roboto Regular" ), 32_dp, 28_dp, 0.0, QFont::Normal ) ); - setFont( M3LabelLarge, createFont( "Roboto Medium", 20_dp, 14_dp, 0.1, QFont::Medium ) ); + setFont( BodySmall, createFont( 12, 16, 0.4, QFont::Normal ) ); + setFont( BodyMedium, createFont( 14, 20, 0.25, QFont::Normal ) ); + setFont( BodyLarge, createFont( 16, 24, 0.5, QFont::Normal ) ); + + setFont( TitleSmall, createFont( 14, 20, 0.1, QFont::Medium ) ); + setFont( TitleMedium, createFont( 16, 24, 0.15, QFont::Medium ) ); + setFont( TitleLarge, createFont( 22, 28, 0.0, QFont::Normal ) ); + + setFont( HeadlineSmall, createFont( 24, 32, 0.0, QFont::Normal ) ); + setFont( HeadlineMedium, createFont( 28, 36, 0.0, QFont::Medium ) ); + setFont( HeadlineLarge, createFont( 32, 40, 0.0, QFont::Medium ) ); + + setFont( DisplaySmall, createFont( 36, 44, 0.0, QFont::Normal ) ); + setFont( DisplayMedium, createFont( 45, 52, 0.0, QFont::Normal ) ); + setFont( DisplayLarge, createFont( 57, 64, 0.0, QFont::Normal ) ); + + // to have something for the unused roles + + setFont( { QskFontRole::Subtitle, QskFontRole::Normal }, + createFont( 16, 24, 0.0, QFont::Normal ) ); + + QskSkin::completeFontTable(); } void QskMaterial3Skin::setGraphicColor( GraphicRole role, QRgb rgb ) diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index 95a89310..b790addb 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -103,9 +103,6 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin using Inherited = QskSkin; public: - QskMaterial3Skin( QObject* parent = nullptr ); - ~QskMaterial3Skin() override; - enum GraphicRole { GraphicRoleOnError, @@ -118,13 +115,8 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin GraphicRoleSurface, }; - enum FontRole - { - M3BodyMedium = DefaultFont, - M3BodyLarge = LargeFont, - M3HeadlineSmall = SmallFont, - M3LabelLarge = HugeFont, - }; + QskMaterial3Skin( QObject* parent = nullptr ); + ~QskMaterial3Skin() override; static constexpr QskAspect::Variation Filled = QskAspect::NoVariation; static constexpr QskAspect::Variation Tonal = QskAspect::Huge; diff --git a/designsystems/material3/icons.qrc b/designsystems/material3/icons.qrc deleted file mode 100644 index ab149d96..00000000 --- a/designsystems/material3/icons.qrc +++ /dev/null @@ -1,8 +0,0 @@ - - - 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 - - diff --git a/designsystems/material3/icons/LICENSE b/designsystems/material3/icons/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/designsystems/material3/icons/LICENSE @@ -0,0 +1,202 @@ + + 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/add.svg b/designsystems/material3/icons/add.svg new file mode 100644 index 00000000..b0570646 --- /dev/null +++ b/designsystems/material3/icons/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/designsystems/material3/icons/arrow_drop_down.svg b/designsystems/material3/icons/arrow_drop_down.svg new file mode 100644 index 00000000..37d90c90 --- /dev/null +++ b/designsystems/material3/icons/arrow_drop_down.svg @@ -0,0 +1 @@ + diff --git a/designsystems/material3/icons/arrow_drop_up.svg b/designsystems/material3/icons/arrow_drop_up.svg new file mode 100644 index 00000000..24f0831a --- /dev/null +++ b/designsystems/material3/icons/arrow_drop_up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/designsystems/material3/icons/check.svg b/designsystems/material3/icons/check.svg new file mode 100644 index 00000000..3be223be --- /dev/null +++ b/designsystems/material3/icons/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/designsystems/material3/icons/icon2qvg.sh b/designsystems/material3/icons/icon2qvg.sh new file mode 100755 index 00000000..c562ca17 --- /dev/null +++ b/designsystems/material3/icons/icon2qvg.sh @@ -0,0 +1,15 @@ +#! /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 new file mode 100644 index 00000000..de7e62c6 Binary files /dev/null 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 new file mode 100644 index 00000000..496378d6 Binary files /dev/null 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 new file mode 100644 index 00000000..09b1976e Binary files /dev/null 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 new file mode 100644 index 00000000..8fb73515 Binary files /dev/null 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 index ea738b86..4b92ce5b 100644 Binary files a/designsystems/material3/icons/qvg/check_small.qvg and b/designsystems/material3/icons/qvg/check_small.qvg differ diff --git a/designsystems/material3/icons/qvg/combo-box-arrow-closed.qvg b/designsystems/material3/icons/qvg/combo-box-arrow-closed.qvg index 2585b892..83f33a45 100644 Binary files a/designsystems/material3/icons/qvg/combo-box-arrow-closed.qvg and b/designsystems/material3/icons/qvg/combo-box-arrow-closed.qvg differ diff --git a/designsystems/material3/icons/qvg/combo-box-arrow-open.qvg b/designsystems/material3/icons/qvg/combo-box-arrow-open.qvg index 6ddcf6c0..1e757789 100644 Binary files a/designsystems/material3/icons/qvg/combo-box-arrow-open.qvg and b/designsystems/material3/icons/qvg/combo-box-arrow-open.qvg differ diff --git a/designsystems/material3/icons/qvg/remove.qvg b/designsystems/material3/icons/qvg/remove.qvg new file mode 100644 index 00000000..6598d5b0 Binary files /dev/null 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 index fa0e1ce2..1b0148e3 100644 Binary files a/designsystems/material3/icons/qvg/segmented-button-check.qvg and b/designsystems/material3/icons/qvg/segmented-button-check.qvg differ diff --git a/designsystems/material3/icons/remove.svg b/designsystems/material3/icons/remove.svg new file mode 100644 index 00000000..9fa856a5 --- /dev/null +++ b/designsystems/material3/icons/remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/doc/classes/QskControl.dox b/doc/classes/QskControl.dox index f8a39834..4bd545b0 100644 --- a/doc/classes/QskControl.dox +++ b/doc/classes/QskControl.dox @@ -8,7 +8,7 @@ what is needed to support a layout system, that is on par with what is known from Qt/Widgets. - \todo Explain: QskQuickItem::geometry(), QskQuickItem::rect(), + \todo Explain: QskItem::geometry(), QskItem::rect(), QQuickItem::boundingRect(), layoutRect(), contentsRect(), subControlRect(), focusIndicatorRect(), QQuickItem::clipRect(), QQuickItem::contains() @@ -22,7 +22,7 @@ A state bit that is set, when QQuickItem::isEnabled() == false. \extends QskAspect::State - \sa QskQuickItem::setDisabled() + \sa QskItem::setDisabled() \saqt QQuickItem::enabled \var QskControl::Hovered @@ -71,7 +71,7 @@ \accessors autoLayoutChildren(), setAutoLayoutChildren() \sa layoutRect(), layoutHints(), layoutAlignmentHint(), - QskQuickItem::transparentForPositioners + QskItem::transparentForPositioners \saqt QQuickItem::updatePolish() \note Auto layouting is similar to what happens when putting several items @@ -274,7 +274,7 @@ \aspect QskControl::Background | QskAspect::Color \sa setBackground() - \sa QskQuickItem::DebugForceBackground + \sa QskItem::DebugForceBackground */ /*! @@ -554,7 +554,7 @@ A control has an impact on the layout calutaions, when: - isTransparentForPositioner() is false - - QskQuickItem::isVisibleToParent() is true, or RetainSizeWhenHidden is set + - QskItem::isVisibleToParent() is true, or RetainSizeWhenHidden is set \sa isVisibleToParent(), RetainSizeWhenHidden */ diff --git a/doc/classes/QskQuickItem.dox b/doc/classes/QskItem.dox similarity index 81% rename from doc/classes/QskQuickItem.dox rename to doc/classes/QskItem.dox index a7fcbf82..488e071c 100644 --- a/doc/classes/QskQuickItem.dox +++ b/doc/classes/QskItem.dox @@ -1,25 +1,25 @@ /*! - \class QskQuickItem QskQuickItem.h + \class QskItem QskItem.h \ingroup Framework - QskQuickItem completes the C++ API of QQuickItem and re-establishs basic + QskItem completes the C++ API of QQuickItem and re-establishs basic concepts like events. It also offers better control over the operations happening in the update cycle. */ /*! - \enum QskQuickItem::UpdateFlag + \enum QskItem::UpdateFlag Qt/Quick classes have a tendency to update items too early and too often. To avoid processing of unwanted operations - QskQuickItem implements a couple of modifications, that + QskItem implements a couple of modifications, that can be en/disabled individually. \sa updateFlags(), resetUpdateFlags() testUpdateFlag(), setUpdateFlag(), resetUpdateFlag() - \var QskQuickItem::UpdateFlag QskQuickItem::DeferredUpdate + \var QskItem::UpdateFlag QskItem::DeferredUpdate Creating of scene graph nodes is blocked when being invisible. @@ -37,7 +37,7 @@ such as viewport frustrum culling (i.e. hiding items outside of the window geometry). - \var QskQuickItem::UpdateFlag QskQuickItem::DeferredPolish + \var QskItem::UpdateFlag QskItem::DeferredPolish Polishing an item is blocked when being invisible. F.e for all items being derived from QskControl the layout calculations @@ -45,7 +45,7 @@ \saqt QQuickItem::updatePolish(), QQuickItem::polish() - \var QskQuickItem::UpdateFlag QskQuickItem::DeferredLayout + \var QskItem::UpdateFlag QskItem::DeferredLayout Recalculations of the implicitSize are blocked until being explicitly requested. @@ -54,15 +54,15 @@ without recalculating the actual size hints ( f.e the implicitSize ). When having layout code that relies on binding the implicit width/height - the QskQuickItem::DeferredLayout flag needs to be disabled. + the QskItem::DeferredLayout flag needs to be disabled. \note All layout classes offered by the Qt/Quick modules of the Qt Company ( f.e anchors ) do require immediate updates of the implicit size. - \sa QskQuickItem::resetImplicitSize() + \sa QskItem::resetImplicitSize() \saqt QQuickItem::implicitSize() - \var QskQuickItem::UpdateFlag QskQuickItem::CleanupOnVisibility + \var QskItem::UpdateFlag QskItem::CleanupOnVisibility Delete scene graph nodes, when the item becomes hidden. Enabling this mode will reduce the memory footprint, but comes at the cost @@ -70,12 +70,12 @@ \saqt QQuickItem::isVisible() - \var QskQuickItem::UpdateFlag QskQuickItem::PreferRasterForTextures + \var QskItem::UpdateFlag QskItem::PreferRasterForTextures When creating textures from QskGraphic, prefer the raster paint engine over the OpenGL paint engine. - \var QskQuickItem::UpdateFlag QskQuickItem::DebugForceBackground + \var QskItem::UpdateFlag QskItem::DebugForceBackground Always fill the background of the item with a random color. @@ -84,7 +84,7 @@ */ /*! - \property QskQuickItem::geometry + \property QskItem::geometry This property holds the geometry of the item relative to its parent item. When changing the geometry, the item receives a QskEvent::GeometryChange event. @@ -93,7 +93,7 @@ */ /*! - \property QskQuickItem::rect + \property QskItem::rect This property returns the internal geometry of the item. It equals QRect(0, 0, width(), height() ). @@ -102,7 +102,7 @@ */ /*! - \var QskQuickItem::transparentForPositioners + \var QskItem::transparentForPositioners When transparentForPositioners is set the item indicates, that it should be excluded from any layout calculations. This flag is actually a concept of QQuickItem, that @@ -112,7 +112,7 @@ */ /*! - \property QskQuickItem::tabFence + \property QskItem::tabFence The tabFence flag can be used to create local tab focus chains. It is usually used in combination with QQuickItem::ItemIsFocusScope. @@ -125,7 +125,7 @@ */ /*! - \property QskQuickItem::visibleToParent + \property QskItem::visibleToParent Flag indicating if an item would become visible if its parentItem() is shown. The implementation relies on the internal explicitVisible flag, that has not @@ -142,7 +142,7 @@ */ /*! - \property QskQuickItem::polishOnResize + \property QskItem::polishOnResize When polishOnResize is set QQuickItem::polish() will be called automatically whenever the size of the item has been changed. This is usually necessary @@ -153,7 +153,7 @@ */ /*! - \property QskQuickItem::initiallyPainted + \property QskItem::initiallyPainted Status flag indicating that there has already been a call of QQuickItem::updatePaintNode() since the item has become visible. @@ -165,7 +165,7 @@ */ /*! - \property QskQuickItem::hasChildItems + \property QskItem::hasChildItems A property indicating if the item has child items. @@ -173,40 +173,40 @@ */ /*! - \property QskQuickItem::updateFlags + \property QskItem::updateFlags - QskQuickItem offers several tweaks to the Qt/Quick update cycle, + QskItem offers several tweaks to the Qt/Quick update cycle, that can be en/disabled individually. The flags are a combination from bits that have been explicitly set by setUpdateFlag() and those being derived from the aapplication wide default settings, that can be set with QskSetup. - \sa QskQuickItem::UpdateFlag, QskQuickItem::updateFlags(), QskSetup::itemUpdateFlags() + \sa QskItem::UpdateFlag, QskItem::updateFlags(), QskSetup::itemUpdateFlags() */ /*! - \fn QskQuickItem::QskQuickItem + \fn QskItem::QskItem Sets the QQuickItem::ItemHasContents flag to true. */ *! - \fn QskQuickItem::~QskQuickItem + \fn QskItem::~QskItem Sets the componentComplete to false, so that its about-to-delete state is known when detaching it from parent/window. */ /*! - \fn QskQuickItem::className + \fn QskItem::className A convenience wrapper for metaObject()->className() \return Class name */ /*! - \fn QskQuickItem::isVisibleTo + \fn QskItem::isVisibleTo The true case occurs if neither the item itself nor any parent up to but excluding ancestor has been explicitly hidden. @@ -218,7 +218,7 @@ */ /*! - \fn QskQuickItem::isVisibleToParent + \fn QskItem::isVisibleToParent An item might be invisible because it has been explicitly hidden or one of its ancestors has been hidden. isVisibleToParent() indicates @@ -231,14 +231,14 @@ */ /*! - \fn QskQuickItem::hasChildItems() const + \fn QskItem::hasChildItems() const \return true, if the item has child items \sa hasChildItems */ /*! - \fn QskQuickItem::setGeometry( qreal, qreal, qreal, qreal ) + \fn QskItem::setGeometry( qreal, qreal, qreal, qreal ) Set the position and the size of an item @@ -252,7 +252,7 @@ */ /*! - \fn QskQuickItem::setGeometry( const QPointF &, const QSizeF & ) + \fn QskItem::setGeometry( const QPointF &, const QSizeF & ) Set the position and the size of an item @@ -261,7 +261,7 @@ */ /*! - \fn QskQuickItem::setGeometry( const QRectF& ) + \fn QskItem::setGeometry( const QRectF& ) Set the position and the size of an item @@ -272,14 +272,14 @@ */ /*! - \fn QskQuickItem::geometry() const + \fn QskItem::geometry() const \return Position and size relative to the parent item \sa geometry, setGeometry() */ /*! - \fn QskQuickItem::geometryChange + \fn QskItem::geometryChange This overloaded notifier calls QQuickItem::polish() depending on the polishOnResize() flag and forwards the notification to the event queue. @@ -288,14 +288,14 @@ */ /*! - \fn QskQuickItem::rect() const + \fn QskItem::rect() const \return Internal geometry of the item, where the position is always at ( 0, 0 ) \sa geometry */ /*! - \fn QskQuickItem::implicitSize + \fn QskItem::implicitSize Qt/Quick offers an oversimplified layout system that is based on the property bindings of implicit width and height. QSkinny restablishs a layout @@ -308,56 +308,56 @@ \return Implicit size, \note Layout code that relies on property bindings of the implicit width a height - needs to disable the QskQuickItem::DeferredLayout flag + needs to disable the QskItem::DeferredLayout flag \sa QskControl::preferredSize(), QskControl::sizeHint() \saqt QQuickItem::implicitWidth(), QQuickItem::implicitHeight() */ /*! - \fn QskQuickItem::setPolishOnResize + \fn QskItem::setPolishOnResize Set or clear the polishOnResize flag. \sa polishOnResize */ /*! - \fn QskQuickItem::polishOnResize() const + \fn QskItem::polishOnResize() const \return Value of the polishOnResize flag \sa setPolishOnResize() */ /*! - \fn QskQuickItem::setTransparentForPositioner + \fn QskItem::setTransparentForPositioner Set or clear the transparentForPositioner flag \sa isTransparentForPositioner() */ /*! - \fn QskQuickItem::isTransparentForPositioner + \fn QskItem::isTransparentForPositioner \return Value of the transparentForPositioner flag \sa transparentForPositioner */ /*! - \fn QskQuickItem::setTabFence + \fn QskItem::setTabFence Set or clear the tabFence property \sa isTabFence() */ /*! - \fn QskQuickItem::isTabFence + \fn QskItem::isTabFence \return Value of the tabFence property \sa setTransparentForPositioner() */ /*! - \fn QskQuickItem::setLayoutMirroring + \fn QskItem::setLayoutMirroring Change the direction how the content is laid out horizontally. @@ -373,7 +373,7 @@ */ /*! - \fn QskQuickItem::resetLayoutMirroring + \fn QskItem::resetLayoutMirroring Clear the layoutMirroring property @@ -381,7 +381,7 @@ */ /*! - \fn QskQuickItem::layoutMirroring() const + \fn QskItem::layoutMirroring() const Direction how the content is layed out horizontally. @@ -402,7 +402,7 @@ */ /*! - \fn QskQuickItem::resetUpdateFlags + \fn QskItem::resetUpdateFlags Reset all flags to the default settings @@ -410,16 +410,16 @@ */ /*! - \fn QskQuickItem::updateFlags() const + \fn QskItem::updateFlags() const \return Flags affecting the item update process \sa testUpdateFlag(), setUpdateFlag(), updateFlags */ /*! - \fn QskQuickItem::setUpdateFlag + \fn QskItem::setUpdateFlag - QskQuickItem offers several tweaks to the Qt/Quick update cycle, + QskItem offers several tweaks to the Qt/Quick update cycle, that can be en/disables individually. The default settings for these flags are taken from QskSetup::itemUpdateFlags() @@ -434,7 +434,7 @@ */ /*! - \fn QskQuickItem::resetUpdateFlag + \fn QskItem::resetUpdateFlag Reset the value for flag to the default settings from QskSetup. Future changes of the corresponding bit with QskSetup::setItemUpdateFlag() will affect @@ -445,7 +445,7 @@ */ /*! - \fn QskQuickItem::testUpdateFlag + \fn QskItem::testUpdateFlag \param Flag affecting the item update process \return true, when the corresponding bit is effective @@ -454,7 +454,7 @@ */ /*! - \fn QskQuickItem::updateFlagsChanged + \fn QskItem::updateFlagsChanged Signal indicating, that the effective update flags have changed @@ -463,7 +463,7 @@ */ /*! - \fn QskQuickItem::classBegin + \fn QskItem::classBegin The QML engine initializes an item as being incomplete by explicitly calling classBegin(). Once the item @@ -481,7 +481,7 @@ */ /*! - \fn QskQuickItem::componentComplete + \fn QskItem::componentComplete The QML engine initilizes an item as being incomplete by explicitly calling QQuickItem::classBegin(). Once the item @@ -499,7 +499,7 @@ */ /*! - \fn QskQuickItem::releaseResources + \fn QskItem::releaseResources This specific hook is called, when the item is about to be removed from the window it was previously rendering to. @@ -510,7 +510,7 @@ */ /*! - \fn QskQuickItem::isPolishScheduled + \fn QskItem::isPolishScheduled \return True, if the item will be polished in the next scene graph update cycle. @@ -519,21 +519,21 @@ */ /*! - \fn QskQuickItem::isUpdateNodeScheduled + \fn QskItem::isUpdateNodeScheduled \return True, if the item will update the paint node in the next scene graph update cycle */ /*! - \fn QskQuickItem::isInitiallyPainted + \fn QskItem::isInitiallyPainted \return Value of the initiallyPainted property \sa initiallyPainted */ /*! - \fn QskQuickItem::maybeUnresized + \fn QskItem::maybeUnresized Flag indicating a potential state, where the item is waiting to be sized by the layout system. @@ -547,7 +547,7 @@ */ /*! - \fn QskQuickItem::itemFlagsChanged + \fn QskItem::itemFlagsChanged Signal indicating that at least one if the following attributes has changed: @@ -557,7 +557,7 @@ */ /*! - \fn QskQuickItem::show + \fn QskItem::show An alternative way to call setVisible( true ). Useful for signal/slot connections @@ -567,7 +567,7 @@ */ /*! - \fn QskQuickItem::hide + \fn QskItem::hide An alternative way to call setVisible( false ). Useful for signal/slot connections @@ -577,7 +577,7 @@ */ /*! - \fn QskQuickItem::setHidden + \fn QskItem::setHidden Convenience function, equivalent to setVisible( !on ). @@ -586,19 +586,19 @@ */ /*! - \fn QskQuickItem::setDisabled + \fn QskItem::setDisabled Convenience function, equivalent to setEnabled( !on ). \saqt QQuickItem::setEnabled() */ /*! - \fn QskQuickItem::resetImplicitSize + \fn QskItem::resetImplicitSize Notifies the layout system that attributes affecting the layout system have changed. - If the QskQuickItem::DeferredLayout flag is enabled ( = default ) + If the QskItem::DeferredLayout flag is enabled ( = default ) the cached implicitSize() gets invalidated and a QEvent::LayoutRequest is sent to the parent item ( like QWidget::updateGeometry ). @@ -607,13 +607,13 @@ */ /*! - \fn QskQuickItem::event + \fn QskItem::event - QskQuickItem handles the additional type of events. + QskItem handles the additional type of events. For some reason the QQuick classes introduced proprietory notfier hooks instead of using the well established and powerful concept of events. - QskQuickItem tries to revert this decision by mapping notifications + QskItem tries to revert this decision by mapping notifications to events, when possible. The following notification events are added by QSkinny - usually with a @@ -633,37 +633,28 @@ */ /*! - \fn QskQuickItem::changeEvent + \fn QskItem::changeEvent This event handler can be reimplemented to handle certain state changes: - QEvent::StyleChange - QEvent::ContentsRectChange - - QEvent::FontChange - - QEvent::PaletteChange - QEvent::ReadOnlyChange - QEvent::EnabledChange - QEvent::LocaleChange - QEvent::ParentChange - QEvent::LayoutDirectionChange - - \param event Qt event - - \note QEvent::PaletteChange, QEvent::FontChange are handled, but both event types - do not fit to the themeing concept of QSkinny and are not posted. However - it is possible to post them if an application has a concept, where they - make sense. */ /*! - \fn QskQuickItem::geometryChangeEvent + \fn QskItem::geometryChangeEvent For no known reason QQuickItem propagates changes of position and size by calling QQuickItem::geometryChange(), instead of using events. - QskQuickItem reestablished the more powerful concept of events by sending/posting + QskItem reestablished the more powerful concept of events by sending/posting events, that can be preprocessed by event filtering. \param event Event indicating the geometry change @@ -673,7 +664,7 @@ */ /*! - \fn QskQuickItem::windowChangeEvent + \fn QskItem::windowChangeEvent This event handler can be reimplemented to handle situations, where an item is added, removed or transferred between windows. It corresponds @@ -686,7 +677,7 @@ */ /*! - \fn QskQuickItem::itemChange + \fn QskItem::itemChange For no obvious reason Qt/Quick has introduced an additional callback mechanism beside events and signals. @@ -707,7 +698,7 @@ */ /*! - \fn QskQuickItem::aboutToShow + \fn QskItem::aboutToShow A specific hook that is intended to be overloaded by controls that need to do some specific operations, when an item is painted the first time @@ -720,7 +711,7 @@ */ /*! - \fn QskQuickItem::mouseUngrabEvent + \fn QskItem::mouseUngrabEvent Notification, that is overloaded for debuging purposes, without doing anything beside calling the base class. @@ -729,7 +720,7 @@ */ /*! - \fn QskQuickItem::touchUngrabEvent + \fn QskItem::touchUngrabEvent Notification, that is overloaded for debuging purposes, without doing anything beside calling the base class. diff --git a/doc/classes/QskPlacementPolicy.dox b/doc/classes/QskPlacementPolicy.dox index e618d4ec..1be2de9b 100644 --- a/doc/classes/QskPlacementPolicy.dox +++ b/doc/classes/QskPlacementPolicy.dox @@ -53,7 +53,7 @@ The default value \accessors visiblePolicy(), setVisiblePolicy() - \sa hiddenPolicy, QskQuickItem::isVisibleToParent(), qskIsVisibleToParent() + \sa hiddenPolicy, QskItem::isVisibleToParent(), qskIsVisibleToParent() \note QskPlacementPolicy::Ignore is stored in the transparentForPositioner bit in QQuickItem and might have an impact on Qt/Quick layout code. @@ -78,7 +78,7 @@ Sometimes an item wants to have its proper size even when being hidden. \accessors hiddenPolicy(), setHiddenPolicy() - \sa visiblePolicy, QskQuickItem::isVisibleToParent(), qskIsVisibleToParent() + \sa visiblePolicy, QskItem::isVisibleToParent(), qskIsVisibleToParent() \note QskPlacementPolicy::Ignore is stored in the transparentForPositioner bit in QQuickItem and might have an impact on Qt/Quick layout code. diff --git a/doc/tutorials/11-How-to-build-for-Wasm.asciidoc b/doc/tutorials/11-How-to-build-for-Wasm.asciidoc new file mode 100644 index 00000000..6c8d7f87 --- /dev/null +++ b/doc/tutorials/11-How-to-build-for-Wasm.asciidoc @@ -0,0 +1,51 @@ +--- +title: 10. Building QSkinny for WebAssembly (Wasm) +layout: docs +--- + +:doctitle: 10. Building QSkinny for WebAssembly (Wasm) +:notitle: + +== 10. Building QSkinny for WebAssembly (Wasm) + + +=== Build Qt for Wasm + +Build Qt for Wasm from source as described here: https://doc.qt.io/qt-6/wasm.html#building-qt-from-source ; The verified Qt version for QSkinny as of this writing was 6.6.0. It might also work to use a downloaded version of Qt for Wasm, but some additional libraries will need to be built. +After configuring Qt as described in the link above, for QSkinny you will need the qtbase, qtdeclarative, qtshadertools and qtsvg modules. +Assuming the Emscripten SDK in `~/dev/emscripten` and Qt in `~/dev/qt6`, Qt can be compiled the following way: + +[source] +.... +cd ~/dev/qt6 +source "~/dev/emsdk/emsdk_env.sh" +cmake --build . -t qtbase -t qtdeclarative -t qtshadertools -t qtsvg +.... + +This will install all required libs in `~/dev/qt6/qtbase/lib`. + + +=== Build QSkinny for Wasm + +With the Qt version from above QSkinny can be built for WAsm, assuming it has been checked out at `~/dev/qskinny`. Note the configuration option `BUILD_QSKDLL=OFF` for static + builds: + +[source] +.... +mkdir -p ~/dev/qskinny-wasm-build +source "~/dev/emsdk/emsdk_env.sh" +~/dev/qt6/qtbase/bin/qt-cmake -S ~/dev/qskinny -B ~/dev/qskinny-wasm-build -DBUILD_QSKDLL=OFF +.... + + +=== Run QSkinny for Wasm + +Qt creates the HTML wrappers automatically, so there is not much to do except letting Emscripten start the server and open our app in the browser: + +[source] +.... +/usr/bin/python3 ~/dev/emsdk/upstream/emscripten/emrun.py --browser firefox --port 30001 --no_emrun_detect ~/dev/qskinny-wasm-build/examples/bin/iotdashboard.html +.... + +.The IOT dashboard example in a browser +image::../images/iotdashboard-wasm.png[The IOT dashboard example in a browser] diff --git a/doc/tutorials/images/iotdashboard-wasm.png b/doc/tutorials/images/iotdashboard-wasm.png new file mode 100644 index 00000000..a7111e95 Binary files /dev/null and b/doc/tutorials/images/iotdashboard-wasm.png differ diff --git a/examples/desktop/main.cpp b/examples/desktop/main.cpp index f5d37909..9156eebd 100644 --- a/examples/desktop/main.cpp +++ b/examples/desktop/main.cpp @@ -26,6 +26,7 @@ class SubWindow : public QskSubWindow : QskSubWindow( parent ) { setObjectName( iconSource ); + setDecorations( TitleBar | Title | Symbol ); const QUrl url( iconSource ); diff --git a/examples/gallery/CMakeLists.txt b/examples/gallery/CMakeLists.txt index 4941b1ff..d8d02042 100644 --- a/examples/gallery/CMakeLists.txt +++ b/examples/gallery/CMakeLists.txt @@ -4,7 +4,6 @@ ############################################################################ set(SOURCES - label/LabelPage.h label/LabelPage.cpp inputs/InputPage.h inputs/InputPage.cpp progressbar/ProgressBarPage.h progressbar/ProgressBarPage.cpp button/ButtonPage.h button/ButtonPage.cpp diff --git a/examples/gallery/dialog/DialogPage.cpp b/examples/gallery/dialog/DialogPage.cpp index d2dccd1e..62e8f8a1 100644 --- a/examples/gallery/dialog/DialogPage.cpp +++ b/examples/gallery/dialog/DialogPage.cpp @@ -8,8 +8,60 @@ #include #include #include -#include -#include + +#if QT_CONFIG(thread) + /* + WebAssembly without asyncify support does not allow recursive + event loops. As we did not implement QskDialog::message and friends + with callbacks yet ( TODO ) we do have some dummy code here to avoid + crashes. + */ + #define QSK_USE_EXEC +#endif + +#ifndef QSK_USE_EXEC + +#include +#include +#include + +namespace +{ + void openMessageSubWindow( QQuickWindow* window, const QString& title, + const QString& text, QskDialog::Actions actions, QskDialog::Action defaultAction ) + { + auto subWindow = new QskMessageSubWindow( window->contentItem() ); + subWindow->setPopupFlag( QskPopup::DeleteOnClose ); + subWindow->setModal( true ); + + subWindow->setTitle( title ); + subWindow->setDialogActions( actions ); + subWindow->setDefaultDialogAction( defaultAction ); + + subWindow->setText( text ); + ( void ) subWindow->open(); + } + + void openSelectSubWindow( QQuickWindow* window, const QString& title, + QskDialog::Actions actions, QskDialog::Action defaultAction, + const QStringList& entries, int selectedRow ) + { + auto subWindow = new QskSelectionSubWindow( window->contentItem() ); + subWindow->setPopupFlag( QskPopup::DeleteOnClose ); + subWindow->setModal( true ); + + subWindow->setTitle( title ); + subWindow->setDialogActions( actions ); + subWindow->setDefaultDialogAction( defaultAction ); + + subWindow->setEntries( entries ); + subWindow->setSelectedRow( selectedRow ); + + ( void ) subWindow->open(); + } +} + +#endif namespace { @@ -19,6 +71,7 @@ namespace Button( const QString& text, QQuickItem* parent = nullptr ) : QskPushButton( text, parent ) { + setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); } }; @@ -26,9 +79,10 @@ namespace { public: ButtonBox( QQuickItem* parent = nullptr ) - : QskLinearBox( Qt::Horizontal, 2, parent ) + : QskLinearBox( Qt::Vertical, parent ) { setObjectName( "ButtonBox" ); + setDefaultAlignment( Qt::AlignCenter ); setMargins( 10 ); setSpacing( 20 ); @@ -36,18 +90,9 @@ namespace auto messageButton = new Button( "Message", this ); connect( messageButton, &Button::clicked, this, &ButtonBox::execMessage ); - auto informationButton = new Button( "Information", this ); - connect( informationButton, &Button::clicked, this, &ButtonBox::execInformation ); - auto questionButton = new Button( "Question", this ); connect( questionButton, &Button::clicked, this, &ButtonBox::execQuestion ); - auto warningButton = new Button( "Warning", this ); - connect( warningButton, &Button::clicked, this, &ButtonBox::execWarning ); - - auto criticalButton = new Button( "Critical", this ); - connect( criticalButton, &Button::clicked, this, &ButtonBox::execCritical ); - auto selectButton = new Button( "Selection", this ); connect( selectButton, &Button::clicked, this, &ButtonBox::execSelection ); @@ -57,35 +102,32 @@ namespace private: void execMessage() { - qskDialog->message( "Message", "Request vector, over.", - QskStandardSymbol::NoSymbol, QskDialog::Close ); - } - - void execInformation() - { - qskDialog->information( "Information", - "We have clearance, Clarence." ); + const QString title( "Message" ); + const QString message( "Request vector, over." ); +#ifndef QSK_USE_EXEC + openMessageSubWindow( window(), title, message, + QskDialog::Ok, QskDialog::Ok ); +#else + qskDialog->information( title, message ); +#endif } void execQuestion() { - qskDialog->question( "Question", - "Roger, Roger. Do we have a vector, Victor ?" ); - } - - void execWarning() - { - qskDialog->warning( "Warning", "We have clearance, Clarence." ); - } - - void execCritical() - { - qskDialog->critical( "Critical", "That's Clarence Oveur. Over." ); + const QString title( "Question" ); + const QString question( "Roger, Roger. Do we have a vector, Victor ?" ); +#ifndef QSK_USE_EXEC + openMessageSubWindow( window(), title, question, + QskDialog::Yes | QskDialog::No, QskDialog::Yes ); +#else + (void )qskDialog->question( title, question ); +#endif } void execSelection() { - // of course we all love "The Teens" + const QString title( "The Teens" ); + const QStringList entries = { "Give Me More", @@ -108,7 +150,12 @@ namespace "Gimme Gimme Gimme Gimme Gimme Your Love" }; - qskDialog->select( "Here we go ...", "Hot Hot Hot", entries, 7 ); +#ifndef QSK_USE_EXEC + openSelectSubWindow( window(), title, + QskDialog::Ok | QskDialog::Cancel, QskDialog::Ok, entries, 7 ); +#else + (void )qskDialog->select( title, entries, 7 ); +#endif } }; } diff --git a/examples/gallery/icons/icon2qvg.sh b/examples/gallery/icons/icon2qvg.sh new file mode 100755 index 00000000..c562ca17 --- /dev/null +++ b/examples/gallery/icons/icon2qvg.sh @@ -0,0 +1,15 @@ +#! /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/examples/gallery/icons/qvg/airport_shuttle.qvg b/examples/gallery/icons/qvg/airport_shuttle.qvg index fe2cb660..fc2feaa8 100644 Binary files a/examples/gallery/icons/qvg/airport_shuttle.qvg and b/examples/gallery/icons/qvg/airport_shuttle.qvg differ diff --git a/examples/gallery/icons/qvg/flight.qvg b/examples/gallery/icons/qvg/flight.qvg index c4d0cd47..7980008b 100644 Binary files a/examples/gallery/icons/qvg/flight.qvg and b/examples/gallery/icons/qvg/flight.qvg differ diff --git a/examples/gallery/icons/qvg/local_pizza.qvg b/examples/gallery/icons/qvg/local_pizza.qvg index 84289057..bea68757 100644 Binary files a/examples/gallery/icons/qvg/local_pizza.qvg and b/examples/gallery/icons/qvg/local_pizza.qvg differ diff --git a/examples/gallery/icons/qvg/plus.qvg b/examples/gallery/icons/qvg/plus.qvg index ce9688d9..73e18e1a 100644 Binary files a/examples/gallery/icons/qvg/plus.qvg and b/examples/gallery/icons/qvg/plus.qvg differ diff --git a/examples/gallery/icons/qvg/sports_soccer.qvg b/examples/gallery/icons/qvg/sports_soccer.qvg index cbf98807..ff3876b8 100644 Binary files a/examples/gallery/icons/qvg/sports_soccer.qvg and b/examples/gallery/icons/qvg/sports_soccer.qvg differ diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index efc52e11..8f41c912 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -62,16 +62,10 @@ namespace { auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0, this ); + spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); spinBox->setPageSize( 5 ); spinBox->setValue( 35 ); } - - { - auto spinBox = new QskSpinBox( 10.0, 100.0, 1.0, this ); - spinBox->setPageSize( 10 ); - spinBox->setDecoration( QskSpinBox::NoDecoration ); - spinBox->setValue( 50 ); - } } }; } diff --git a/examples/gallery/label/LabelPage.cpp b/examples/gallery/label/LabelPage.cpp deleted file mode 100644 index f009b098..00000000 --- a/examples/gallery/label/LabelPage.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "LabelPage.h" -#include -#include -#include -#include -#include - -namespace -{ - class TextLabel : public QskTextLabel - { - public: - TextLabel( int role, QQuickItem* parent = nullptr ) - : QskTextLabel( parent ) - { - setText( textFromRole( role ) ); - setFontRole( role ); - - setSizePolicy( Qt::Horizontal, QskSizePolicy::Ignored ); - } - - private: - QString textFromRole( int role ) const - { - static QMetaEnum metaEnum; - - if ( !metaEnum.isValid() ) - { - const auto& mo = QskSkin::staticMetaObject; - metaEnum = mo.enumerator( mo.indexOfEnumerator( "SkinFontRole" ) ); - } - - QString s( metaEnum.valueToKey( role ) ); - s.remove( QStringLiteral( "Font" ) ); - - return s; - } - }; - - class TextBox : public QskLinearBox - { - public: - TextBox( QQuickItem* parent = nullptr ) - : QskLinearBox( Qt::Horizontal, 3, parent ) - { - setMargins( 10 ); - setDefaultAlignment( Qt::AlignCenter ); - - for ( int i = 0; i <= QskSkin::HugeFont; i++ ) - ( void ) new TextLabel( i, this ); - } - }; - - class IconBox : public QskLinearBox - { - public: - IconBox( QQuickItem* parent = nullptr ) - : QskLinearBox( Qt::Horizontal, 3, parent ) - { - setMargins( 10 ); - setSpacing( 20 ); - - const char* icons[] = - { - "rectangle/royalblue", - "triangleright/thistle", - "ellipse/khaki", - "ring/sandybrown", - "star/darkviolet", - "hexagon/darkslategray" - }; - - const auto prefix = QStringLiteral( "image://shapes/" ); - - for ( const auto icon : icons ) - { - auto label = new QskGraphicLabel( prefix + icon, this ); - label->setAlignment( Qt::AlignCenter ); - } - } - }; -} - -LabelPage::LabelPage( QQuickItem* parent ) - : Page( Qt::Vertical, parent ) -{ - setSpacing( 40 ); - - (void) new TextBox( this ); - (void) new QskSeparator( this ); - (void) new IconBox( this ); -} diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index 26878e3c..fad5d426 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -3,7 +3,6 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#include "label/LabelPage.h" #include "progressbar/ProgressBarPage.h" #include "inputs/InputPage.h" #include "button/ButtonPage.h" @@ -261,7 +260,6 @@ namespace { auto tabView = new TabView( this ); tabView->addPage( "Buttons", new ButtonPage() ); - tabView->addPage( "Labels", new LabelPage() ); tabView->addPage( "Inputs", new InputPage() ); tabView->addPage( "Indicators", new ProgressBarPage() ); tabView->addPage( "Selectors", new SelectorPage() ); diff --git a/examples/gallery/progressbar/ProgressBarPage.cpp b/examples/gallery/progressbar/ProgressBarPage.cpp index 668f4996..3517eb91 100644 --- a/examples/gallery/progressbar/ProgressBarPage.cpp +++ b/examples/gallery/progressbar/ProgressBarPage.cpp @@ -159,6 +159,7 @@ void ProgressBarPage::populate() { auto* ring = new QskProgressRing( determinateRingsHBox ); ring->setSize( size ); + ring->setLayoutAlignmentHint( Qt::AlignCenter ); QQuickItem* parentItem; @@ -178,7 +179,7 @@ void ProgressBarPage::populate() } } - connect( this, &QskQuickItem::windowChanged, this, [this, determinateIndicators]( QQuickWindow* window ) + connect( this, &QskItem::windowChanged, this, [this, determinateIndicators]( QQuickWindow* window ) { if( window ) { diff --git a/examples/iotdashboard/Box.cpp b/examples/iotdashboard/Box.cpp index 5ce0c9a8..93a4eb00 100644 --- a/examples/iotdashboard/Box.cpp +++ b/examples/iotdashboard/Box.cpp @@ -7,6 +7,7 @@ #include "Skin.h" #include +#include QSK_SUBCONTROL( Box, Panel ) @@ -19,7 +20,7 @@ Box::Box( const QString& title, QQuickItem* parent ) if ( !title.isEmpty() ) { auto label = new QskTextLabel( title, this ); - label->setFontRole( Skin::TitleFont ); + label->setFontRole( { QskFontRole::Caption, QskFontRole::High } ); } } diff --git a/examples/iotdashboard/BoxWithButtons.cpp b/examples/iotdashboard/BoxWithButtons.cpp index 3ad42e90..7df72a99 100644 --- a/examples/iotdashboard/BoxWithButtons.cpp +++ b/examples/iotdashboard/BoxWithButtons.cpp @@ -10,6 +10,7 @@ #include #include +#include QSK_SUBCONTROL( BoxWithButtons, ValueText ) QSK_SUBCONTROL( BoxWithButtons, ValuePanel ) @@ -66,7 +67,7 @@ BoxWithButtons::BoxWithButtons( titleAndValue->setSubcontrolProxy( QskBox::Panel, ValuePanel ); auto* titleLabel = new QskTextLabel( title, titleAndValue ); - titleLabel->setFontRole( Skin::TitleFont ); + titleLabel->setFontRole( { QskFontRole::Caption, QskFontRole::High } ); m_valueLabel = new QskTextLabel( titleAndValue ); m_valueLabel->setSubcontrolProxy( QskTextLabel::Text, ValueText ); diff --git a/examples/iotdashboard/CMakeLists.txt b/examples/iotdashboard/CMakeLists.txt index 183c0112..afeffdb9 100644 --- a/examples/iotdashboard/CMakeLists.txt +++ b/examples/iotdashboard/CMakeLists.txt @@ -6,11 +6,8 @@ set(SOURCES Box.h Box.cpp BoxWithButtons.h BoxWithButtons.cpp - CircularProgressBar.h CircularProgressBar.cpp - CircularProgressBarSkinlet.h CircularProgressBarSkinlet.cpp Diagram.h Diagram.cpp DiagramSkinlet.h DiagramSkinlet.cpp - EnergyMeter.h EnergyMeter.cpp GraphicProvider.h GraphicProvider.cpp GridBox.h GridBox.cpp LightDisplaySkinlet.h LightDisplaySkinlet.cpp @@ -31,7 +28,7 @@ set(SOURCES UsageBox.h UsageBox.cpp UsageDiagram.h UsageDiagram.cpp StoragePage.h StoragePage.cpp - StorageMeter.h StorageMeter.cpp + ValueMeter.h ValueMeter.cpp StorageBar.h StorageBar.cpp StorageBarSkinlet.h StorageBarSkinlet.cpp nodes/DiagramDataNode.h nodes/DiagramDataNode.cpp diff --git a/examples/iotdashboard/CircularProgressBar.cpp b/examples/iotdashboard/CircularProgressBar.cpp deleted file mode 100644 index 479c5668..00000000 --- a/examples/iotdashboard/CircularProgressBar.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "CircularProgressBar.h" - -#include -#include - -QSK_SUBCONTROL( CircularProgressBar, Groove ) -QSK_SUBCONTROL( CircularProgressBar, Bar ) - -namespace -{ - class PositionAnimator : public QskAnimator - { - public: - PositionAnimator( CircularProgressBar* progressBar ) - : m_progressBar( progressBar ) - { - setAutoRepeat( true ); - setDuration( 1300 ); - - setWindow( progressBar->window() ); - } - - void advance( qreal value ) override - { - const auto aspect = CircularProgressBar::Bar | QskAspect::Position; - - m_progressBar->setMetric( aspect, value ); - m_progressBar->update(); - } - - private: - CircularProgressBar* m_progressBar; - }; -} - -class CircularProgressBar::PrivateData -{ - public: - void updateIndeterminateAnimator( CircularProgressBar* progressBar ) - { - if ( !isIndeterminate ) - { - delete animator; - animator = nullptr; - - return; - } - - if ( progressBar->window() && progressBar->isVisible() ) - { - if ( animator == nullptr ) - animator = new PositionAnimator( progressBar ); - - animator->start(); - } - else - { - if ( animator ) - animator->stop(); - } - } - - PositionAnimator* animator = nullptr; - - qreal value = 0.0; - qreal origin = 0.0; - - bool hasOrigin = false; - bool isIndeterminate = false; -}; - -CircularProgressBar::~CircularProgressBar() = default; - -CircularProgressBar::CircularProgressBar( qreal min, qreal max, QQuickItem* parent ) - : QskBoundedControl( min, max, parent ) - , m_data( new PrivateData ) -{ - m_data->value = minimum(); - - initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::MinimumExpanding ); - - connect( this, &QskBoundedControl::boundariesChanged, - this, &CircularProgressBar::adjustValue ); -} - -CircularProgressBar::CircularProgressBar( QQuickItem* parent ) - : CircularProgressBar( 0.0, 100.0, parent ) -{ -} - -bool CircularProgressBar::isIndeterminate() const -{ - return m_data->isIndeterminate; -} - -void CircularProgressBar::setIndeterminate( bool on ) -{ - if ( on == m_data->isIndeterminate ) - return; - - m_data->isIndeterminate = on; - m_data->updateIndeterminateAnimator( this ); - - update(); - Q_EMIT indeterminateChanged( on ); -} - -void CircularProgressBar::resetOrigin() -{ - if ( m_data->hasOrigin ) - { - m_data->hasOrigin = false; - - update(); - Q_EMIT originChanged( origin() ); - } -} - -qreal CircularProgressBar::origin() const -{ - if ( m_data->hasOrigin ) - { - return boundedValue( m_data->origin ); - } - - return minimum(); -} - -qreal CircularProgressBar::value() const -{ - return m_data->value; -} - -qreal CircularProgressBar::valueAsRatio() const -{ - return QskBoundedControl::valueAsRatio( m_data->value ); -} - -void CircularProgressBar::setValue( qreal value ) -{ - if ( isComponentComplete() ) - value = boundedValue( value ); - - setValueInternal( value ); -} - -void CircularProgressBar::setValueAsRatio( qreal ratio ) -{ - ratio = qBound( 0.0, ratio, 1.0 ); - setValue( minimum() + ratio * boundaryLength() ); -} - -void CircularProgressBar::setOrigin( qreal origin ) -{ - if ( isComponentComplete() ) - origin = boundedValue( origin ); - - if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) ) - { - m_data->hasOrigin = true; - m_data->origin = origin; - - update(); - Q_EMIT originChanged( origin ); - } -} - -void CircularProgressBar::componentComplete() -{ - Inherited::componentComplete(); - adjustValue(); -} - -void CircularProgressBar::setValueInternal( qreal value ) -{ - if ( !qskFuzzyCompare( value, m_data->value ) ) - { - m_data->value = value; - - Q_EMIT valueChanged( value ); - - update(); - } -} - -void CircularProgressBar::adjustValue() -{ - if ( isComponentComplete() ) - setValueInternal( boundedValue( m_data->value ) ); -} - -#include "moc_CircularProgressBar.cpp" diff --git a/examples/iotdashboard/CircularProgressBar.h b/examples/iotdashboard/CircularProgressBar.h deleted file mode 100644 index 9cd84898..00000000 --- a/examples/iotdashboard/CircularProgressBar.h +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class CircularProgressBar : public QskBoundedControl -{ - Q_OBJECT - - Q_PROPERTY( bool indeterminate READ isIndeterminate - WRITE setIndeterminate NOTIFY indeterminateChanged ) - - Q_PROPERTY( qreal origin READ origin - WRITE setOrigin RESET resetOrigin NOTIFY originChanged ) - - Q_PROPERTY( qreal value READ value WRITE setValue NOTIFY valueChanged ) - Q_PROPERTY( qreal valueAsRatio READ valueAsRatio - WRITE setValueAsRatio NOTIFY valueChanged ) - - using Inherited = QskBoundedControl; - - public: - QSK_SUBCONTROLS( Groove, Bar ) - - CircularProgressBar( qreal min, qreal max, QQuickItem* parent = nullptr ); - CircularProgressBar( QQuickItem* parent = nullptr ); - ~CircularProgressBar() override; - - bool isIndeterminate() const; - void setIndeterminate( bool on = true ); - - void resetOrigin(); - qreal origin() const; - - qreal value() const; - qreal valueAsRatio() const; // [0.0, 1.0] - - public Q_SLOTS: - void setValue( qreal ); - void setValueAsRatio( qreal ); - void setOrigin( qreal ); - - Q_SIGNALS: - void indeterminateChanged( bool ); - void valueChanged( qreal ); - void originChanged( qreal ); - - protected: - void componentComplete() override; - - private: - void setValueInternal( qreal value ); - void adjustValue(); - - class PrivateData; - std::unique_ptr< PrivateData > m_data; -}; diff --git a/examples/iotdashboard/CircularProgressBarSkinlet.cpp b/examples/iotdashboard/CircularProgressBarSkinlet.cpp deleted file mode 100644 index 0bd5f418..00000000 --- a/examples/iotdashboard/CircularProgressBarSkinlet.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "CircularProgressBarSkinlet.h" -#include "CircularProgressBar.h" - -CircularProgressBarSkinlet::CircularProgressBarSkinlet( QskSkin* skin ) - : QskSkinlet( skin ) -{ - setNodeRoles( { GrooveRole, BarRole } ); -} - -CircularProgressBarSkinlet::~CircularProgressBarSkinlet() -{ -} - -QRectF CircularProgressBarSkinlet::subControlRect( - const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const -{ - return contentsRect; -} - -QSGNode* CircularProgressBarSkinlet::updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const -{ - switch( nodeRole ) - { - case GrooveRole: - { - return updateArcNode( skinnable, node, CircularProgressBar::Groove ); - } - case BarRole: - { - const auto bar = static_cast< const CircularProgressBar* >( skinnable ); - - const qreal startAngle = 90.0; - const qreal spanAngle = 360.0 * bar->valueAsRatio(); - - return updateArcNode( skinnable, node, startAngle, -spanAngle, - CircularProgressBar::Bar ); - } - } - - return Inherited::updateSubNode( skinnable, nodeRole, node ); -} - -#include "moc_CircularProgressBarSkinlet.cpp" diff --git a/examples/iotdashboard/CircularProgressBarSkinlet.h b/examples/iotdashboard/CircularProgressBarSkinlet.h deleted file mode 100644 index 9075fbb3..00000000 --- a/examples/iotdashboard/CircularProgressBarSkinlet.h +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class CircularProgressBar; - -class CircularProgressBarSkinlet : public QskSkinlet -{ - Q_GADGET - - using Inherited = QskSkinlet; - - public: - enum NodeRole - { - GrooveRole, - BarRole, - - RoleCount, - }; - - Q_INVOKABLE CircularProgressBarSkinlet( QskSkin* = nullptr ); - ~CircularProgressBarSkinlet() override; - - QRectF subControlRect( const QskSkinnable*, - const QRectF&, QskAspect::Subcontrol ) const override; - - protected: - QSGNode* updateSubNode( const QskSkinnable*, - quint8 nodeRole, QSGNode* ) const override; -}; diff --git a/examples/iotdashboard/DashboardPage.cpp b/examples/iotdashboard/DashboardPage.cpp index a91291bf..3c7f5028 100644 --- a/examples/iotdashboard/DashboardPage.cpp +++ b/examples/iotdashboard/DashboardPage.cpp @@ -14,17 +14,6 @@ #include "TopBar.h" #include "UsageBox.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include - QSK_SUBCONTROL( DashboardPage, Panel ) namespace diff --git a/examples/iotdashboard/DevicesPage.cpp b/examples/iotdashboard/DevicesPage.cpp index 811840bc..8a394383 100644 --- a/examples/iotdashboard/DevicesPage.cpp +++ b/examples/iotdashboard/DevicesPage.cpp @@ -5,8 +5,8 @@ #include "DevicesPage.h" -#include #include +#include QSK_SUBCONTROL( DevicesPage, Panel ) @@ -15,7 +15,7 @@ DevicesPage::DevicesPage( QQuickItem* parent ) { auto* const textLabel = new QskTextLabel( "devices page", this ); textLabel->setAlignmentHint( QskTextLabel::Text, Qt::AlignCenter ); - textLabel->setFontRole( QskSkin::HugeFont ); + textLabel->setFontRole( QskFontRole::Display ); } #include "moc_DevicesPage.cpp" diff --git a/examples/iotdashboard/EnergyMeter.cpp b/examples/iotdashboard/EnergyMeter.cpp deleted file mode 100644 index 836dc977..00000000 --- a/examples/iotdashboard/EnergyMeter.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "EnergyMeter.h" -#include "CircularProgressBar.h" - -#include -#include - -namespace -{ - class ValueLabel : public QskTextLabel - { - public: - ValueLabel( QQuickItem* parent ) - : QskTextLabel( parent ) - { - initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - setLayoutAlignmentHint( Qt::AlignCenter ); - setFontRole( QskSkin::SmallFont ); - } - - void setValue( int value ) - { - setText( locale().toString( value ) + " " + locale().percent() ); - } - }; -} - -EnergyMeter::EnergyMeter( const QColor& textColor, - const QskGradient& gradient, int value, QQuickItem* parent ) - : QskControl( parent ) -{ - setAutoLayoutChildren( true ); - - auto valueBar = new CircularProgressBar( this ); - valueBar->setGradientHint( CircularProgressBar::Bar, gradient ); - valueBar->setValue( value ); - - auto valueLabel = new ValueLabel( this ); - valueLabel->setTextColor( textColor ); - valueLabel->setValue( value ); -} - -QSizeF EnergyMeter::contentsSizeHint( - Qt::SizeHint which, const QSizeF& constraint ) const -{ - if ( which != Qt::PreferredSize ) - return QSizeF(); - - qreal size; - - if ( constraint.width() > 0 ) - { - size = constraint.width(); - } - else if ( constraint.height() > 0 ) - { - size = constraint.height(); - } - else - { - size = 57; - } - - return QSizeF( size, size ); -} diff --git a/examples/iotdashboard/EnergyMeter.h b/examples/iotdashboard/EnergyMeter.h deleted file mode 100644 index 7780dd9e..00000000 --- a/examples/iotdashboard/EnergyMeter.h +++ /dev/null @@ -1,18 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class EnergyMeter : public QskControl -{ - public: - EnergyMeter( const QColor&, const QskGradient&, - int progress, QQuickItem* parent = nullptr ); - - protected: - QSizeF contentsSizeHint( Qt::SizeHint, const QSizeF& ) const override; -}; diff --git a/examples/iotdashboard/LightDisplay.cpp b/examples/iotdashboard/LightDisplay.cpp index 9f305b51..8ef43ea3 100644 --- a/examples/iotdashboard/LightDisplay.cpp +++ b/examples/iotdashboard/LightDisplay.cpp @@ -51,17 +51,6 @@ bool LightDisplay::isPressed() const return hasSkinState( Pressed ); } -void LightDisplay::setGradient( const QskGradient& gradient ) -{ - m_gradient = gradient; - update(); -} - -const QskGradient& LightDisplay::gradient() const -{ - return m_gradient; -} - void LightDisplay::mousePressEvent( QMouseEvent* event ) { QRectF handleRect = subControlRect( LightDisplay::Knob ); diff --git a/examples/iotdashboard/LightDisplay.h b/examples/iotdashboard/LightDisplay.h index bddd5f47..8c7728a5 100644 --- a/examples/iotdashboard/LightDisplay.h +++ b/examples/iotdashboard/LightDisplay.h @@ -6,7 +6,6 @@ #pragma once #include -#include #include class LightDisplay : public QskBoundedValueInput @@ -22,9 +21,6 @@ class LightDisplay : public QskBoundedValueInput bool isPressed() const; - void setGradient( const QskGradient& ); - const QskGradient& gradient() const; - protected: void mousePressEvent( QMouseEvent* e ) override; void mouseMoveEvent( QMouseEvent* e ) override; @@ -33,9 +29,4 @@ class LightDisplay : public QskBoundedValueInput private: qreal angleFromPoint( const QRectF&, const QPointF& ) const; bool arcContainsPoint( const QRectF&, const QPointF& ) const; - - QskShadowMetrics m_shadow; - QColor m_shadowColor = Qt::black; - - QskGradient m_gradient; }; diff --git a/examples/iotdashboard/MembersPage.cpp b/examples/iotdashboard/MembersPage.cpp index 691d8731..cbd6812b 100644 --- a/examples/iotdashboard/MembersPage.cpp +++ b/examples/iotdashboard/MembersPage.cpp @@ -7,6 +7,7 @@ #include #include +#include QSK_SUBCONTROL( MembersPage, Panel ) @@ -15,7 +16,7 @@ MembersPage::MembersPage( QQuickItem* parent ) { auto* const textLabel = new QskTextLabel( "members page", this ); textLabel->setAlignmentHint( QskTextLabel::Text, Qt::AlignCenter ); - textLabel->setFontRole( QskSkin::HugeFont ); + textLabel->setFontRole( QskFontRole::Display ); } #include "moc_MembersPage.cpp" diff --git a/examples/iotdashboard/MyDevices.cpp b/examples/iotdashboard/MyDevices.cpp index edeb7a5a..6e44f1ed 100644 --- a/examples/iotdashboard/MyDevices.cpp +++ b/examples/iotdashboard/MyDevices.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -32,7 +33,7 @@ namespace icon->setCheckable( true ); auto textLabel = new QskTextLabel( name, this ); - textLabel->setFontRole( QskSkin::TinyFont ); + textLabel->setFontRole( { QskFontRole::Caption, QskFontRole::Low } ); textLabel->setAlignment( Qt::AlignHCenter ); } }; diff --git a/examples/iotdashboard/Skin.cpp b/examples/iotdashboard/Skin.cpp index 24392fe6..09b7b3c3 100644 --- a/examples/iotdashboard/Skin.cpp +++ b/examples/iotdashboard/Skin.cpp @@ -7,8 +7,6 @@ #include "Box.h" #include "BoxWithButtons.h" -#include "CircularProgressBar.h" -#include "CircularProgressBarSkinlet.h" #include "DashboardPage.h" #include "Diagram.h" #include "DiagramSkinlet.h" @@ -21,7 +19,6 @@ #include "RoundedIcon.h" #include "StorageBar.h" #include "StorageBarSkinlet.h" -#include "StorageMeter.h" #include "StoragePage.h" #include "TopBar.h" #include "UsageBox.h" @@ -37,23 +34,18 @@ #include #include #include +#include #include +#include #include namespace { - static inline QFont qskFont( qreal pointSize, bool semiBold = false ) + inline QFont createFont( qreal pointSize, + QFont::Weight weight = QFont::Normal ) { - QFont font( "Proxima Nova" ); - - if ( semiBold ) - { - font.setWeight( QFont::Bold ); - } - - font.setPointSizeF( pointSize /*/ qskDpiScaled( 1.0 )*/ ); - return font; + return QFont( QStringLiteral( "Proxima Nova" ), pointSize, weight ); } } @@ -62,7 +54,6 @@ Skin::Skin( QObject* parent ) { setObjectName( "iot" ); - declareSkinlet< CircularProgressBar, CircularProgressBarSkinlet >(); declareSkinlet< Diagram, DiagramSkinlet >(); declareSkinlet< LightDisplay, LightDisplaySkinlet >(); declareSkinlet< StorageBar, StorageBarSkinlet >(); @@ -78,25 +69,24 @@ void Skin::initHints() { const auto palette = Skin::palette( colorScheme() ); - QFontDatabase db; - db.addApplicationFont( ":/fonts/ProximaNova-Regular.otf" ); // ### use fontconfig + QFontDatabase::addApplicationFont( ":/fonts/ProximaNova-Regular.otf" ); - setFont( QskSkin::DefaultFont, qskFont( 12 ) ); - setFont( QskSkin::TinyFont, qskFont( 9 ) ); - setFont( QskSkin::SmallFont, qskFont( 10 ) ); - setFont( QskSkin::MediumFont, qskFont( 13 ) ); - setFont( QskSkin::LargeFont, qskFont( 20 ) ); - setFont( QskSkin::HugeFont, qskFont( 27, true ) ); + setFont( { QskFontRole::Caption, QskFontRole::Low }, createFont( 9 ) ); + setFont( { QskFontRole::Caption, QskFontRole::Normal }, createFont( 10 ) ); + setFont( { QskFontRole::Caption, QskFontRole::High }, createFont( 10, QFont::Bold ) ); - setFont( Skin::TitleFont, qskFont( 10, true ) ); + setFont( { QskFontRole::Body, QskFontRole::Normal }, createFont( 12 ) ); + setFont( { QskFontRole::Subtitle, QskFontRole::Normal }, createFont( 13 ) ); + setFont( { QskFontRole::Headline, QskFontRole::Normal }, createFont( 20 ) ); + setFont( { QskFontRole::Display, QskFontRole::Normal }, createFont( 27, QFont::Bold ) ); QskSkinHintTableEditor ed( &hintTable() ); ed.setPadding( MainContentGridBox::Panel, { 19, 0, 27, 24 } ); - // menu bar: - { + // menu bar: + using Q = QskPushButton; using A = QskAspect; @@ -112,7 +102,7 @@ void Skin::initHints() ed.setSpacing( Q::Panel | A::Header, 10 ); ed.setColor( Q::Text | A::Header, Qt::white ); - ed.setFontRole( Q::Text | A::Header, QskSkin::SmallFont ); + ed.setFontRole( Q::Text | A::Header, QskFontRole::Caption ); ed.setAlignment( Q::Text | A::Header, Qt::AlignLeft | Qt::AlignVCenter ); ed.setPadding( Q::Icon | A::Header, { 30, 0, 0, 0 } ); @@ -120,36 +110,38 @@ void Skin::initHints() ed.setAlignment( Q::Icon | A::Header, Qt::AlignCenter ); } - // top bar: - ed.setPadding( TopBar::Panel, { 25, 35, 25, 0 } ); + { + // top bar: - ed.setColor( TopBarItem::Item1 | QskAspect::TextColor, 0xffff3122 ); - ed.setColor( TopBarItem::Item2 | QskAspect::TextColor, 0xff6776ff ); - ed.setColor( TopBarItem::Item3 | QskAspect::TextColor, 0xfff99055 ); - ed.setColor( TopBarItem::Item4 | QskAspect::TextColor, 0xff6776ff ); + ed.setPadding( TopBar::Panel, { 25, 35, 25, 0 } ); - // arcs are counterclockwise, so specify the 2nd color first: - ed.setGradient( TopBarItem::Item1, 0xffff3122, 0xffff5c00 ); - ed.setGradient( TopBarItem::Item2, 0xff6100ff, 0xff6776ff ); - ed.setGradient( TopBarItem::Item3, 0xffff3122, 0xffffce50 ); - ed.setGradient( TopBarItem::Item4, 0xff6100ff, 0xff6776ff ); + ed.setColor( TopBarItem::Item1 | QskAspect::TextColor, 0xffff3122 ); + ed.setColor( TopBarItem::Item2 | QskAspect::TextColor, 0xff6776ff ); + ed.setColor( TopBarItem::Item3 | QskAspect::TextColor, 0xfff99055 ); + ed.setColor( TopBarItem::Item4 | QskAspect::TextColor, 0xff6776ff ); + + ed.setGradient( TopBarItem::Item1, 0xffff5c00, 0xffff3122 ); + ed.setGradient( TopBarItem::Item2, 0xff6776ff, 0xff6100ff ); + ed.setGradient( TopBarItem::Item3, 0xffffce50, 0xffff3122 ); + ed.setGradient( TopBarItem::Item4, 0xff6776ff, 0xff6100ff ); + } // the bar gradient is defined through the top bar items above - ed.setArcMetrics( CircularProgressBar::Groove, 90, -360, 8.53 ); + ed.setArcMetrics( QskProgressRing::Groove, 90, -360, 8.53 ); // the span angle will be set in the progress bar, we just give a dummy // value here: - ed.setArcMetrics( CircularProgressBar::Bar, 90, -180, 8.53 ); + ed.setArcMetrics( QskProgressRing::Fill, ed.arcMetrics( QskProgressRing::Groove ) ); - ed.setFontRole( TimeTitleLabel::Text, Skin::TitleFont ); + ed.setFontRole( TimeTitleLabel::Text, { QskFontRole::Caption, QskFontRole::High } ); - ed.setFontRole( TimeLabel::Text, QskSkin::HugeFont ); + ed.setFontRole( TimeLabel::Text, QskFontRole::Display ); ed.setColor( TimeLabel::Text, 0xff6776ff ); // boxes: ed.setPadding( Box::Panel, 8 ); // content in boxes (indoor temperature, humidity etc.): - ed.setFontRole( UsageBox::Separator, QskSkin::SmallFont ); + ed.setFontRole( UsageBox::Separator, QskFontRole::Caption ); ed.setColor( UsageBox::Separator, 0xffdddddd ); ed.setPadding( BoxWithButtons::Panel, 8 ); @@ -199,7 +191,7 @@ void Skin::initHints() ed.setGradient( subControl | RoundedIcon::Bright, bright ); } - ed.setFontRole( BoxWithButtons::ValueText, QskSkin::HugeFont ); + ed.setFontRole( BoxWithButtons::ValueText, QskFontRole::Display ); ed.setColor( BoxWithButtons::ValueText, 0xff929cb2 ); ed.setPadding( BoxWithButtons::ValuePanel, 0, 10, 0, 0 ); @@ -210,7 +202,7 @@ void Skin::initHints() // diagram: ed.setBoxBorderMetrics( UsageDiagramBox::DaysBox, 0, 0, 3, 3 ); - ed.setFontRole( UsageDiagramBox::DayText, QskSkin::TinyFont ); + ed.setFontRole( UsageDiagramBox::DayText, { QskFontRole::Caption, QskFontRole::Low } ); ed.setStrutSize( UsageDiagramLegend::Symbol, 8, 8 ); ed.setBoxShape( UsageDiagramLegend::Symbol, 100, Qt::RelativeSize ); // a circle @@ -246,7 +238,7 @@ void Skin::initHints() ed.setArcMetrics( LightDisplay::Tickmarks, 0, 180, 4.69 ); ed.setColor( LightDisplay::Tickmarks, 0x55929cb2 ); - ed.setFontRole( LightDisplay::ValueText, QskSkin::LargeFont ); + ed.setFontRole( LightDisplay::ValueText, QskFontRole::Headline ); ed.setColor( LightDisplay::ValueText, 0xff929cb2 ); ed.setStrutSize( LightDisplay::Knob, { 20, 20 } ); @@ -290,8 +282,8 @@ void Skin::initHints() ed.setColor( QskTextLabel::Text, palette.text ); ed.setColor( UsageDiagramBox::DayText, palette.text ); - ed.setMetric( CircularProgressBar::Groove | QskAspect::Border, 2 ); - ed.setColor( CircularProgressBar::Groove | QskAspect::Border, + ed.setMetric( QskProgressRing::Groove | QskAspect::Border, 2 ); + ed.setColor( QskProgressRing::Groove | QskAspect::Border, palette.circularProgressBarGroove ); // storage bar @@ -309,9 +301,13 @@ void Skin::initHints() // storage meter { - ed.setGradient( StorageMeter::Status, - { { { 0.00, "#00ff00" }, { 0.33, "#00ff00" }, { 0.33, "#ffaf00" }, { 0.66, "#ffaf00" }, - { 0.66, "#ff0000" }, { 1.00, "#ff0000" } } } ); + ed.setGradient( StoragePage::Status, + { { + { 0.00, "#00ff00" }, { 0.33, "#00ff00" }, + { 0.33, "#ffaf00" }, { 0.66, "#ffaf00" }, + { 0.66, "#ff0000" }, { 1.00, "#ff0000" } + } } + ); } } @@ -329,7 +325,7 @@ Skin::Palette Skin::palette( QskSkin::ColorScheme colorScheme ) const Qt::white, 0xff4a4a4a, 0xff555555, - { { { 0.0, 0xff991100 }, { 0.2, 0xff9a7a57 }, { 0.5, 0xff3726af }, { 1.0, Qt::black } } }, + { { { 0.0, 0xff991100 }, { 0.4, 0xff9a7a57 }, { 1.0, 0xff3726af } } }, 0x10ffffff, 0xff222222 }; @@ -346,8 +342,7 @@ Skin::Palette Skin::palette( QskSkin::ColorScheme colorScheme ) const Qt::black, 0xffe5e5e5, 0xffc4c4c4, - { { { 0.0, 0xffff3122 }, { 0.2, 0xfffeeeb7 }, { 0.3, 0xffa7b0ff }, { 0.5, 0xff6776ff }, - { 1.0, Qt::black } } }, + { { { 0.0, 0xffff3122 }, { 0.4, 0xfffeeeb7 }, { 0.6, 0xffa7b0ff }, { 1.0, 0xff6776ff } } }, 0x10000000, 0xffdddddd }; diff --git a/examples/iotdashboard/Skin.h b/examples/iotdashboard/Skin.h index 1b0d18d1..1a06c446 100644 --- a/examples/iotdashboard/Skin.h +++ b/examples/iotdashboard/Skin.h @@ -30,14 +30,8 @@ class Skin : public QskSkin Skin( QObject* parent = nullptr ); ~Skin() override; - enum SkinFontRole - { - TitleFont = QskSkin::HugeFont + 1, - }; - private: void initHints() override; Palette palette( ColorScheme ) const; - void initHints( const Palette& ); }; diff --git a/examples/iotdashboard/StatisticsPage.cpp b/examples/iotdashboard/StatisticsPage.cpp index 93427f39..be949611 100644 --- a/examples/iotdashboard/StatisticsPage.cpp +++ b/examples/iotdashboard/StatisticsPage.cpp @@ -5,8 +5,8 @@ #include "StatisticsPage.h" -#include #include +#include QSK_SUBCONTROL( StatisticsPage, Panel ) @@ -15,7 +15,7 @@ StatisticsPage::StatisticsPage( QQuickItem* parent ) { auto* const textLabel = new QskTextLabel( "statistics page", this ); textLabel->setAlignmentHint( QskTextLabel::Text, Qt::AlignCenter ); - textLabel->setFontRole( QskSkin::HugeFont ); + textLabel->setFontRole( QskFontRole::Display ); } #include "moc_StatisticsPage.cpp" diff --git a/examples/iotdashboard/StorageBar.cpp b/examples/iotdashboard/StorageBar.cpp index c717e1f7..2bc1213b 100644 --- a/examples/iotdashboard/StorageBar.cpp +++ b/examples/iotdashboard/StorageBar.cpp @@ -18,7 +18,7 @@ QSK_SUBCONTROL( StorageBar, Free ) using S = StorageBar; -StorageBar::StorageBar( QskQuickItem* const parent ) +StorageBar::StorageBar( QskItem* const parent ) : Inherited( parent ) { static constexpr qreal size = 16.0; diff --git a/examples/iotdashboard/StorageBar.h b/examples/iotdashboard/StorageBar.h index b8773b34..ba841227 100644 --- a/examples/iotdashboard/StorageBar.h +++ b/examples/iotdashboard/StorageBar.h @@ -20,7 +20,7 @@ class StorageBar final : public QskControl public: QSK_SUBCONTROLS( Pictures, Music, Videos, Documents, Others, Free ) - explicit StorageBar( QskQuickItem* parent = nullptr ); + explicit StorageBar( QskItem* parent = nullptr ); qreal pictures() const; void setPictures( qreal newPictures ); diff --git a/examples/iotdashboard/StorageMeter.cpp b/examples/iotdashboard/StorageMeter.cpp deleted file mode 100644 index 73bc39e8..00000000 --- a/examples/iotdashboard/StorageMeter.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2022 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "StorageMeter.h" -#include "CircularProgressBar.h" -#include -#include - -QSK_SUBCONTROL( StorageMeter, Status ) - -namespace -{ - inline QString make_text( const QLocale& locale, const qreal value ) - { - return locale.toString( static_cast< int >( value ) ) + " " + locale.percent(); - } -} - -StorageMeter::StorageMeter( QQuickItem* parent ) noexcept - : CircularProgressBar( parent ) - , label( new QskTextLabel( this ) ) -{ - setAutoLayoutChildren( true ); - setSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Constrained ); - - label->setText( make_text( locale(), value() ) ); - label->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - label->setLayoutAlignmentHint( Qt::AlignCenter ); - label->setFontRole( QskSkin::SmallFont ); -} - -void StorageMeter::setValue( const qreal value ) -{ - const auto gradient = gradientHint( StorageMeter::Status ); - const auto color = gradient.extracted( value / 100.0, value / 100.0 ).startColor(); - setGradientHint( StorageMeter::Bar, { color, color.lighter() } ); - CircularProgressBar::setValue( value ); - label->setTextColor( color ); - label->setText( make_text( locale(), value ) ); -} - -QSizeF StorageMeter::contentsSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const -{ - if ( which != Qt::PreferredSize ) - return QSizeF(); - - qreal size; - - if ( constraint.width() > 0 ) - { - size = constraint.width(); - } - else if ( constraint.height() > 0 ) - { - size = constraint.height(); - } - else - { - size = 57; - } - - return QSizeF( size, size ); -} diff --git a/examples/iotdashboard/StorageMeter.h b/examples/iotdashboard/StorageMeter.h deleted file mode 100644 index 16a8f454..00000000 --- a/examples/iotdashboard/StorageMeter.h +++ /dev/null @@ -1,22 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2022 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include "CircularProgressBar.h" -#include - -class StorageMeter final : public CircularProgressBar -{ - public: - QSK_SUBCONTROLS( Status ) - explicit StorageMeter( QQuickItem* parent = nullptr ) noexcept; - public Q_SLOTS: - void setValue( qreal value ); - - private: - QSizeF contentsSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const override; - class QskTextLabel* label = nullptr; -}; diff --git a/examples/iotdashboard/StoragePage.cpp b/examples/iotdashboard/StoragePage.cpp index 35261843..038a6e39 100644 --- a/examples/iotdashboard/StoragePage.cpp +++ b/examples/iotdashboard/StoragePage.cpp @@ -6,7 +6,7 @@ #include "StoragePage.h" #include "Box.h" #include "StorageBar.h" -#include "StorageMeter.h" +#include "ValueMeter.h" #include #include #include @@ -15,11 +15,65 @@ #include #include #include -#include +#include #include #include QSK_SUBCONTROL( StoragePage, Panel ) +QSK_SUBCONTROL( StoragePage, Status ) + +namespace +{ + struct Storage + { + struct Media + { + qreal pictures = 0; + qreal music = 0; + qreal videos = 0; + qreal documents = 0; + qreal others = 0; + + inline constexpr bool operator==( const Media& rhs ) const noexcept + { + return pictures == rhs.pictures && music == rhs.music && videos == rhs.videos && + documents == rhs.documents && others == rhs.others; + } + + inline constexpr qreal free() const noexcept + { + return 1.0 - pictures - music - videos - documents - others; + } + }; + + QString title; + QString description; + Media distribution; + }; + + class StorageMeter final : public ValueMeter + { + public: + StorageMeter( QQuickItem* parent = nullptr ) + : ValueMeter( parent ) + { + setFixedSize( 64, 64 ); + + connect( this, &ValueMeter::valueChanged, + this, &StorageMeter::setStatusColor ); + } + + private: + void setStatusColor( qreal value ) + { + const auto color = qskInterpolatedColorAt( + gradientHint( StoragePage::Status ).stops(), value / 100.0 ); + + setFillGradient( { color, color.lighter() } ); + setTextColor( color ); + } + }; +} struct StorageRowAnimator final : public QObject, public QskAnimator { @@ -85,13 +139,11 @@ void StoragePage::addRow( const QString& title, const QString& description, const auto percent = 100.0 * ( 1.0 - storage.distribution.free() ); auto* const meter = new StorageMeter( left ); meter->setValue( percent ); - meter->setMinimumSize( 64, 64 ); - meter->setMaximumSize( 64, 64 ); auto* const maintitle = new QskTextLabel( storage.title, center ); - maintitle->setFontRole( QskSkin::LargeFont ); + maintitle->setFontRole( QskFontRole::Headline ); auto* const subtitle = new QskTextLabel( storage.description, center ); - subtitle->setFontRole( QskSkin::MediumFont ); + subtitle->setFontRole( QskFontRole::Subtitle ); const auto& media = storage.distribution; @@ -106,7 +158,7 @@ void StoragePage::addRow( const QString& title, const QString& description, legend->setSpacing( 12 ); legend->addSpacer( 1, 999 ); auto* const sync = new QskPushButton( "Update", legend ); - sync->setFontRoleHint( QskPushButton::Text, QskSkin::SmallFont ); + sync->setFontRoleHint( QskPushButton::Text, QskFontRole::Caption ); using S = StorageBar; for ( const auto& pair : QVector< QPair< QString, QskGradient > >{ @@ -124,7 +176,7 @@ void StoragePage::addRow( const QString& title, const QString& description, dot->setMaximumSize( size, size ); dot->setGradientHint( QskBox::Panel, pair.second ); auto* const label = new QskTextLabel( pair.first, legend ); - label->setFontRole( QskSkin::SmallFont ); + label->setFontRole( QskFontRole::Caption ); } auto* const animator = new StorageRowAnimator( window(), sync ); @@ -136,5 +188,6 @@ void StoragePage::addRow( const QString& title, const QString& description, bar->setDocuments( media.documents * v ); bar->setOthers( media.others * v ); }; - connect( sync, &QskPushButton::clicked, animator, [ animator ]() { animator->start(); } ); + connect( sync, &QskPushButton::clicked, + animator, [ animator ]() { animator->start(); } ); } diff --git a/examples/iotdashboard/StoragePage.h b/examples/iotdashboard/StoragePage.h index efb09d41..7f1d0146 100644 --- a/examples/iotdashboard/StoragePage.h +++ b/examples/iotdashboard/StoragePage.h @@ -7,44 +7,15 @@ #include #include -#include - -class QQuickItem; class StoragePage final : public QskLinearBox { public: - QSK_SUBCONTROLS( Panel ) - explicit StoragePage( QQuickItem* parent = nullptr ); + QSK_SUBCONTROLS( Panel, Status ) + + StoragePage( QQuickItem* parent = nullptr ); private: - struct Storage - { - struct Media - { - qreal pictures = 0; - qreal music = 0; - qreal videos = 0; - qreal documents = 0; - qreal others = 0; - - inline constexpr bool operator==( const Media& rhs ) const noexcept - { - return pictures == rhs.pictures && music == rhs.music && videos == rhs.videos && - documents == rhs.documents && others == rhs.others; - } - - inline constexpr qreal free() const noexcept - { - return 1.0 - pictures - music - videos - documents - others; - } - }; - - QString title; - QString description; - Media distribution; - }; - void addRow( const QString& title, const QString& description, qreal pictures, qreal music, qreal videos, qreal documents, qreal others ); }; diff --git a/examples/iotdashboard/TopBar.cpp b/examples/iotdashboard/TopBar.cpp index f869bb8c..0667f21e 100644 --- a/examples/iotdashboard/TopBar.cpp +++ b/examples/iotdashboard/TopBar.cpp @@ -4,9 +4,9 @@ *****************************************************************************/ #include "TopBar.h" -#include "EnergyMeter.h" +#include "ValueMeter.h" -#include +#include #include #include @@ -41,6 +41,22 @@ namespace return TopBarItem::Item4; } } + + class EnergyMeter : public ValueMeter + { + public: + EnergyMeter( const QColor& textColor, + const QskGradient& gradient, int value, QQuickItem* parent ) + : ValueMeter( parent ) + { + setFillGradient( gradient ); + setValue( value ); + setTextColor( textColor ); + + setFixedSize( 57, 57 ); + } + }; + } TopBarItem::TopBarItem( @@ -54,7 +70,7 @@ TopBarItem::TopBarItem( setSpacing( 15 ); auto* textLabel = new QskTextLabel( name, this ); - textLabel->setFontRole( QskSkin::SmallFont ); + textLabel->setFontRole( QskFontRole::Caption ); auto* pieChartAndDisplay = new QskLinearBox( Qt::Horizontal, this ); pieChartAndDisplay->setSpacing( 10 ); @@ -62,19 +78,17 @@ TopBarItem::TopBarItem( const auto subcontrol = subcontrolForIndex( index ); const auto textColor = color( subcontrol | QskAspect::TextColor ); - auto pieChart = new EnergyMeter( - textColor, gradient, progress, pieChartAndDisplay ); - pieChart->setSizePolicy( Qt::Horizontal, QskSizePolicy::Constrained ); + (void) new EnergyMeter( textColor, gradient, progress, pieChartAndDisplay ); auto display = new QskLinearBox( Qt::Vertical, pieChartAndDisplay ); display->setSpacing( 0 ); display->addSpacer( 0, 1 ); auto displayValue = new QskTextLabel( QString::number( value ), display ); - displayValue->setFontRole( QskSkin::MediumFont ); + displayValue->setFontRole( QskFontRole::Subtitle ); auto displayUnit = new QskTextLabel( "kwH", display ); - displayUnit->setFontRole( QskSkin::SmallFont ); + displayUnit->setFontRole( QskFontRole::Caption ); display->addSpacer( 0, 1 ); } diff --git a/examples/iotdashboard/UsageBox.cpp b/examples/iotdashboard/UsageBox.cpp index 5ade4bc2..d8ea9ad3 100644 --- a/examples/iotdashboard/UsageBox.cpp +++ b/examples/iotdashboard/UsageBox.cpp @@ -4,22 +4,28 @@ *****************************************************************************/ #include "UsageBox.h" -#include "Skin.h" +#include #include QSK_SUBCONTROL( UsageBox, Separator ) namespace { - class SeparatorLabel : public QskTextLabel + class UsageLine : public QskLinearBox { public: - - SeparatorLabel( QQuickItem* parent = nullptr ) - : QskTextLabel( "_____", parent ) + UsageLine( const QString& info, const QString& value, QQuickItem* parent ) + : QskLinearBox( Qt::Horizontal, parent ) { - setSubcontrolProxy( QskTextLabel::Text, UsageBox::Separator ); + auto infoLabel = new QskTextLabel( info, this ); + infoLabel->setFontRole( QskFontRole::Caption ); + + auto separator = new QskTextLabel( "_____", this ); + separator->setSubcontrolProxy( QskTextLabel::Text, UsageBox::Separator ); + + auto valueLabel = new QskTextLabel( value, this ); + valueLabel->setFontRole( QskFontRole::Caption ); } }; } @@ -27,30 +33,7 @@ namespace UsageBox::UsageBox( QQuickItem* parent ) : Box( "Usage", parent ) { - auto today = new QskLinearBox( Qt::Horizontal, this ); - auto todayText = new QskTextLabel( "Usage today", today ); - todayText->setFontRole( QskSkin::SmallFont ); - - new SeparatorLabel( today ); - - auto todayValue = new QskTextLabel( "0,5 kwH", today ); - todayValue->setFontRole( QskSkin::SmallFont ); - - auto month = new QskLinearBox( Qt::Horizontal, this ); - auto monthText = new QskTextLabel( "Usage this month", month ); - monthText->setFontRole( QskSkin::SmallFont ); - - new SeparatorLabel( month ); - - auto monthValue = new QskTextLabel( "66 kwH", month ); - monthValue->setFontRole( QskSkin::SmallFont ); - - auto total = new QskLinearBox( Qt::Horizontal, this ); - auto totalText = new QskTextLabel( "Total working hours", total ); - totalText->setFontRole( QskSkin::SmallFont ); - - new SeparatorLabel( total ); - - auto totalValue = new QskTextLabel( "125 hrs", total ); - totalValue->setFontRole( QskSkin::SmallFont ); + ( void ) new UsageLine( "Usage today", "0,5 kwH", this ); + ( void ) new UsageLine( "Usage this month", "66 kwH", this ); + ( void ) new UsageLine( "Total working hours", "125 hrs", this ); } diff --git a/examples/iotdashboard/UsageDiagram.cpp b/examples/iotdashboard/UsageDiagram.cpp index 95f511cb..21523a58 100644 --- a/examples/iotdashboard/UsageDiagram.cpp +++ b/examples/iotdashboard/UsageDiagram.cpp @@ -7,7 +7,7 @@ #include "Diagram.h" #include -#include +#include #include QSK_SUBCONTROL( UsageDiagramLegend, Panel ) @@ -52,7 +52,7 @@ namespace } auto label = new QskTextLabel( text ); - label->setFontRole( QskSkin::TinyFont ); + label->setFontRole( { QskFontRole::Caption, QskFontRole::Low } ); addItem( symbol ); addItem( label ); diff --git a/examples/iotdashboard/ValueMeter.cpp b/examples/iotdashboard/ValueMeter.cpp new file mode 100644 index 00000000..43f34dd2 --- /dev/null +++ b/examples/iotdashboard/ValueMeter.cpp @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "ValueMeter.h" + +#include +#include + +ValueMeter::ValueMeter( QQuickItem* parent ) + : QskProgressRing( parent ) +{ + setAutoLayoutChildren( true ); + initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Constrained ); + + m_label = new QskTextLabel( this ); + m_label->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + m_label->setLayoutAlignmentHint( Qt::AlignCenter ); + m_label->setFontRole( QskFontRole::Caption ); + + connect( this, &QskProgressRing::valueChanged, + this, &ValueMeter::updateMeter ); + + updateMeter( value() ); +} + +void ValueMeter::updateMeter( const qreal value ) +{ + m_label->setText( text( value ) ); +} + +QString ValueMeter::text( qreal value ) const +{ + value = static_cast< int >( value ); + return locale().toString( value ) + ' ' + locale().percent(); +} + +void ValueMeter::setTextColor( const QColor& color ) +{ + m_label->setTextColor( color ); +} diff --git a/examples/iotdashboard/ValueMeter.h b/examples/iotdashboard/ValueMeter.h new file mode 100644 index 00000000..52d31dd3 --- /dev/null +++ b/examples/iotdashboard/ValueMeter.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class QskTextLabel; + +class ValueMeter : public QskProgressRing +{ + public: + ValueMeter( QQuickItem* parent = nullptr ); + + void setTextColor( const QColor& ); + + protected: + virtual QString text( qreal ) const; + + private: + void updateMeter( qreal value ); + QskTextLabel* m_label = nullptr; +}; diff --git a/examples/iotdashboard/images/icon2qvg.sh b/examples/iotdashboard/images/icon2qvg.sh new file mode 100755 index 00000000..c562ca17 --- /dev/null +++ b/examples/iotdashboard/images/icon2qvg.sh @@ -0,0 +1,15 @@ +#! /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/examples/iotdashboard/images/qvg/ac.qvg b/examples/iotdashboard/images/qvg/ac.qvg index 56ba5116..dfb13034 100644 Binary files a/examples/iotdashboard/images/qvg/ac.qvg and b/examples/iotdashboard/images/qvg/ac.qvg differ diff --git a/examples/iotdashboard/images/qvg/dashboard.qvg b/examples/iotdashboard/images/qvg/dashboard.qvg index 85ae5e05..f1d86e56 100644 Binary files a/examples/iotdashboard/images/qvg/dashboard.qvg and b/examples/iotdashboard/images/qvg/dashboard.qvg differ diff --git a/examples/iotdashboard/images/qvg/devices.qvg b/examples/iotdashboard/images/qvg/devices.qvg index aefe5935..c3d323d6 100644 Binary files a/examples/iotdashboard/images/qvg/devices.qvg and b/examples/iotdashboard/images/qvg/devices.qvg differ diff --git a/examples/iotdashboard/images/qvg/down.qvg b/examples/iotdashboard/images/qvg/down.qvg index 71e37f4e..e4cc9906 100644 Binary files a/examples/iotdashboard/images/qvg/down.qvg and b/examples/iotdashboard/images/qvg/down.qvg differ diff --git a/examples/iotdashboard/images/qvg/humidity.qvg b/examples/iotdashboard/images/qvg/humidity.qvg index f71f332c..c7f96afe 100644 Binary files a/examples/iotdashboard/images/qvg/humidity.qvg and b/examples/iotdashboard/images/qvg/humidity.qvg differ diff --git a/examples/iotdashboard/images/qvg/indoor-temperature.qvg b/examples/iotdashboard/images/qvg/indoor-temperature.qvg index 34d9e6d5..e90a7c38 100644 Binary files a/examples/iotdashboard/images/qvg/indoor-temperature.qvg and b/examples/iotdashboard/images/qvg/indoor-temperature.qvg differ diff --git a/examples/iotdashboard/images/qvg/lamps.qvg b/examples/iotdashboard/images/qvg/lamps.qvg index 42adabc6..4bf3df10 100644 Binary files a/examples/iotdashboard/images/qvg/lamps.qvg and b/examples/iotdashboard/images/qvg/lamps.qvg differ diff --git a/examples/iotdashboard/images/qvg/logout.qvg b/examples/iotdashboard/images/qvg/logout.qvg index 9213e3c3..ede8a8c1 100644 Binary files a/examples/iotdashboard/images/qvg/logout.qvg and b/examples/iotdashboard/images/qvg/logout.qvg differ diff --git a/examples/iotdashboard/images/qvg/main-icon.qvg b/examples/iotdashboard/images/qvg/main-icon.qvg index 067c743a..7fc15101 100644 Binary files a/examples/iotdashboard/images/qvg/main-icon.qvg and b/examples/iotdashboard/images/qvg/main-icon.qvg differ diff --git a/examples/iotdashboard/images/qvg/members.qvg b/examples/iotdashboard/images/qvg/members.qvg index b6170fdc..4102ee76 100644 Binary files a/examples/iotdashboard/images/qvg/members.qvg and b/examples/iotdashboard/images/qvg/members.qvg differ diff --git a/examples/iotdashboard/images/qvg/music-system.qvg b/examples/iotdashboard/images/qvg/music-system.qvg index 365c6090..600f2184 100644 Binary files a/examples/iotdashboard/images/qvg/music-system.qvg and b/examples/iotdashboard/images/qvg/music-system.qvg differ diff --git a/examples/iotdashboard/images/qvg/rooms.qvg b/examples/iotdashboard/images/qvg/rooms.qvg index d0bb7001..6671862f 100644 Binary files a/examples/iotdashboard/images/qvg/rooms.qvg and b/examples/iotdashboard/images/qvg/rooms.qvg differ diff --git a/examples/iotdashboard/images/qvg/router.qvg b/examples/iotdashboard/images/qvg/router.qvg index 5d413904..58db7763 100644 Binary files a/examples/iotdashboard/images/qvg/router.qvg and b/examples/iotdashboard/images/qvg/router.qvg differ diff --git a/examples/iotdashboard/images/qvg/statistics.qvg b/examples/iotdashboard/images/qvg/statistics.qvg index c43fb548..1adc83f3 100644 Binary files a/examples/iotdashboard/images/qvg/statistics.qvg and b/examples/iotdashboard/images/qvg/statistics.qvg differ diff --git a/examples/iotdashboard/images/qvg/storage.qvg b/examples/iotdashboard/images/qvg/storage.qvg index 1313ee76..4a1f4a9b 100644 Binary files a/examples/iotdashboard/images/qvg/storage.qvg and b/examples/iotdashboard/images/qvg/storage.qvg differ diff --git a/examples/iotdashboard/images/qvg/up.qvg b/examples/iotdashboard/images/qvg/up.qvg index 58c142f6..6c379e34 100644 Binary files a/examples/iotdashboard/images/qvg/up.qvg and b/examples/iotdashboard/images/qvg/up.qvg differ diff --git a/examples/iotdashboard/main.cpp b/examples/iotdashboard/main.cpp index bbd0ddaa..f0b2c681 100644 --- a/examples/iotdashboard/main.cpp +++ b/examples/iotdashboard/main.cpp @@ -28,7 +28,6 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); - qskSetup->setItemUpdateFlag( QskQuickItem::PreferRasterForTextures, true ); qskSkinManager->setSkin( new Skin() ); Qsk::addGraphicProvider( QString(), new GraphicProvider() ); diff --git a/examples/iotdashboard_smoketest/CMakeLists.txt b/examples/iotdashboard_smoketest/CMakeLists.txt index 8f8a0c5f..6bd8d28c 100644 --- a/examples/iotdashboard_smoketest/CMakeLists.txt +++ b/examples/iotdashboard_smoketest/CMakeLists.txt @@ -45,3 +45,13 @@ add_subdirectory(../iotdashboard ${CMAKE_CURRENT_BINARY_DIR}/../iotdashboard) get_target_property(iotdashboard_COMPILE_DEFINITIONS iotdashboard COMPILE_DEFINITIONS) list(FILTER iotdashboard_COMPILE_DEFINITIONS EXCLUDE REGEX [[^USE_SHORTCUTS=1$]]) set_property(TARGET iotdashboard PROPERTY COMPILE_DEFINITIONS ${iotdashboard_COMPILE_DEFINITIONS}) + +# generate qvg consuming dummy target +qsk_svg2qvg( + ../iotdashboard/images/ac.svg + ${CMAKE_CURRENT_BINARY_DIR}/ac.qvg) + +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/test_svg_qgv.cpp "int main(){}") +add_executable(test_svg_qgv + ${CMAKE_CURRENT_BINARY_DIR}/test_svg_qgv.cpp + ${CMAKE_CURRENT_BINARY_DIR}/ac.qvg) \ No newline at end of file diff --git a/examples/layouts/TestRectangle.cpp b/examples/layouts/TestRectangle.cpp index e1232206..955e0880 100644 --- a/examples/layouts/TestRectangle.cpp +++ b/examples/layouts/TestRectangle.cpp @@ -4,13 +4,13 @@ *****************************************************************************/ #include "TestRectangle.h" -#include +#include TestRectangle::TestRectangle( QQuickItem* parent ) : QskTextLabel( parent ) { setAlignment( Qt::AlignCenter ); - setFontRole( QskSkin::HugeFont ); + setFontRole( QskFontRole::Title ); setTextColor( Qt::white ); setPreferredSize( 10, 10 ); diff --git a/examples/mycontrols/main.cpp b/examples/mycontrols/main.cpp index 425c953c..ec9923c7 100644 --- a/examples/mycontrols/main.cpp +++ b/examples/mycontrols/main.cpp @@ -139,8 +139,6 @@ int main( int argc, char* argv[] ) qskSkinManager->registerFactory( QStringLiteral( "MySkinFactory" ), new MySkinFactory() ); - qskSetup->setItemUpdateFlag( QskQuickItem::PreferRasterForTextures, true ); - Window window; window.resize( 480, 360 ); window.show(); diff --git a/examples/qvgviewer/qvg/Tux.qvg b/examples/qvgviewer/qvg/Tux.qvg index f94effbf..f37fe99b 100644 Binary files a/examples/qvgviewer/qvg/Tux.qvg and b/examples/qvgviewer/qvg/Tux.qvg differ diff --git a/examples/tabview/CMakeLists.txt b/examples/tabview/CMakeLists.txt index 70e4c967..564835fa 100644 --- a/examples/tabview/CMakeLists.txt +++ b/examples/tabview/CMakeLists.txt @@ -3,9 +3,4 @@ # SPDX-License-Identifier: BSD-3-Clause ############################################################################ -qsk_add_example(tabview - CustomSlider.h CustomSlider.cpp - CustomSliderSkinlet.h CustomSliderSkinlet.cpp - OtherSlider.h OtherSlider.cpp - main.cpp -) +qsk_add_example(tabview main.cpp) diff --git a/examples/tabview/CustomSlider.cpp b/examples/tabview/CustomSlider.cpp deleted file mode 100644 index aedfec13..00000000 --- a/examples/tabview/CustomSlider.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "CustomSlider.h" -#include "CustomSliderSkinlet.h" - -#include -#include -#include -#include -#include - -#include -#include - -QSK_SUBCONTROL( CustomSlider, Scale ) -QSK_SUBCONTROL( CustomSlider, Decoration ) - -CustomSlider::CustomSlider( QQuickItem* parentItem ) - : QskSlider( parentItem ) -{ - using namespace QskRgb; - - QskSkinHintTableEditor ed( &hintTable() ); - - ed.setBoxShape( Fill, 0 ); - ed.setGradient( Fill, QskRgb::DimGray ); - ed.setColor( Scale, qRgb( 178, 178, 178 ) ); // for the ticks - - ed.setStrutSize( Handle, 80, 80 ); - ed.setColor( Handle, QskRgb::DimGray ); - - ed.setColor( Handle | Pressed, QskRgb::Orange ); - - const auto combinationMask = Focused | Hovered; - - ed.setColor( Handle, QskRgb::Orange, combinationMask ); - ed.setAnimation( Handle | QskAspect::Color, 300, combinationMask ); - - ed.setAnimation( Handle | QskAspect::Color, 1000 ); - - // using an individual skinlet, not known by the skin - - auto skinlet = new CustomSliderSkinlet(); - skinlet->setOwnedBySkinnable( true ); - - setSkinlet( skinlet ); - - connect( this, &QskSlider::valueChanged, - this, &QskControl::focusIndicatorRectChanged ); -} - -QRectF CustomSlider::focusIndicatorRect() const -{ - return subControlRect( Handle ); -} diff --git a/examples/tabview/CustomSlider.h b/examples/tabview/CustomSlider.h deleted file mode 100644 index 628a171e..00000000 --- a/examples/tabview/CustomSlider.h +++ /dev/null @@ -1,20 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class CustomSlider : public QskSlider -{ - using Inherited = QskSlider; - - public: - QSK_SUBCONTROLS( Scale, Decoration ) - - CustomSlider( QQuickItem* parent = nullptr ); - - QRectF focusIndicatorRect() const override; -}; diff --git a/examples/tabview/CustomSliderSkinlet.cpp b/examples/tabview/CustomSliderSkinlet.cpp deleted file mode 100644 index af511854..00000000 --- a/examples/tabview/CustomSliderSkinlet.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "CustomSliderSkinlet.h" -#include "CustomSlider.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#if 1 -// should be skin hints -static const qreal qskMinorTick = 20; -static const qreal qskMajorTick = 1.5 * qskMinorTick; -static const qreal qskMargin = 20; -static const qreal qskPeak = 10; - -static QFont qskLabelFont; - -#endif - -namespace -{ - class TicksNode : public QSGGeometryNode - { - public: - TicksNode( const QColor& color ) - : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) - { - m_geometry.setDrawingMode( QSGGeometry::DrawLines ); - m_geometry.setVertexDataPattern( QSGGeometry::StaticPattern ); - - m_material.setColor( color ); - - setGeometry( &m_geometry ); - setMaterial( &m_material ); - } - - private: - QSGFlatColorMaterial m_material; - QSGGeometry m_geometry; - }; - - class HandleNode : public QSGGeometryNode - { - public: - HandleNode() - : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 8 * 2 ) - { - setGeometry( &m_geometry ); - setMaterial( &m_material ); - } - - void update( const QRectF& rect, qreal peakPos, const QColor& color ) - { - if ( color != m_color ) - { - m_material.setColor( color ); - - m_color = color; - markDirty( QSGNode::DirtyMaterial ); - } - - if ( rect != m_rect || peakPos != m_peakPos ) - { - auto p = reinterpret_cast< QSGGeometry::Point2D* >( m_geometry.vertexData() ); - - const qreal y0 = rect.y() + qskPeak; - - setLine( p, peakPos, peakPos, rect.y() ); - setLine( p + 2, peakPos - 5, peakPos + 5, y0 ); - - // corners manually "rounded" by 3 pixels - - setLine( p + 4, rect.left() + 2, rect.right() - 2, y0 ); - setLine( p + 6, rect.left() + 1, rect.right() - 1, y0 + 1 ); - setLine( p + 8, rect.left(), rect.right(), y0 + 2 ); - setLine( p + 10, rect.left(), rect.right(), rect.bottom() - 1 ); - setLine( p + 12, rect.left() + 1, rect.right() - 1, rect.bottom() - 1 ); - setLine( p + 14, rect.left() + 2, rect.right() - 2, rect.bottom() ); - - m_rect = rect; - m_peakPos = peakPos; - markDirty( QSGNode::DirtyGeometry ); - } - } - - private: - inline void setLine( QSGGeometry::Point2D* points, float x1, float x2, qreal y ) - { - points[ 0 ].x = x1; - points[ 0 ].y = y; - - points[ 1 ].x = x2; - points[ 1 ].y = y; - } - - QRectF m_rect; - qreal m_peakPos; - QColor m_color; - - QSGFlatColorMaterial m_material; - QSGGeometry m_geometry; - }; -} - -CustomSliderSkinlet::CustomSliderSkinlet() -{ - qskLabelFont = QFont(); - setNodeRoles( { ScaleRole, FillRole, DecorationRole, HandleRole } ); -} - -QRectF CustomSliderSkinlet::subControlRect( const QskSkinnable* skinnable, - const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const -{ - const auto slider = static_cast< const QskSlider* >( skinnable ); - - if ( subControl == QskSlider::Groove ) - { - return QRectF(); // we don't have a groove - } - else if ( subControl == QskSlider::Fill ) - { - return fillRect( slider, contentsRect ); - } - else if ( subControl == QskSlider::Handle ) - { - return handleRect( slider, contentsRect ); - } - else if ( subControl == CustomSlider::Scale ) - { - return scaleRect( contentsRect ); - } - else if ( subControl == CustomSlider::Decoration ) - { - return decorationRect( slider, contentsRect ); - } - - return Inherited::subControlRect( skinnable, contentsRect, subControl ); -} - -QSGNode* CustomSliderSkinlet::updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const -{ - const auto slider = static_cast< const QskSlider* >( skinnable ); - - switch ( nodeRole ) - { - case ScaleRole: - return updateScaleNode( slider, node ); - - case DecorationRole: - return updateDecorationNode( slider, node ); - - case FillRole: - return Inherited::updateSubNode( skinnable, nodeRole, node ); - - case HandleRole: - return updateHandleNode( slider, node ); - - default: - return nullptr; - } -} - -QRectF CustomSliderSkinlet::scaleRect( const QRectF& contentsRect ) const -{ - auto r = contentsRect; - - r.setX( r.left() + qskMargin ); - r.setBottom( r.center().y() ); - r.setTop( r.bottom() - qskMajorTick ); - r.setWidth( r.width() - qskMargin ); - - return r; -} - -QRectF CustomSliderSkinlet::fillRect( - const QskSlider* slider, const QRectF& contentsRect ) const -{ - auto r = subControlRect( slider, contentsRect, CustomSlider::Scale ); - - r.setTop( r.bottom() - qskMinorTick ); - r.setWidth( r.width() * slider->valueAsRatio() ); - - return r; -} - -QRectF CustomSliderSkinlet::decorationRect( - const QskSlider* slider, const QRectF& contentsRect ) const -{ - // decoration exceeds scale !!!! - - auto r = subControlRect( slider, contentsRect, CustomSlider::Scale ); - r.setBottom( r.top() ); - r.setTop( r.bottom() - QFontMetricsF( qskLabelFont ).height() ); - - return r; -} - -QRectF CustomSliderSkinlet::handleRect( - const QskSlider* slider, const QRectF& contentsRect ) const -{ - const QRectF fillRect = subControlRect( slider, contentsRect, QskSlider::Fill ); - const QRectF scaleRect = subControlRect( slider, contentsRect, CustomSlider::Scale ); - - QRectF handleRect( 0, scaleRect.bottom(), 80, 50 ); - handleRect.moveCenter( QPointF( fillRect.right(), handleRect.center().y() ) ); - - if ( handleRect.left() < contentsRect.left() ) - { - handleRect.moveLeft( contentsRect.left() ); - } - else if ( handleRect.right() > contentsRect.right() ) - { - handleRect.moveRight( contentsRect.right() ); - } - - return handleRect; -} - -QSGNode* CustomSliderSkinlet::updateScaleNode( - const QskSlider* slider, QSGNode* node ) const -{ - const auto scaleRect = subControlRect( - slider, slider->contentsRect(), CustomSlider::Scale ); - - if ( scaleRect.isEmpty() ) - return nullptr; - - auto ticksNode = static_cast< TicksNode* >( node ); - if ( ticksNode == nullptr ) - ticksNode = new TicksNode( slider->color( CustomSlider::Scale ) ); - - const int tickCount = std::floor( slider->boundaryLength() / slider->stepSize() ) + 1; - - auto geometry = ticksNode->geometry(); - geometry->allocate( tickCount * 2 ); - - auto vertexData = geometry->vertexDataAsPoint2D(); - memset( vertexData, 0, geometry->vertexCount() ); - - auto stepStride = slider->stepSize() / slider->boundaryLength() * scaleRect.width(); - - auto x = scaleRect.x(); - const auto y = scaleRect.bottom(); - - // Create a series of tickmarks from minimum to maximum - for ( int i = 0; i < tickCount; ++i ) - { - vertexData[ 0 ].set( x, y ); - vertexData[ 1 ].set( x, y - ( ( i % 10 ) ? qskMinorTick : qskMajorTick ) ); - - vertexData += 2; - x += stepStride; - } - geometry->setLineWidth( 1 ); - geometry->markVertexDataDirty(); - - ticksNode->markDirty( QSGNode::DirtyGeometry ); - - return ticksNode; -} - -QSGNode* CustomSliderSkinlet::updateDecorationNode( - const QskSlider* slider, QSGNode* node ) const -{ - const QRectF decorationRect = subControlRect( - slider, slider->contentsRect(), CustomSlider::Decoration ); - - if ( decorationRect.isEmpty() ) - return nullptr; - - auto decorationNode = static_cast< QSGTransformNode* >( node ); - if ( decorationNode == nullptr ) - decorationNode = new QSGTransformNode(); - - const int tickCount = std::floor( slider->boundaryLength() / slider->stepSize() ) + 1; - - auto stepStride = slider->stepSize() / slider->boundaryLength() * decorationRect.width(); - - auto x = decorationRect.x(); - const auto y = decorationRect.y(); - - auto labelNode = decorationNode->firstChild(); - - const QFontMetrics fm( qskLabelFont ); - - for ( int i = 0; i < tickCount; i += 100 ) - { - const auto text = QString::number( slider->minimum() + slider->stepSize() * i, 'f', 0 ); - - const auto w = qskHorizontalAdvance( fm, text ); - const auto h = fm.height(); - - labelNode = QskSkinlet::updateTextNode( slider, labelNode, - QRectF( x - 0.5 * w, y, w, h ), Qt::AlignHCenter, text, qskLabelFont, - QskTextOptions(), QskTextColors( QskRgb::DimGray ), Qsk::Normal ); - - if ( labelNode ) - { - if ( labelNode->parent() != decorationNode ) - decorationNode->appendChildNode( labelNode ); - - labelNode = labelNode->nextSibling(); - } - - x += 100 * stepStride; - } - - QskSGNode::removeAllChildNodesAfter( decorationNode, labelNode ); - return decorationNode; -} - -QSGNode* CustomSliderSkinlet::updateHandleNode( - const QskSlider* slider, QSGNode* node ) const -{ - const auto cr = slider->contentsRect(); - - const auto handleRect = subControlRect( slider, cr, QskSlider::Handle ); - - if ( handleRect.isEmpty() ) - return nullptr; - - const auto fillRect = subControlRect( slider, cr, QskSlider::Fill ); - - auto handleNode = static_cast< HandleNode* >( node ); - if ( handleNode == nullptr ) - handleNode = new HandleNode(); - - const auto handleColor = slider->color( QskSlider::Handle ); - handleNode->update( handleRect, fillRect.right(), handleColor ); - - // finally the value label - - QFont font( QStringLiteral( "Roboto" ) ); - font.setPixelSize( 26 ); - - const qreal h = QFontMetrics( font ).height(); - - auto textRect = handleRect; - textRect.setTop( textRect.bottom() - 0.5 * ( textRect.height() - qskPeak + h ) ); - - const auto text = QString::number( slider->value(), 'f', 0 ); - - auto labelNode = QskSkinlet::updateTextNode( slider, handleNode->firstChild(), - textRect, Qt::AlignHCenter, text, font, QskTextOptions(), - QskTextColors( Qt::white ), Qsk::Normal ); - - if ( labelNode->parent() != handleNode ) - handleNode->appendChildNode( labelNode ); - - return handleNode; -} - -QSizeF CustomSliderSkinlet::sizeHint( const QskSkinnable* skinnable, - Qt::SizeHint which, const QSizeF& constraint ) const -{ - auto size = Inherited::sizeHint( skinnable, which, constraint ); - - if ( which == Qt::PreferredSize && size.height() >= 0 ) - size.rheight() += 60; - - return size; -} diff --git a/examples/tabview/CustomSliderSkinlet.h b/examples/tabview/CustomSliderSkinlet.h deleted file mode 100644 index deb23a6f..00000000 --- a/examples/tabview/CustomSliderSkinlet.h +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class QSGTransformNode; - -class CustomSliderSkinlet : public QskSliderSkinlet -{ - using Inherited = QskSliderSkinlet; - - public: - enum NodeRole - { - // we have a scale instead of a groove - ScaleRole = QskSliderSkinlet::HandleRole + 1, - DecorationRole - }; - - CustomSliderSkinlet(); - - QRectF subControlRect( const QskSkinnable*, - const QRectF&, QskAspect::Subcontrol ) const override; - - QSizeF sizeHint( const QskSkinnable*, - Qt::SizeHint, const QSizeF& ) const override; - - protected: - QSGNode* updateSubNode( const QskSkinnable*, - quint8 nodeRole, QSGNode* ) const override; - - private: - QSGNode* updateScaleNode( const QskSlider*, QSGNode* ) const; - QSGNode* updateDecorationNode( const QskSlider*, QSGNode* ) const; - QSGNode* updateHandleNode( const QskSlider*, QSGNode* ) const; - - QRectF scaleRect( const QRectF& ) const; - QRectF fillRect( const QskSlider*, const QRectF& ) const; - QRectF decorationRect( const QskSlider*, const QRectF& ) const; - QRectF handleRect( const QskSlider*, const QRectF& ) const; -}; diff --git a/examples/tabview/OtherSlider.cpp b/examples/tabview/OtherSlider.cpp deleted file mode 100644 index b8c7f157..00000000 --- a/examples/tabview/OtherSlider.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "OtherSlider.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -OtherSlider::OtherSlider( QQuickItem* parentItem ) - : QskSlider( parentItem ) -{ - using A = QskAspect; - using namespace QskRgb; - - const qreal h = 30; - const qreal w = 2.0 * h; - const qreal paddingW = 0.5 * w + 1; - - QskSkinHintTableEditor ed( &hintTable() ); - - // Panel - - for ( auto variation : { A::Horizontal, A::Vertical } ) - { - const auto aspect = Panel | variation; - - ed.setMetric( aspect | A::Size, h ); - ed.setBoxShape( aspect, 4 ); - ed.setBoxBorderMetrics( aspect, 1 ); - ed.setBoxBorderColors( aspect, DimGray ); - ed.setGradient( aspect, Silver ); - - if ( variation == A::Horizontal ) - ed.setPadding( aspect, QskMargins( paddingW, 0 ) ); - else - ed.setPadding( aspect, QskMargins( 0, paddingW ) ); - } - - // Groove - - for ( auto variation : { A::Horizontal, A::Vertical } ) - { - const auto aspect = Groove | variation; - - ed.setMetric( aspect | A::Size, 4 ); - ed.setBoxBorderMetrics( aspect, 0 ); - ed.setBoxShape( aspect, 1 ); - - ed.setGradient( aspect, Qt::black ); - } - - // no Fill - for ( auto variation : { A::Horizontal, A::Vertical } ) - { - const auto aspect = Fill | variation; - ed.setMetric( aspect | A::Size, 0 ); - } - - // Handle - - for ( auto variation : { A::Horizontal, A::Vertical } ) - { - const auto aspect = Handle | variation; - - ed.setBoxBorderMetrics( aspect, 1 ); - ed.setBoxShape( aspect, 4 ); - - const qreal m = 0.5 * std::ceil( 0.5 * ( w - h ) ) + 1; - - if ( variation == A::Horizontal ) - ed.setMargin( aspect, QskMargins( -m, 0 ) ); - else - ed.setMargin( aspect, QskMargins( 0, -m ) ); - - for ( auto state : { A::NoState, Pressed } ) - { - ed.setBoxBorderColors( aspect | state, SlateGrey ); - ed.setGradient( aspect | state, DodgerBlue ); - } - } -} diff --git a/examples/tabview/OtherSlider.h b/examples/tabview/OtherSlider.h deleted file mode 100644 index 0cb01910..00000000 --- a/examples/tabview/OtherSlider.h +++ /dev/null @@ -1,15 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class OtherSlider : public QskSlider -{ - public: - // Slider overriding many hints from the skin. - OtherSlider( QQuickItem* = nullptr ); -}; diff --git a/examples/tabview/main.cpp b/examples/tabview/main.cpp index 1311650d..29504d9f 100644 --- a/examples/tabview/main.cpp +++ b/examples/tabview/main.cpp @@ -3,13 +3,8 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#include "CustomSlider.h" -#include "OtherSlider.h" - #include -#include -#include #include #include #include @@ -19,6 +14,7 @@ #include #include #include +#include #include @@ -29,41 +25,11 @@ class Label : public QskTextLabel : QskTextLabel( text, parent ) { setTextColor( Qt::darkRed ); - setFontRole( QskSkin::LargeFont ); + setFontRole( QskFontRole::Headline ); setAlignment( Qt::AlignCenter ); } }; -class SliderBox : public QskLinearBox -{ - public: - SliderBox( QQuickItem* parent = nullptr ) - : QskLinearBox( Qt::Vertical, parent ) - { - setMargins( 30 ); - setSpacing( 50 ); - setExtraSpacingAt( Qt::BottomEdge ); - - { - auto slider = new OtherSlider( this ); - - slider->setMinimum( 0 ); - slider->setMaximum( 10 ); - slider->setStepSize( 1 ); - } - - { - auto slider = new CustomSlider( this ); - - slider->setSnap( true ); - slider->setMinimum( 0 ); - slider->setMaximum( 2000 ); - slider->setStepSize( 10 ); - slider->setPageSize( 10 ); - } - } -}; - class TabView : public QskTabView { public: @@ -77,16 +43,11 @@ class TabView : public QskTabView const auto text = QStringLiteral( "Another Tab" ); addTab( text, new Label( text ) ); } - else if ( i == 7 ) - { - addTab( "Sliders", new SliderBox() ); - } else { const auto text = QString( "Tab %1" ).arg( i + 1 ); addTab( text, new Label( text ) ); } - } setTabEnabled( 2, false ); @@ -167,8 +128,6 @@ int main( int argc, char* argv[] ) layoutBox->addItem( tabView ); auto focusIndicator = new QskFocusIndicator(); - focusIndicator->setObjectName( "FocusIndicator" ); - focusIndicator->setBoxBorderColorsHint( QskFocusIndicator::Panel, Qt::red ); QskWindow window; window.resize( 800, 600 ); diff --git a/examples/thumbnails/main.cpp b/examples/thumbnails/main.cpp index 98e0f78a..f19eca4d 100644 --- a/examples/thumbnails/main.cpp +++ b/examples/thumbnails/main.cpp @@ -138,7 +138,7 @@ class IconGrid : public QskLinearBox When having too many nodes, the scene graph becomes horribly slow. So we explicitely hide all items outside the visible area ( see updateVisibilities below ) and make use of the DeferredUpdate and - CleanupOnVisibility features of QskQuickItem. + CleanupOnVisibility features of QskItem. */ setSize( sizeConstraint() ); updateLayout(); // so that every item has its initial geometry diff --git a/playground/CMakeLists.txt b/playground/CMakeLists.txt index d22f0c1c..43483ec6 100644 --- a/playground/CMakeLists.txt +++ b/playground/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(anchors) add_subdirectory(dials) add_subdirectory(dialogbuttons) +add_subdirectory(fonts) add_subdirectory(gradients) add_subdirectory(invoker) add_subdirectory(shadows) diff --git a/playground/charts/CircularChartSkinlet.cpp b/playground/charts/CircularChartSkinlet.cpp index 736479a9..efdabda4 100644 --- a/playground/charts/CircularChartSkinlet.cpp +++ b/playground/charts/CircularChartSkinlet.cpp @@ -9,153 +9,10 @@ #include #include -#include +#include -#include #include -#define PAINTED_NODE 0 - -#if PAINTED_NODE - -/* - This is a fallback implementation for a user who is using an outdated - version of QSkinny where only shaders for linear gradients are available. - */ -#include -#include -#include -#include -#include - -namespace -{ - QConicalGradient qskQConicalGradient( - const QskGradientStops& stops, qreal startAngle, qreal spanAngle ) - { - QskGradientStops scaledStops; - scaledStops.reserve( stops.size() ); - - const auto ratio = qAbs( spanAngle ) / 360.0; - - if ( spanAngle > 0.0 ) - { - for ( auto it = stops.cbegin(); it != stops.cend(); ++it ) - scaledStops += { ratio * it->position(), it->color() }; - } - else - { - for ( auto it = stops.crbegin(); it != stops.crend(); ++it ) - scaledStops += { 1.0 - ratio * it->position(), it->color() }; - } - - QConicalGradient qGradient( QPointF(), startAngle ); - qGradient.setStops( qskToQGradientStops( scaledStops ) ); - - return qGradient; - } - - class PaintedArcNode : public QskPaintedNode - { - public: - void setArcData( const QRectF&, const QskArcMetrics&, - qreal, const QColor&, const QskGradient&, QQuickWindow* ); - - protected: - void paint( QPainter*, const QSize&, const void* nodeData ) override; - QskHashValue hash( const void* nodeData ) const override; - - private: - QskHashValue arcHash( const QRectF&, const QskArcMetrics&, - qreal, const QColor&, const QskGradient& ) const; - - QBrush fillBrush( const QskGradient&, const QRectF&, qreal, qreal ) const; - - struct ArcData - { - QPointF translation; - QPen pen; - QBrush brush; - QPainterPath path; - - QskHashValue hash; - }; - }; - - void PaintedArcNode::setArcData( - const QRectF& rect, const QskArcMetrics& metrics, - qreal borderWidth, const QColor& borderColor, - const QskGradient& gradient, QQuickWindow* window ) - { - const auto hash = arcHash( rect, metrics, borderWidth, borderColor, gradient ); - - const auto brush = fillBrush( gradient, rect, - metrics.startAngle(), metrics.spanAngle() ); - - QPen pen( borderColor, borderWidth ); - if ( borderWidth <= 0.0 ) - pen.setStyle( Qt::NoPen ); - - const auto path = metrics.painterPath( rect ); - const auto r = path.controlPointRect(); - - const ArcData arcData { r.topLeft(), pen, brush, path, hash }; - update( window, r, QSizeF(), &arcData ); - } - - void PaintedArcNode::paint( QPainter* painter, const QSize&, const void* nodeData ) - { - const auto arcData = reinterpret_cast< const ArcData* >( nodeData ); - - painter->setRenderHint( QPainter::Antialiasing, true ); - painter->translate( -arcData->translation ); - painter->setPen( arcData->pen ); - painter->setBrush( arcData->brush ); - painter->drawPath( arcData->path ); - } - - QskHashValue PaintedArcNode::hash( const void* nodeData ) const - { - const auto arcData = reinterpret_cast< const ArcData* >( nodeData ); - return arcData->hash; - } - - QBrush PaintedArcNode::fillBrush( const QskGradient& gradient, - const QRectF& rect, qreal startAngle, qreal spanAngle ) const - { - const auto qGradient = qskQConicalGradient( - gradient.stops(), startAngle, spanAngle ); - - const qreal sz = qMax( rect.width(), rect.height() ); - const qreal sx = rect.width() / sz; - const qreal sy = rect.height() / sz; - - QTransform t; - t.scale( sx, sy ); - t.translate( rect.center().x() / sx, rect.center().y() / sy ); - - QBrush brush( qGradient ); - brush.setTransform( t ); - - return brush; - } - - inline QskHashValue PaintedArcNode::arcHash( - const QRectF& rect, const QskArcMetrics& metrics, qreal borderWidth, - const QColor& borderColor, const QskGradient& gradient ) const - { - auto hash = metrics.hash( 6753 ); - hash = qHashBits( &rect, sizeof( rect ), hash ); - hash = qHash( borderWidth, hash ); - hash = qHash( borderColor.rgba(), hash ); - hash = gradient.hash( hash ); - - return hash; - } -} - -#endif // PAINTED_NODE - namespace { inline QskArcMetrics segmentMetrics( @@ -334,39 +191,16 @@ QSGNode* CircularChartSkinlet::updateSampleNode( const QskSkinnable* skinnable, return nullptr; } -QSGNode* CircularChartSkinlet::updateArcSegmentNode( - const QskSkinnable* skinnable, +QSGNode* CircularChartSkinlet::updateArcSegmentNode( const QskSkinnable*, QSGNode* node, qreal borderWidth, const QColor& borderColor, const QskGradient& gradient, const QskArcMetrics& metrics ) const { -#if PAINTED_NODE - auto arcNode = static_cast< PaintedArcNode* >( node ); + auto arcNode = static_cast< QskArcRenderNode* >( node ); if ( arcNode == nullptr ) - arcNode = new PaintedArcNode(); + arcNode = new QskArcRenderNode(); - const auto chart = static_cast< const CircularChart* >( skinnable ); - - arcNode->setArcData( m_data->closedArcRect, metrics, - borderWidth, borderColor, gradient, chart->window() ); -#else - Q_UNUSED( skinnable ) - - auto fillGradient = gradient; - - if ( fillGradient.type() == QskGradient::Stops ) - { - fillGradient.setStretchMode( QskGradient::StretchToSize ); - fillGradient.setConicDirection( 0.5, 0.5, - metrics.startAngle(), metrics.spanAngle() ); - } - - auto arcNode = static_cast< QskArcNode* >( node ); - if ( arcNode == nullptr ) - arcNode = new QskArcNode(); - - arcNode->setArcData( m_data->closedArcRect, metrics, - borderWidth, borderColor, fillGradient ); -#endif + arcNode->updateNode( m_data->closedArcRect, metrics, true, + borderWidth, borderColor, gradient ); return arcNode; } diff --git a/playground/dials/Skin.cpp b/playground/dials/Skin.cpp index 7a0e79f9..760c927e 100644 --- a/playground/dials/Skin.cpp +++ b/playground/dials/Skin.cpp @@ -12,6 +12,7 @@ #include #include #include +#include static inline QFont qskFont( qreal pointSize ) { @@ -30,8 +31,9 @@ void Skin::initHints() { using namespace QskRgb; - setFont( QskSkin::DefaultFont, qskFont( 13 ) ); - setFont( QskSkin::LargeFont, qskFont( 20 ) ); + setFont( QskFontRole::Caption, qskFont( 8 ) ); + setFont( QskFontRole::Body, qskFont( 13 ) ); + setFont( QskFontRole::Title, qskFont( 20 ) ); const auto rgb1 = qRgb( 1, 16, 27 ); // Maastricht blue const auto rgb2 = qRgb( 255, 0, 22 ); // Ruddy @@ -65,6 +67,6 @@ void Skin::initHints() ed.setSpacing( Q::TickLabels, 4 ); ed.setStrutSize( Q::TickLabels, 2, 15 ); ed.setColor( Q::TickLabels, rgb4 ); - ed.setFontRole( Q::TickLabels, QskSkin::SmallFont ); + ed.setFontRole( Q::TickLabels, QskFontRole::Caption ); } } diff --git a/playground/fonts/CMakeLists.txt b/playground/fonts/CMakeLists.txt new file mode 100644 index 00000000..edf62848 --- /dev/null +++ b/playground/fonts/CMakeLists.txt @@ -0,0 +1,6 @@ +############################################################################ +# QSkinny - Copyright (C) The authors +# SPDX-License-Identifier: BSD-3-Clause +############################################################################ + +qsk_add_example(fonts main.cpp) diff --git a/playground/fonts/main.cpp b/playground/fonts/main.cpp new file mode 100644 index 00000000..03fb9751 --- /dev/null +++ b/playground/fonts/main.cpp @@ -0,0 +1,134 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + class TextLabel : public QskTextLabel + { + public: + TextLabel( const QskFontRole& role, QQuickItem* parent = nullptr ) + : QskTextLabel( parent ) + { + setAlignment( Qt::AlignCenter ); + setText( textFromRole( role ) ); + setFontRole( role ); + + setSizePolicy( Qt::Horizontal, QskSizePolicy::Ignored ); + } + + private: + QString textFromRole( const QskFontRole& role ) const + { + static QMetaEnum metaEnums[2]; + + const auto& mo = QskFontRole::staticMetaObject; + + if ( !metaEnums[0].isValid() ) + metaEnums[0] = mo.enumerator( mo.indexOfEnumerator( "Category" ) ); + + if ( !metaEnums[1].isValid() ) + metaEnums[1] = mo.enumerator( mo.indexOfEnumerator( "Emphasis" ) ); + + QString s( metaEnums[0].valueToKey( role.category() ) ); +#if 0 + s += ' '; + s += metaEnums[1].valueToKey( role.emphasis() ); +#endif + + return s; + } + }; + + class TextBox : public QskLinearBox + { + public: + TextBox( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, 3, parent ) + { + setMargins( 10 ); + setDefaultAlignment( Qt::AlignCenter ); + + for ( int i = 0; i <= QskFontRole::Display; i++ ) + { + const auto category = static_cast< QskFontRole::Category >( i ); + for ( int j = 0; j <= QskFontRole::VeryHigh; j++ ) + { + const auto emphasis = static_cast< QskFontRole::Emphasis >( j ); + ( void ) new TextLabel( { category, emphasis }, this ); + } + } + } + }; + + class SkinLabel : public QskTextLabel + { + public: + SkinLabel( QQuickItem* parent = nullptr ) + : QskTextLabel( parent ) + { + setSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed ); + setAlignment( Qt::AlignCenter ); + setFontRole( QskFontRole::Title ); + + updateText(); + } + + protected: + void changeEvent( QEvent* event ) + { + if ( event->type() == QEvent::StyleChange ) + updateText(); + + QskTextLabel::changeEvent( event ); + } + + private: + void updateText() + { + setText( qskSkinManager->skinName() ); + } + }; + + class MainView : public QskLinearBox + { + public: + MainView( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Vertical, parent ) + { + setPanel( true ); + + (void) new SkinLabel( this ); + (void) new QskSeparator( this ); + (void) new TextBox( this ); + } + }; +} + +int main( int argc, char** argv ) +{ + QGuiApplication app( argc, argv ); + + Skinny::init(); // we need a skin + SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); + + QskWindow window; + window.addItem( new MainView() ); + window.resize( 1024, 600 ); + window.show(); + + return app.exec(); +} diff --git a/playground/plots/PlotSkin.cpp b/playground/plots/PlotSkin.cpp index 4ff8b1d2..e79dcb3f 100644 --- a/playground/plots/PlotSkin.cpp +++ b/playground/plots/PlotSkin.cpp @@ -30,6 +30,7 @@ #include #include +#include #include @@ -98,7 +99,7 @@ void SkinEditor::setupPlotHints() setPadding( Q::AxisScale | A::Bottom, 0, padding, 0, 0 ); setColor( Q::AxisScale, qRgb( 20, 20, 20 ) ); - setFontRole( Q::AxisScale, QskSkin::MediumFont ); + setFontRole( Q::AxisScale, QskFontRole::Caption ); setFlag( Q::AxisScale | A::Style, QskGraduationRenderer::Backbone ); // thickness/length of the major ticks diff --git a/playground/plots/QskPlotViewSkinlet.cpp b/playground/plots/QskPlotViewSkinlet.cpp index f3f1aaf4..08eb396b 100644 --- a/playground/plots/QskPlotViewSkinlet.cpp +++ b/playground/plots/QskPlotViewSkinlet.cpp @@ -13,11 +13,7 @@ #include #include #include -#include -#include - -#include -#include +#include static inline QskTextColors qskTextColors( const QskSkinnable* skinnable, QskAspect aspect ) @@ -84,9 +80,7 @@ namespace setTickMetrics( graduation.value< QskGraduationMetrics >() ); setSpacing( view->spacingHint( aspect ) ); - - const auto fontRole = view->fontRoleHint( aspect ); - setFont( view->effectiveSkin()->font( fontRole ) ); + setFont( view->effectiveFont( aspect ) ); setTextColors( qskTextColors( view, aspect ) ); } diff --git a/playground/shadows/ArcPage.cpp b/playground/shadows/ArcPage.cpp index 3ed5c5e4..b4d020e1 100644 --- a/playground/shadows/ArcPage.cpp +++ b/playground/shadows/ArcPage.cpp @@ -73,32 +73,36 @@ namespace } } }; + + class Arc : public ShadowedArc + { + public: + Arc() + { + setStartAngle( 45.0 ); + setSpanAngle( 270.0 ); + setThickness( 10.0 ); + + setFillGradient( Qt::darkRed ); + + setBorderWidth( 0 ); + setBorderColor( Qt::darkYellow ); + + setShadowColor( Qt::black ); + setSpreadRadius( 0.0 ); + setBlurRadius( 4.0 ); + setOffsetX( 2.0 ); + setOffsetY( 2.0 ); + } + }; } ArcPage::ArcPage( QQuickItem* parent ) : QskLinearBox( Qt::Vertical, parent ) { - auto arc = new ShadowedArc(); + auto arc = new Arc(); arc->setMargins( 40 ); // some extra space for testing the offsets - { - // initial settings - arc->setStartAngle( 45.0 ); - arc->setSpanAngle( 270.0 ); - arc->setThickness( 10.0 ); - - arc->setFillColor( Qt::darkRed ); - - arc->setBorderWidth( 0 ); - arc->setBorderColor( Qt::darkYellow ); - - arc->setShadowColor( Qt::black ); - arc->setSpreadRadius( 0.0 ); - arc->setBlurRadius( 4.0 ); - arc->setOffsetX( 2.0 ); - arc->setOffsetY( 2.0 ); - } - auto panel = new ControlPanel( arc ); panel->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); diff --git a/playground/shadows/ShadowedArc.cpp b/playground/shadows/ShadowedArc.cpp index bb209f3f..6f7ff077 100644 --- a/playground/shadows/ShadowedArc.cpp +++ b/playground/shadows/ShadowedArc.cpp @@ -101,7 +101,7 @@ ShadowedArc::ShadowedArc( QQuickItem* parent ) setArcMetrics( { 0.0, 360.0, 1.0, Qt::RelativeSize } ); - setFillColor( Qt::darkRed ); + setFillGradient( Qt::darkRed ); setBorderWidth( 0 ); setBorderColor( Qt::gray ); @@ -216,14 +216,14 @@ qreal ShadowedArc::blurRadius() const return shadowMetrics().blurRadius(); } -void ShadowedArc::setFillColor( const QColor& color ) +void ShadowedArc::setFillGradient( const QskGradient& gradient ) { - setColor( Arc, color ); + setGradientHint( Arc, gradient ); } -QColor ShadowedArc::fillColor() const +QskGradient ShadowedArc::fillGradient() const { - return color( Arc ); + return gradientHint( Arc ); } void ShadowedArc::setShadowColor( const QColor& color ) diff --git a/playground/shadows/ShadowedArc.h b/playground/shadows/ShadowedArc.h index c8f486d3..2f683e26 100644 --- a/playground/shadows/ShadowedArc.h +++ b/playground/shadows/ShadowedArc.h @@ -9,6 +9,7 @@ class QskShadowMetrics; class QskArcMetrics; +class QskGradient; class ShadowedArc : public QskControl { @@ -35,7 +36,7 @@ class ShadowedArc : public QskControl qreal blurRadius() const; QColor borderColor() const; - QColor fillColor() const; + QskGradient fillGradient() const; QColor shadowColor() const; public Q_SLOTS: @@ -52,7 +53,7 @@ class ShadowedArc : public QskControl void setBlurRadius( qreal ); void setBorderColor( const QColor& ); - void setFillColor( const QColor& ); + void setFillGradient( const QskGradient& ); void setShadowColor( const QColor& ); private: diff --git a/qmlexport/CMakeLists.txt b/qmlexport/CMakeLists.txt index a4660b24..e6d51add 100644 --- a/qmlexport/CMakeLists.txt +++ b/qmlexport/CMakeLists.txt @@ -3,16 +3,25 @@ # SPDX-License-Identifier: BSD-3-Clause ############################################################################ -set(HEADERS +list(APPEND HEADERS QskQmlGlobal.h QskShortcutQml.h QskLayoutQml.h - QskQml.h) + QskQmlModule.h + QskQmlRegister.h + QskQml.h +) -set(SOURCES +list(APPEND SOURCES QskShortcutQml.cpp QskLayoutQml.cpp - QskQml.cpp) + QskQml.cpp +) + +if (QT_VERSION_MAJOR GREATER_EQUAL 6) + list(APPEND HEADERS QskQmlClassInfo.h) + list(APPEND SOURCES QskQmlClassInfo.cpp) +endif() set(target qskqmlexport) diff --git a/qmlexport/QskQml.cpp b/qmlexport/QskQml.cpp index e5581aad..e1a251db 100644 --- a/qmlexport/QskQml.cpp +++ b/qmlexport/QskQml.cpp @@ -4,8 +4,8 @@ *****************************************************************************/ #include "QskQml.h" -#include "QskQml.hpp" +#include "QskQmlRegister.h" #include "QskLayoutQml.h" #include "QskShortcutQml.h" @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -41,11 +42,8 @@ #include #include #include -#include #include #include -#include -#include #include #include #include @@ -61,9 +59,9 @@ #include #if QT_VERSION < QT_VERSION_CHECK( 6, 2, 0 ) - QSK_QT_PRIVATE_BEGIN +QSK_QT_PRIVATE_BEGIN #include - QSK_QT_PRIVATE_END +QSK_QT_PRIVATE_END #endif #if QT_VERSION < QT_VERSION_CHECK( 6, 5, 0 ) @@ -191,10 +189,6 @@ namespace void QskQml::registerTypes() { - qmlRegisterUncreatableType< QskSetup >( QSK_MODULE_NAME, 1, 0, "Setup", QString() ); - qmlRegisterUncreatableType< QskSkin >( QSK_MODULE_NAME, 1, 0, "Skin", QString() ); - qRegisterMetaType< QskSkin* >(); - registerObject< QskShortcutQml >( "Shortcut" ); registerObject< QskWindow >(); @@ -243,8 +237,8 @@ void QskQml::registerTypes() QSK_VERSION_MAJOR, QSK_VERSION_MINOR, "Skin", QString() ); #endif - QSK_REGISTER_FLAGS( QskQuickItem::UpdateFlag ); - QSK_REGISTER_FLAGS( QskQuickItem::UpdateFlags ); + QSK_REGISTER_FLAGS( QskItem::UpdateFlag ); + QSK_REGISTER_FLAGS( QskItem::UpdateFlags ); QSK_REGISTER_FLAGS( QskSizePolicy::Policy ); QSK_REGISTER_FLAGS( QskDialog::Actions ); @@ -269,6 +263,7 @@ void QskQml::registerTypes() registerGadget< QskPlacementPolicy >(); registerGadget< QskSizePolicy >(); registerGadget< QskTextOptions >(); + registerGadget< QskFontRole >(); registerNamespace( QskStandardSymbol::staticMetaObject ); diff --git a/qmlexport/QskQml.hpp b/qmlexport/QskQml.hpp deleted file mode 100644 index dff4db9a..00000000 --- a/qmlexport/QskQml.hpp +++ /dev/null @@ -1,276 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#ifndef QSK_QML_HPP -#define QSK_QML_HPP - -#include -#include - -#define QSK_MODULE_NAME "Skinny" -#define QSK_VERSION_MAJOR 1 -#define QSK_VERSION_MINOR 0 - -#if QT_VERSION < QT_VERSION_CHECK( 6, 3, 0 ) - #define QSK_STRUCT_VERSION 0 -#elif QT_VERSION < QT_VERSION_CHECK( 6, 5, 0 ) - #define QSK_STRUCT_VERSION 1 -#else - #define QSK_STRUCT_VERSION 2 -#endif - -// Required for QFlags to be constructed from an enum value -#define QSK_REGISTER_FLAGS( Type ) \ - QMetaType::registerConverter< int, Type >( []( int value ) { return Type( value ); } ) - -namespace QskQml -{ - inline const char* classNameQml( const QMetaObject& metaObject ) - { - // without the "Qsk" prefix - return metaObject.className() + 3; - } - - /* - ClassInfo corresponds to the most reecent QQmlPrivate::RegisterType - ( structVersion: 2 introduced with Qt 6.5 ) - */ - class ClassInfo - { - public: - - template< typename T > - void setTypeInfo() - { - using namespace QQmlPrivate; - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - const char* className = T::staticMetaObject.className(); \ - - const int nameLen = int(strlen(className) ); \ - const int listLen = int(strlen("QQmlListProperty<") ); \ - - QVarLengthArray< char,64 > listName(listLen + nameLen + 2); \ - memcpy(listName.data(), "QQmlListProperty<", size_t(listLen) ); \ - memcpy(listName.data() + listLen, className, size_t(nameLen) ); \ - listName[listLen + nameLen] = '>'; \ - listName[listLen + nameLen + 1] = '\0'; - - typeId = qMetaTypeId< T* >( ); - listId = qRegisterNormalizedMetaType< QQmlListProperty< T > >( listName.constData() ); -#else - if constexpr (std::is_base_of_v< QObject, T >) - { - typeId = QMetaType::fromType< T* >( ); - listId = QMetaType::fromType< QQmlListProperty< T > >( ); - } - else - { - typeId = QMetaType::fromType< T >( ); - listId = QMetaType::fromType< QList< T > >( ); - } - - createValueType = ValueType< T, void >::create; -#endif - - - parserStatusCast = StaticCastSelector< T,QQmlParserStatus >::cast(); - valueSourceCast = StaticCastSelector< T,QQmlPropertyValueSource >::cast(); - valueInterceptorCast = StaticCastSelector< T,QQmlPropertyValueInterceptor >::cast(); -#if QSK_STRUCT_VERSION >= 1 - finalizerCast = StaticCastSelector< T,QQmlFinalizerHook >::cast(); -#endif - } - - public: - const int structVersion = QSK_STRUCT_VERSION; - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - QMetaType typeId; - QMetaType listId; -#else - int typeId = 0; - int listId = 0; -#endif - - int objectSize = 0; - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - void ( *create )( void* ) = nullptr; -#else - void ( *create )( void*, void* ) = nullptr; - void* const userdata = nullptr; // unused -#endif - - const QString noCreationReason; // unused - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - /* - This one was introdued with Qt 6.x, but never worked - as expected. With Qt 6.5 it has been replaced by adding - the creationMethod that is triggering to look for - invokable constructors. - Let's check if it makes any sense to initialize it below - at all. TODO ... - */ - QVariant ( *createValueType )( const QJSValue& ) = nullptr; -#endif - - const char* const uri = QSK_MODULE_NAME; - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - const QTypeRevision version = - QTypeRevision::fromVersion( QSK_VERSION_MAJOR, QSK_VERSION_MINOR ); -#else - const int versionMajor = QSK_VERSION_MAJOR; - const int versionMinor = QSK_VERSION_MINOR; -#endif - const char* elementName = nullptr; - const QMetaObject* metaObject = nullptr; - - /* - We do not use attached properties as it always comes with - creating extra QObjects. - */ - QObject* (* const attachedPropertiesFunction)( QObject* ) = nullptr; - const QMetaObject* const attachedPropertiesMetaObject = nullptr; - - int parserStatusCast = -1; - int valueSourceCast = -1; - int valueInterceptorCast = -1; - - /* - We do not use extensions as it always comes with - creating extra QObjects. - */ - QObject* (* const extensionObjectCreate )( QObject* ) = nullptr; - const QMetaObject* const extensionMetaObject = nullptr; - - void* const customParser = nullptr; // QQmlCustomParser, unused - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - const QTypeRevision revision = QTypeRevision::zero(); -#else - const int revision = 0; -#endif - int finalizerCast = -1; - - const int creationMethod = 2; // ValueTypeCreationMethod::Structured - }; - - template< typename T > - inline int registerType( const char* qmlName ) - { - using namespace QQmlPrivate; - - ClassInfo type; - - type.setTypeInfo< T >(); - - type.objectSize = sizeof( T ); -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - type.create = Constructors< T >::createInto; -#else - type.create = createInto< T >; -#endif - - type.elementName = qmlName; - type.metaObject = &T::staticMetaObject; - - return qmlregister( TypeRegistration, &type ); - } - - template< typename T > - inline int registerUncreatableType( const char* qmlName ) - { - using namespace QQmlPrivate; - - ClassInfo type; - - type.setTypeInfo< T >(); - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - type.objectSize = sizeof( T ); - type.create = Constructors< T >::createInto; -#endif - - type.elementName = qmlName; - type.metaObject = &T::staticMetaObject; - - return qmlregister( TypeRegistration, &type ); - } - - inline int registerUncreatableMetaObject( - const QMetaObject& staticMetaObject, const char* qmlName ) - { - using namespace QQmlPrivate; - - ClassInfo type; - - type.elementName = qmlName; - type.metaObject = &staticMetaObject; - - return qmlregister( TypeRegistration, &type ); - } - - template< typename T > - inline void registerObject( const char* qmlName = nullptr ) - { - // the class name without the "Qsk" prefix - if ( qmlName == nullptr ) - qmlName = classNameQml( T::staticMetaObject ); - - ( void ) registerType< T >( qmlName ); - } - - template< typename T > - inline void registerGadget() - { - auto className = classNameQml( T::staticMetaObject ); - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - registerUncreatableType< T >( className ); -#else - /* - According to the QML naming rules uncreatables have to - start with a lowercase letter ( since Qt6 ), while namespaces - and creatable items usually start with a upper letter. - This results in an odd naming scheme for the enums defined inside of gadgets. - - To work around this we register the gadget twice - starting with - upper or lower letter. - - Maybe it would make sense to only pass stripped metaObjects, where all - enums are removed from the first and everything else than the enums from - the second. TODO ... - */ - - if ( T::staticMetaObject.enumeratorCount() > 0 ) - { - registerUncreatableMetaObject( T::staticMetaObject, className ); - } - - QByteArray name = className; - name.data()[0] = std::tolower( name.data()[0] ); - registerUncreatableType< T >( name.constData() ); -#endif - } - - inline int registerNamespace( const QMetaObject& metaObject ) - { - return registerUncreatableMetaObject( metaObject, classNameQml( metaObject ) ); - } - - template< typename T > - inline int registerSingleton( QObject* singleton ) - { - const auto name = classNameQml( T::staticMetaObject ); - - return qmlRegisterSingletonInstance( QSK_MODULE_NAME, - QSK_VERSION_MAJOR, QSK_VERSION_MINOR, name, singleton ); - } -} - -#endif diff --git a/qmlexport/QskQmlClassInfo.cpp b/qmlexport/QskQmlClassInfo.cpp new file mode 100644 index 00000000..a51e4203 --- /dev/null +++ b/qmlexport/QskQmlClassInfo.cpp @@ -0,0 +1,86 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskQmlClassInfo.h" +#include "QskQmlModule.h" +#include + +using namespace QskQml; + +ClassInfo::ClassInfo( const char* qmlName, const QMetaObject* metaObject ) +{ + m_info.structVersion = QSK_STRUCT_VERSION; + + m_info.objectSize = 0; + m_info.create = nullptr; + m_info.userdata = nullptr; // unused + + /* + This one was introdued with Qt 6.x, but never worked + as expected. With Qt 6.5 it has been replaced by adding + the creationMethod that is triggering to look for + invokable constructors. + Let's check if it makes any sense to initialize it below + at all. TODO ... + */ + m_info.createValueType = nullptr; + + m_info.uri = QskQmlModule::name; + m_info.version = QTypeRevision::fromVersion( + QskQmlModule::version[0], QskQmlModule::version[1] ); + + m_info.elementName = qmlName; + m_info.metaObject = metaObject; + + /* + We do not use attached properties as it always comes with + creating extra QObjects. + */ + m_info.attachedPropertiesFunction = nullptr; + m_info.attachedPropertiesMetaObject = nullptr; + + m_info.parserStatusCast = m_info.valueSourceCast = m_info.valueInterceptorCast = -1; + + /* + We do not use extensions as it always comes with + creating extra QObjects. + */ + m_info.extensionObjectCreate = nullptr; + m_info.extensionMetaObject = nullptr; + + m_info.customParser = nullptr; // QQmlCustomParser, unused + + m_info.revision = QTypeRevision::zero(); + m_info.finalizerCast = -1; + + m_info.creationMethod = QQmlPrivate::ValueTypeCreationMethod::Structured; +} + +QByteArray ClassInfo::normalizedListName( + const char* containerName, const QMetaObject& metaObject ) +{ + static QByteArray name; + name.reserve( 256 ); + + const int length1 = strlen( containerName ); + const int length2 = strlen( metaObject.className() ); + + name.resize( length1 + length2 + 3 ); + + auto p = name.data(); + + memcpy( p, containerName, size_t( length1 ) ); + p += length1; + + *p++ = '<'; + + memcpy( p, metaObject.className(), size_t( length2 ) ); + p += length2; + + *p++ = '>'; + *p++ = '\0'; + + return name; +} diff --git a/qmlexport/QskQmlClassInfo.h b/qmlexport/QskQmlClassInfo.h new file mode 100644 index 00000000..e3698c6d --- /dev/null +++ b/qmlexport/QskQmlClassInfo.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_QML_CLASSINFO_H +#define QSK_QML_CLASSINFO_H + +#include + +class QByteArray; + +#if QT_VERSION < QT_VERSION_CHECK( 6, 3, 0 ) + #define QSK_STRUCT_VERSION 0 +#elif QT_VERSION < QT_VERSION_CHECK( 6, 5, 0 ) + #define QSK_STRUCT_VERSION 1 +#else + #define QSK_STRUCT_VERSION 2 +#endif + +namespace QskQml +{ + class ClassInfo + { + public: + ClassInfo( const char* qmlName, const QMetaObject* ); + template< typename T > void setTypeInfo(); + + int registerType(); + + private: + static QByteArray normalizedListName( + const char* containerName, const QMetaObject& ); + + QQmlPrivate::RegisterType m_info; + }; + + template< typename T > + inline void ClassInfo::setTypeInfo() + { + using namespace QQmlPrivate; + + constexpr bool isObject = std::is_base_of_v< QObject, T >; + + if ( isObject ) + m_info.typeId = QMetaType::fromType< T* >(); + else + m_info.typeId = QMetaType::fromType< T >(); + + m_info.objectSize = sizeof( T ); + m_info.create = Constructors< T >::createInto; + + m_info.createValueType = ValueType< T, void >::create; + + const auto name = normalizedListName( + isObject ? "QQmlListProperty" : "QList", T::staticMetaObject ); + + /* + QMetaType::fromType< QList< T >() creates a lot of symbols + that end up in QskQml.o for all gadgets. So we export only + registered lists. Registration might be done in the qskinny library + itself - or in QskQml.cpp. + + As we do not design plain data being a QObject I'm not sure + if we need to have QQmlListProperty< T > at all ... + */ + m_info.listId = QMetaType::fromName( name.constData() ); + + m_info.parserStatusCast = StaticCastSelector< T, QQmlParserStatus >::cast(); + m_info.valueSourceCast = StaticCastSelector< T, QQmlPropertyValueSource >::cast(); + m_info.valueInterceptorCast = StaticCastSelector< T, QQmlPropertyValueInterceptor >::cast(); + +#if QSK_STRUCT_VERSION >= 1 + m_info.finalizerCast = StaticCastSelector< T, QQmlFinalizerHook >::cast(); +#endif + } + + inline int ClassInfo::registerType() + { + return qmlregister( QQmlPrivate::TypeRegistration, &m_info ); + } +} + +#endif diff --git a/examples/gallery/label/LabelPage.h b/qmlexport/QskQmlModule.h similarity index 59% rename from examples/gallery/label/LabelPage.h rename to qmlexport/QskQmlModule.h index b25cc549..e036ef8a 100644 --- a/examples/gallery/label/LabelPage.h +++ b/qmlexport/QskQmlModule.h @@ -3,12 +3,15 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#pragma once +#ifndef QSK_QML_MODULE_H +#define QSK_QML_MODULE_H -#include "Page.h" - -class LabelPage : public Page +namespace QskQmlModule { - public: - LabelPage( QQuickItem* = nullptr ); -}; + const char name[] = "Skinny"; + + // major, minor + const int version[] = { 1, 0 }; +} + +#endif diff --git a/qmlexport/QskQmlRegister.h b/qmlexport/QskQmlRegister.h new file mode 100644 index 00000000..134309b2 --- /dev/null +++ b/qmlexport/QskQmlRegister.h @@ -0,0 +1,131 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_QML_REGISTER_H +#define QSK_QML_REGISTER_H + +#include "QskQmlModule.h" + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) +#include "QskQmlClassInfo.h" +#endif + +#include + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + #define QSK_QML_REGISTER 0 +#else + #define QSK_QML_REGISTER 1 +#endif + +// Required for QFlags to be constructed from an enum value +#define QSK_REGISTER_FLAGS( Type ) \ + QMetaType::registerConverter< int, Type >( []( int value ) { return Type( value ); } ) + +namespace QskQml +{ + inline const char* classNameQml( const QMetaObject& metaObject ) + { + // without the "Qsk" prefix + return metaObject.className() + 3; + } + + template< typename T > + inline int registerUncreatableType( const char* qmlName ) + { +#if QSK_QML_REGISTER + ClassInfo typeInfo( qmlName, &T::staticMetaObject ); + typeInfo.setTypeInfo< T >(); + + return typeInfo.registerType(); +#else + return qmlRegisterUncreatableType< T >( QskQmlModule::name, + QskQmlModule::version[0], QskQmlModule::version[1], qmlName, QString() ); +#endif + } + + inline int registerUncreatableMetaObject( + const QMetaObject& staticMetaObject, const char* qmlName ) + { +#if QSK_QML_REGISTER + ClassInfo typeInfo( qmlName, &staticMetaObject ); + return typeInfo.registerType(); +#else + return qmlRegisterUncreatableMetaObject( staticMetaObject, + QskQmlModule::name, QskQmlModule::version[0], QskQmlModule::version[1], + qmlName, QString() ); +#endif + } + + template< typename T > + inline int registerObject( const char* qmlName = nullptr ) + { + // the class name without the "Qsk" prefix + if ( qmlName == nullptr ) + qmlName = classNameQml( T::staticMetaObject ); + +#if QSK_QML_REGISTER + ClassInfo typeInfo( qmlName, &T::staticMetaObject ); + typeInfo.setTypeInfo< T >(); + + return typeInfo.registerType(); +#else + return qmlRegisterType< T >( QskQmlModule::name, + QskQmlModule::version[0], QskQmlModule::version[1], qmlName ); +#endif + } + + template< typename T > + inline int registerGadget() + { + auto className = classNameQml( T::staticMetaObject ); + +#if QSK_QML_REGISTER + /* + According to the QML naming rules uncreatables have to + start with a lowercase letter, while namespaces + and creatable items usually start with a upper letter. + This results in an odd naming scheme for the enums defined inside of gadgets. + + To work around this we register the gadget twice - starting with + upper or lower letter. + + Maybe it would make sense to only pass stripped metaObjects, where all + enums are removed from the first and everything else than the enums from + the second. TODO ... + */ + + if ( T::staticMetaObject.enumeratorCount() > 0 ) + registerUncreatableMetaObject( T::staticMetaObject, className ); + + QByteArray name = className; + name.data()[0] = std::tolower( name.data()[0] ); + + return registerUncreatableType< T >( name.constData() ); +#else + return registerUncreatableType< T >( className ); +#endif + } + + inline int registerNamespace( const QMetaObject& metaObject ) + { + return registerUncreatableMetaObject( metaObject, classNameQml( metaObject ) ); + } + + template< typename T > + inline int registerSingleton( QObject* singleton ) + { + const auto name = classNameQml( T::staticMetaObject ); + + return qmlRegisterSingletonInstance( QskQmlModule::name, + QskQmlModule::version[0], QskQmlModule::version[1], name, singleton ); + } +} + +#ifdef QSK_QML_REGISTER + #undef QSK_QML_REGISTER +#endif + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 29aca47d..0b774c94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,7 @@ list(APPEND HEADERS common/QskBoxBorderMetrics.h common/QskBoxShapeMetrics.h common/QskBoxHints.h + common/QskFontRole.h common/QskFunctions.h common/QskGlobal.h common/QskGradient.h @@ -45,6 +46,7 @@ list(APPEND SOURCES common/QskBoxBorderMetrics.cpp common/QskBoxShapeMetrics.cpp common/QskBoxHints.cpp + common/QskFontRole.cpp common/QskFunctions.cpp common/QskGradient.cpp common/QskGradientDirection.cpp @@ -99,6 +101,8 @@ list(APPEND SOURCES list(APPEND HEADERS nodes/QskArcNode.h + nodes/QskArcRenderer.h + nodes/QskArcRenderNode.h nodes/QskArcShadowNode.h nodes/QskBasicLinesNode.h nodes/QskBoxNode.h @@ -139,6 +143,8 @@ list(APPEND PRIVATE_HEADERS list(APPEND SOURCES nodes/QskArcNode.cpp + nodes/QskArcRenderer.cpp + nodes/QskArcRenderNode.cpp nodes/QskArcShadowNode.cpp nodes/QskBasicLinesNode.cpp nodes/QskBoxNode.cpp @@ -219,7 +225,7 @@ list(APPEND HEADERS controls/QskGraphicLabel.h controls/QskGraphicLabelSkinlet.h controls/QskHintAnimator.h - controls/QskInputGrabber.h + controls/QskItem.h controls/QskListView.h controls/QskListViewSkinlet.h controls/QskMenu.h @@ -239,7 +245,6 @@ list(APPEND HEADERS controls/QskPushButton.h controls/QskPushButtonSkinlet.h controls/QskQuick.h - controls/QskQuickItem.h controls/QskRadioBox.h controls/QskRadioBoxSkinlet.h controls/QskScrollArea.h @@ -290,8 +295,9 @@ list(APPEND HEADERS list(APPEND PRIVATE_HEADERS controls/QskDirtyItemFilter.h + controls/QskInputGrabber.h controls/QskControlPrivate.h - controls/QskQuickItemPrivate.h + controls/QskItemPrivate.h ) list(APPEND SOURCES @@ -324,6 +330,8 @@ list(APPEND SOURCES controls/QskGraphicLabelSkinlet.cpp controls/QskHintAnimator.cpp controls/QskInputGrabber.cpp + controls/QskItem.cpp + controls/QskItemPrivate.cpp controls/QskListView.cpp controls/QskListViewSkinlet.cpp controls/QskMenuSkinlet.cpp @@ -343,8 +351,6 @@ list(APPEND SOURCES controls/QskPushButton.cpp controls/QskPushButtonSkinlet.cpp controls/QskQuick.cpp - controls/QskQuickItem.cpp - controls/QskQuickItemPrivate.cpp controls/QskScrollArea.cpp controls/QskScrollBox.cpp controls/QskScrollView.cpp diff --git a/src/common/QskArcMetrics.cpp b/src/common/QskArcMetrics.cpp index a7981827..220630f9 100644 --- a/src/common/QskArcMetrics.cpp +++ b/src/common/QskArcMetrics.cpp @@ -21,6 +21,83 @@ static void qskRegisterArcMetrics() Q_CONSTRUCTOR_FUNCTION( qskRegisterArcMetrics ) +static inline QPainterPath qskRadialPathPath( + const QRectF& rect, qreal startAngle, qreal spanAngle, qreal width ) +{ + const auto sz = qMin( rect.width(), rect.height() ); + + const auto tx = width * rect.width() / sz; + const auto ty = width * rect.height() / sz; + + const auto innerRect = rect.adjusted( tx, ty, -tx, -ty ); + + QPainterPath path; + + if ( innerRect.isEmpty() ) + { + if ( qAbs( spanAngle ) >= 360.0 ) + { + path.addEllipse( rect ); + } + else + { + // pie + path.arcMoveTo( rect, startAngle ); + path.arcTo( rect, startAngle, spanAngle ); + path.lineTo( rect.center() ); + path.closeSubpath(); + } + } + else + { + if ( qAbs( spanAngle ) >= 360.0 ) + { + path.addEllipse( rect ); + + QPainterPath innerPath; + innerPath.addEllipse( innerRect ); + path -= innerPath; + } + else + { + /* + We need the end point of the inner arc to add the line that connects + the inner/outer arcs. As QPainterPath does not offer such a method + we insert a dummy arcMoveTo and grab the calculated position. + */ + path.arcMoveTo( innerRect, startAngle + spanAngle ); + const auto pos = path.currentPosition(); + + path.arcMoveTo( rect, startAngle ); // replaces the dummy arcMoveTo above + path.arcTo( rect, startAngle, spanAngle ); + + path.lineTo( pos ); + path.arcTo( innerRect, startAngle + spanAngle, -spanAngle ); + + path.closeSubpath(); + } + } + + return path; +} + +static inline QPainterPath qskOrthogonalPath( + const QRectF& rect, qreal startAngle, qreal spanAngle, qreal width ) +{ + const auto t2 = 0.5 * width; + const auto r = rect.adjusted( t2, t2, -t2, -t2 ); + + QPainterPath arcPath; + arcPath.arcMoveTo( r, startAngle ); + arcPath.arcTo( r, startAngle, spanAngle ); + + QPainterPathStroker stroker; + stroker.setCapStyle( Qt::FlatCap ); + stroker.setWidth( width ); + + return stroker.createStroke( arcPath ); +} + static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio ) { return from + ( to - from ) * ratio; @@ -48,7 +125,7 @@ void QskArcMetrics::setSpanAngle( qreal spanAngle ) noexcept void QskArcMetrics::setSizeMode( Qt::SizeMode sizeMode ) noexcept { - m_sizeMode = sizeMode; + m_relativeSize = ( sizeMode == Qt::RelativeSize ); } bool QskArcMetrics::isClosed() const @@ -79,7 +156,7 @@ bool QskArcMetrics::containsAngle( qreal angle ) const QskArcMetrics QskArcMetrics::interpolated( const QskArcMetrics& to, qreal ratio ) const noexcept { - if ( ( *this == to ) || ( m_sizeMode != to.m_sizeMode ) ) + if ( ( *this == to ) || ( m_relativeSize != to.m_relativeSize ) ) return to; const qreal thickness = qskInterpolated( m_thickness, to.m_thickness, ratio ); @@ -87,7 +164,7 @@ QskArcMetrics QskArcMetrics::interpolated( const qreal s1 = qskInterpolated( m_startAngle, to.m_startAngle, ratio ); const qreal s2 = qskInterpolated( endAngle(), to.endAngle(), ratio ); - return QskArcMetrics( s1, s2 - s1, thickness, m_sizeMode ); + return QskArcMetrics( s1, s2 - s1, thickness, sizeMode() ); } QVariant QskArcMetrics::interpolate( @@ -97,6 +174,11 @@ QVariant QskArcMetrics::interpolate( return QVariant::fromValue( from.interpolated( to, progress ) ); } +QskArcMetrics QskArcMetrics::toAbsolute( const QSizeF& size ) const noexcept +{ + return toAbsolute( 0.5 * size.width(), 0.5 * size.height() ); +} + QskArcMetrics QskArcMetrics::toAbsolute( qreal radiusX, qreal radiusY ) const noexcept { if ( radiusX < 0.0 ) @@ -110,73 +192,33 @@ QskArcMetrics QskArcMetrics::toAbsolute( qreal radiusX, qreal radiusY ) const no QskArcMetrics QskArcMetrics::toAbsolute( qreal radius ) const noexcept { - if ( m_sizeMode != Qt::RelativeSize ) + if ( !m_relativeSize ) return *this; const qreal t = qskEffectiveThickness( radius, m_thickness ); return QskArcMetrics( m_startAngle, m_spanAngle, t, Qt::AbsoluteSize ); } -QPainterPath QskArcMetrics::painterPath( const QRectF& ellipseRect ) const +QPainterPath QskArcMetrics::painterPath( const QRectF& rect, bool radial ) const { - const auto sz = qMin( ellipseRect.width(), ellipseRect.height() ); - - qreal t = m_thickness; - if ( m_sizeMode == Qt::RelativeSize ) - t = qskEffectiveThickness( 0.5 * sz, t ); - - if ( t <= 0.0 || qFuzzyIsNull( m_spanAngle ) ) - return QPainterPath(); - - const auto tx = t * ellipseRect.width() / sz; - const auto ty = t * ellipseRect.height() / sz; - - const auto innerRect = ellipseRect.adjusted( tx, ty, -tx, -ty ); - QPainterPath path; - if ( innerRect.isEmpty() ) + if ( !qFuzzyIsNull( m_spanAngle ) ) { - if ( qAbs( m_spanAngle ) >= 360.0 ) + qreal t = m_thickness; + + if ( m_relativeSize ) { - path.addEllipse( ellipseRect ); + const auto sz = qMin( rect.width(), rect.height() ); + t = qskEffectiveThickness( 0.5 * sz, t ); } - else + + if ( t > 0.0 ) { - // pie - path.arcMoveTo( ellipseRect, m_startAngle ); - path.arcTo( ellipseRect, m_startAngle, m_spanAngle ); - path.lineTo( ellipseRect.center() ); - path.closeSubpath(); - } - } - else - { - if ( qAbs( m_spanAngle ) >= 360.0 ) - { - path.addEllipse( ellipseRect ); - - QPainterPath innerPath; - innerPath.addEllipse( innerRect ); - path -= innerPath; - } - else - { - /* - We need the end point of the inner arc to add the line that connects - the inner/outer arcs. As QPainterPath does not offer such a method - we insert a dummy arcMoveTo and grab the calculated position. - */ - path.arcMoveTo( innerRect, m_startAngle + m_spanAngle ); - const auto pos = path.currentPosition(); - - path.arcMoveTo( ellipseRect, m_startAngle ); // replaces the dummy arcMoveTo above - path.arcTo( ellipseRect, m_startAngle, m_spanAngle ); - - path.lineTo( pos ); - path.arcTo( innerRect, m_startAngle + m_spanAngle, -m_spanAngle ); - - path.closeSubpath(); + if ( radial ) + path = qskRadialPathPath( rect, m_startAngle, m_spanAngle, t ); + else + path = qskOrthogonalPath( rect, m_startAngle, m_spanAngle, t ); } } @@ -212,8 +254,7 @@ QskHashValue QskArcMetrics::hash( QskHashValue seed ) const noexcept hash = qHash( m_startAngle, hash ); hash = qHash( m_spanAngle, hash ); - const int mode = m_sizeMode; - return qHashBits( &mode, sizeof( mode ), hash ); + return qHash( m_relativeSize, hash ); } #ifndef QT_NO_DEBUG_STREAM diff --git a/src/common/QskArcMetrics.h b/src/common/QskArcMetrics.h index fe80ed46..608e9d8e 100644 --- a/src/common/QskArcMetrics.h +++ b/src/common/QskArcMetrics.h @@ -29,8 +29,8 @@ class QSK_EXPORT QskArcMetrics constexpr QskArcMetrics( qreal thickness, Qt::SizeMode = Qt::AbsoluteSize ) noexcept; - constexpr QskArcMetrics( qreal startAngle, qreal spanAngle, - qreal thickness, Qt::SizeMode = Qt::AbsoluteSize ) noexcept; + constexpr QskArcMetrics( qreal startAngle, qreal spanAngle, qreal thickness, + Qt::SizeMode = Qt::AbsoluteSize ) noexcept; bool operator==( const QskArcMetrics& ) const noexcept; bool operator!=( const QskArcMetrics& ) const noexcept; @@ -58,10 +58,26 @@ class QSK_EXPORT QskArcMetrics QskArcMetrics interpolated( const QskArcMetrics&, qreal value ) const noexcept; + QskArcMetrics toAbsolute( const QSizeF& ) const noexcept; QskArcMetrics toAbsolute( qreal radiusX, qreal radiusY ) const noexcept; QskArcMetrics toAbsolute( qreal radius ) const noexcept; - QPainterPath painterPath( const QRectF& ellipseRect ) const; + /* + The arc is interpolated by pairs of points, where one point is on + the outer and the other on the inner side of the arc. The length between + these points depends on the thickness. + + When radial is set the inner point lies on the line between the outer point + and the center of the arc. This corresponds to the lines of a conic gradient. + + Otherwise the line between the inner and outer point is orthogonal to the + tangent at the point in the middle of the arc. This is how the width + of the pen is expanded by QPainter::drawArc. + + Note, that the radial flag is irrelevant for circular arcs as the tangent + is always orthogonal to any point on the circle. + */ + QPainterPath painterPath( const QRectF& ellipseRect, bool radial = false ) const; QRectF boundingRect( const QRectF& ellipseRect ) const; QSizeF boundingSize( const QSizeF& ellipseSize ) const; @@ -76,7 +92,8 @@ class QSK_EXPORT QskArcMetrics qreal m_spanAngle = 0.0; qreal m_thickness = 0.0; - Qt::SizeMode m_sizeMode = Qt::AbsoluteSize; + + bool m_relativeSize = false; }; inline constexpr QskArcMetrics::QskArcMetrics( @@ -85,23 +102,22 @@ inline constexpr QskArcMetrics::QskArcMetrics( { } -inline constexpr QskArcMetrics::QskArcMetrics( - qreal startAngle, qreal spanAngle, +inline constexpr QskArcMetrics::QskArcMetrics( qreal startAngle, qreal spanAngle, qreal thickness, Qt::SizeMode sizeMode ) noexcept : m_startAngle( startAngle ) , m_spanAngle( spanAngle ) , m_thickness( thickness ) - , m_sizeMode( sizeMode ) + , m_relativeSize( sizeMode == Qt::RelativeSize ) { } inline bool QskArcMetrics::operator==( const QskArcMetrics& other ) const noexcept { - return ( qskFuzzyCompare( m_thickness, other.m_thickness ) + return qskFuzzyCompare( m_thickness, other.m_thickness ) && qskFuzzyCompare( m_startAngle, other.m_startAngle ) && qskFuzzyCompare( m_spanAngle, other.m_spanAngle ) - && m_sizeMode == other.m_sizeMode ); + && ( m_relativeSize == other.m_relativeSize ); } inline bool QskArcMetrics::operator!=( @@ -142,7 +158,7 @@ inline constexpr qreal QskArcMetrics::angleAtRatio( qreal ratio ) const noexcept inline constexpr Qt::SizeMode QskArcMetrics::sizeMode() const noexcept { - return m_sizeMode; + return m_relativeSize ? Qt::RelativeSize : Qt::AbsoluteSize; } #ifndef QT_NO_DEBUG_STREAM diff --git a/src/common/QskBoxBorderColors.cpp b/src/common/QskBoxBorderColors.cpp index 9bde095b..e8fb4c13 100644 --- a/src/common/QskBoxBorderColors.cpp +++ b/src/common/QskBoxBorderColors.cpp @@ -135,17 +135,20 @@ const QskGradient& QskBoxBorderColors::gradientAt( Qt::Edge edge ) const bool QskBoxBorderColors::isVisible() const { - if ( m_gradients[ 0 ].isVisible() ) - return true; + if ( isValid() ) + { + if ( m_gradients[ 0 ].isVisible() ) + return true; - if ( m_gradients[ 1 ].isVisible() ) - return true; + if ( m_gradients[ 1 ].isVisible() ) + return true; - if ( m_gradients[ 2 ].isVisible() ) - return true; + if ( m_gradients[ 2 ].isVisible() ) + return true; - if ( m_gradients[ 3 ].isVisible() ) - return true; + if ( m_gradients[ 3 ].isVisible() ) + return true; + } return false; } @@ -175,9 +178,9 @@ bool QskBoxBorderColors::isMonochrome() const bool QskBoxBorderColors::isValid() const { return m_gradients[ 0 ].isValid() - || m_gradients[ 1 ].isValid() - || m_gradients[ 2 ].isValid() - || m_gradients[ 3 ].isValid(); + && m_gradients[ 1 ].isValid() + && m_gradients[ 2 ].isValid() + && m_gradients[ 3 ].isValid(); } QskBoxBorderColors QskBoxBorderColors::interpolated( diff --git a/src/common/QskFontRole.cpp b/src/common/QskFontRole.cpp new file mode 100644 index 00000000..5be6b21f --- /dev/null +++ b/src/common/QskFontRole.cpp @@ -0,0 +1,45 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskFontRole.h" +#include + +static void qskRegisterFontRole() +{ + qRegisterMetaType< QskFontRole >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskFontRole >(); +#endif +} + +Q_CONSTRUCTOR_FUNCTION( qskRegisterFontRole ) + +QskHashValue QskFontRole::hash( QskHashValue seed ) const noexcept +{ + const auto v = static_cast< ushort >( category() ) + | ( static_cast< ushort >( emphasis() ) << 8 ); + + return qHash( v, seed ); +} + +#ifndef QT_NO_DEBUG_STREAM + +#include + +QDebug operator<<( QDebug debug, const QskFontRole& fontRole ) +{ + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << "FontRole( " << fontRole.category() + << ", " << fontRole.emphasis() << " )"; + + return debug; +} + +#endif + +#include "moc_QskFontRole.cpp" diff --git a/src/common/QskFontRole.h b/src/common/QskFontRole.h new file mode 100644 index 00000000..c0873686 --- /dev/null +++ b/src/common/QskFontRole.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_FONT_ROLE_H +#define QSK_FONT_ROLE_H + +#include "QskGlobal.h" +#include + +class QSK_EXPORT QskFontRole +{ + Q_GADGET + + Q_PROPERTY( Category category READ category WRITE setCategory ) + Q_PROPERTY( Emphasis emphasis READ emphasis WRITE setEmphasis ) + + public: + enum Category + { + Caption, + Body, + Subtitle, + Title, + Headline, + Display + }; + Q_ENUM( Category ); + + enum Emphasis + { + VeryLow, + Low, + + Normal, + + High, + VeryHigh + }; + Q_ENUM( Emphasis ); + + constexpr QskFontRole( Category = Body, Emphasis = Normal ) noexcept; + + constexpr bool operator==( const QskFontRole& ) const noexcept; + constexpr bool operator!=( const QskFontRole& ) const noexcept; + + void setCategory( Category ) noexcept; + constexpr Category category() const noexcept; + + void setEmphasis( Emphasis ) noexcept; + constexpr Emphasis emphasis() const noexcept; + + QskHashValue hash( QskHashValue seed = 0 ) const noexcept; + + private: + unsigned char m_category; + unsigned char m_emphasis; +}; + +inline constexpr QskFontRole::QskFontRole( Category category, Emphasis emphasis ) noexcept + : m_category( category ) + , m_emphasis( emphasis ) +{ +} + +inline constexpr bool QskFontRole::operator==( const QskFontRole& other ) const noexcept +{ + return ( m_category == other.m_category ) && ( m_emphasis == other.m_emphasis ); +} + +inline constexpr bool QskFontRole::operator!=( const QskFontRole& other ) const noexcept +{ + return !( *this == other ); +} + +inline void QskFontRole::setCategory( Category category ) noexcept +{ + m_category = category; +} + +inline constexpr QskFontRole::Category QskFontRole::category() const noexcept +{ + return static_cast< Category >( m_category ); +} + +inline void QskFontRole::setEmphasis( Emphasis emphasis ) noexcept +{ + m_emphasis = emphasis; +} + +inline constexpr QskFontRole::Emphasis QskFontRole::emphasis() const noexcept +{ + return static_cast< Emphasis >( m_emphasis ); +} + +inline QskHashValue qHash( const QskFontRole fontRole, QskHashValue seed = 0 ) noexcept +{ + return fontRole.hash( seed ); +} + +namespace std +{ + template< > struct hash< QskFontRole > + { + size_t operator()( const QskFontRole& fontRole ) const noexcept + { + return fontRole.hash(); + } + }; +} + +#ifndef QT_NO_DEBUG_STREAM + +QSK_EXPORT QDebug operator<<( QDebug, const QskFontRole& ); + +#endif + +Q_DECLARE_TYPEINFO( QskFontRole, Q_MOVABLE_TYPE ); +Q_DECLARE_METATYPE( QskFontRole ) + +#endif diff --git a/src/common/QskGradientStop.cpp b/src/common/QskGradientStop.cpp index 451fa70c..82d59d06 100644 --- a/src/common/QskGradientStop.cpp +++ b/src/common/QskGradientStop.cpp @@ -293,44 +293,67 @@ QColor qskInterpolatedColorAt( const QskGradientStops& stops, qreal pos ) noexce } QskGradientStops qskExtractedGradientStops( - const QskGradientStops& stops, qreal from, qreal to ) + const QskGradientStops& gradientStops, qreal from, qreal to ) { - if ( ( from > to ) || ( from > 1.0 ) || stops.isEmpty() ) + if ( ( from > to ) || ( from > 1.0 ) || gradientStops.isEmpty() ) return QskGradientStops(); if ( ( from <= 0.0 ) && ( to >= 1.0 ) ) - return stops; + return gradientStops; from = qMax( from, 0.0 ); to = qMin( to, 1.0 ); - QVector< QskGradientStop > extracted; - extracted.reserve( stops.count() ); + QVector< QskGradientStop > stops1 = gradientStops; - int i = 0; +#if 1 + // not the most efficient implementation - maybe later TODO ... - for ( ; i < stops.count(); i++ ) + if ( stops1.first().position() > 0.0 ) + stops1.prepend( QskGradientStop( 0.0, stops1.first().color() ) ); + + if ( stops1.last().position() < 1.0 ) + stops1.append( QskGradientStop( 1.0, stops1.last().color() ) ); +#endif + + QVector< QskGradientStop > stops2; + stops2.reserve( stops1.count() ); + + if ( stops1.count() == 2 ) { - if ( stops[i].position() > from ) - break; + const auto rgb1 = stops1.first().rgb(); + const auto rgb2 = stops1.last().rgb(); + + stops2 += QskGradientStop( 0.0, QskRgb::interpolated( rgb1, rgb2, from ) ); + stops2 += QskGradientStop( 1.0, QskRgb::interpolated( rgb1, rgb2, to ) ); + } + else + { + int i = 0; + + for ( ; i < stops1.count(); i++ ) + { + if ( stops1[i].position() > from ) + break; + } + + stops2 += QskGradientStop( 0.0, + qskInterpolatedColor( stops1, i - 1, i, from ) ); + + for ( ; i < stops1.count(); i++ ) + { + if ( stops1[i].position() >= to ) + break; + + const auto pos = ( stops1[i].position() - from ) / ( to - from ); + stops2 += QskGradientStop( pos, stops1[i].color() ); + } + + stops2 += QskGradientStop( 1.0, + qskInterpolatedColor( stops1, i, i + 1, to ) ); } - extracted += QskGradientStop( 0.0, - qskInterpolatedColor( stops, i - 1, i, from ) ); - - for ( ; i < stops.count(); i++ ) - { - if ( stops[i].position() >= to ) - break; - - const auto pos = ( stops[i].position() - from ) / ( to - from ); - extracted += QskGradientStop( pos, stops[i].color() ); - } - - extracted += QskGradientStop( 1.0, - qskInterpolatedColor( stops, i, i + 1, to ) ); - - return extracted; + return stops2; } QskGradientStops qskRevertedGradientStops( const QskGradientStops& stops ) diff --git a/src/common/QskGraduation.cpp b/src/common/QskGraduation.cpp index 3d20132d..1eab1f28 100644 --- a/src/common/QskGraduation.cpp +++ b/src/common/QskGraduation.cpp @@ -17,8 +17,6 @@ namespace Engine { // What about using qskFuzzyCompare and friends ??? - const double _eps = 1.0e-6; - inline int fuzzyCompare( double value1, double value2, double intervalSize ) { const double eps = std::abs( 1.0e-6 * intervalSize ); diff --git a/src/common/QskMetaInvokable.cpp b/src/common/QskMetaInvokable.cpp index 09a576f0..31a6c0df 100644 --- a/src/common/QskMetaInvokable.cpp +++ b/src/common/QskMetaInvokable.cpp @@ -26,15 +26,6 @@ static void qskRegisterMetaInvokable() Q_CONSTRUCTOR_FUNCTION( qskRegisterMetaInvokable ) -static inline void* qskMetaTypeCreate( int type, const void* copy ) -{ -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - return QMetaType( type ).create( copy ); -#else - return QMetaType::create( type, copy ); -#endif -} - namespace { using CallFunction = QObjectPrivate::StaticMetaCallFunction; diff --git a/src/common/QskPlatform.cpp b/src/common/QskPlatform.cpp index c54c29eb..10fdc86f 100644 --- a/src/common/QskPlatform.cpp +++ b/src/common/QskPlatform.cpp @@ -76,10 +76,15 @@ static inline qreal qskRoundedDpi( qreal dpi ) qreal qskDpToPixelsFactor() { if ( const auto screen = QGuiApplication::primaryScreen() ) - { - // see: https://en.wikipedia.org/wiki/Device-independent_pixel return qskRoundedDpi( screen->physicalDotsPerInch() ) / 160.0; - } + + return 1.0; +} + +qreal qskPxToPixelsFactor() +{ + if ( const auto screen = QGuiApplication::primaryScreen() ) + return screen->physicalDotsPerInch() / 96.0; return 1.0; } diff --git a/src/common/QskPlatform.h b/src/common/QskPlatform.h index 48be41b3..e5c9fa83 100644 --- a/src/common/QskPlatform.h +++ b/src/common/QskPlatform.h @@ -22,13 +22,16 @@ QSK_EXPORT const QPlatformIntegration* qskPlatformIntegration(); QSK_EXPORT const QPlatformTheme* qskPlatformTheme(); /* + see: https://en.wikipedia.org/wiki/Device-independent_pixel + One dp is a virtual pixel unit that's roughly equal to one pixel on a medium-density screen ( 160 dpi ). - see: https://en.wikipedia.org/wiki/Device-independent_pixel + One px is equivalent to 1/96th of an inch. */ QSK_EXPORT qreal qskDpToPixelsFactor(); +QSK_EXPORT qreal qskPxToPixelsFactor(); inline qreal qskDpToPixels( qreal value ) { @@ -40,4 +43,14 @@ inline qreal qskDpToPixels( qreal value ) return value * factor; } +inline qreal qskPxToPixels( qreal value ) +{ + static qreal factor = -1.0; + + if ( factor < 0.0 ) + factor = qskPxToPixelsFactor(); + + return value * factor; +} + #endif diff --git a/src/controls/QskAbstractButton.h b/src/controls/QskAbstractButton.h index 6d081a48..60b0616b 100644 --- a/src/controls/QskAbstractButton.h +++ b/src/controls/QskAbstractButton.h @@ -28,7 +28,7 @@ class QSK_EXPORT QskAbstractButton : public QskControl WRITE setPressed NOTIFY pressedChanged FINAL ) Q_PROPERTY( bool checked READ isChecked - WRITE setChecked NOTIFY checkedChanged FINAL ) + WRITE setChecked NOTIFY checkedChanged USER true FINAL ) using Inherited = QskControl; diff --git a/src/controls/QskBoundedValueInput.h b/src/controls/QskBoundedValueInput.h index e5af6617..31ad760f 100644 --- a/src/controls/QskBoundedValueInput.h +++ b/src/controls/QskBoundedValueInput.h @@ -13,7 +13,7 @@ class QSK_EXPORT QskBoundedValueInput : public QskBoundedInput Q_OBJECT Q_PROPERTY( qreal value READ value - WRITE setValue NOTIFY valueChanged ) + WRITE setValue NOTIFY valueChanged USER true ) Q_PROPERTY( qreal valueAsRatio READ valueAsRatio WRITE setValueAsRatio NOTIFY valueChanged ) diff --git a/src/controls/QskComboBox.cpp b/src/controls/QskComboBox.cpp index 9e39f602..0ca87675 100644 --- a/src/controls/QskComboBox.cpp +++ b/src/controls/QskComboBox.cpp @@ -236,14 +236,16 @@ void QskComboBox::openPopup() auto menu = new QskMenu(); - menu->setParent( this ); - menu->setParentItem( window()->contentItem() ); - menu->setPopupFlag( QskPopup::DeleteOnClose, true ); - + menu->setWrapping( false ); menu->setOrigin( mapToScene( cr.bottomLeft() ) ); menu->setFixedWidth( cr.width() ); menu->setOptions( m_data->options ); + menu->setCurrentIndex( currentIndex() ); + + menu->setParent( this ); + menu->setParentItem( window()->contentItem() ); + menu->setPopupFlag( QskPopup::DeleteOnClose, true ); connect( menu, &QskMenu::currentIndexChanged, this, &QskComboBox::indexInPopupChanged ); diff --git a/src/controls/QskComboBox.h b/src/controls/QskComboBox.h index 76b7df1e..51495f72 100644 --- a/src/controls/QskComboBox.h +++ b/src/controls/QskComboBox.h @@ -24,7 +24,7 @@ class QSK_EXPORT QskComboBox : public QskControl WRITE setCurrentIndex NOTIFY currentIndexChanged ) Q_PROPERTY( QString currentText READ currentText - NOTIFY currentIndexChanged ) + NOTIFY currentIndexChanged USER true) Q_PROPERTY( int count READ count ) diff --git a/src/controls/QskControl.cpp b/src/controls/QskControl.cpp index b2b03bca..066e9308 100644 --- a/src/controls/QskControl.cpp +++ b/src/controls/QskControl.cpp @@ -47,10 +47,8 @@ static inline bool qskMaybeGesture( QQuickItem* item, } QskControl::QskControl( QQuickItem* parent ) - : QskQuickItem( *( new QskControlPrivate() ), parent ) + : QskItem( *( new QskControlPrivate() ), parent ) { - Inherited::setActiveFocusOnTab( false ); - if ( parent ) { // inheriting attributes from parent @@ -91,55 +89,6 @@ bool QskControl::autoLayoutChildren() const return d_func()->autoLayoutChildren; } -void QskControl::setWheelEnabled( bool on ) -{ - Q_D( QskControl ); - if ( on != d->isWheelEnabled ) - { - d->isWheelEnabled = on; - Q_EMIT wheelEnabledChanged(); - } -} - -bool QskControl::isWheelEnabled() const -{ - return d_func()->isWheelEnabled; -} - -void QskControl::setFocusPolicy( Qt::FocusPolicy policy ) -{ - Q_D( QskControl ); - if ( policy != d->focusPolicy ) - { - d->focusPolicy = ( policy & ~Qt::TabFocus ); - - const bool tabFocus = policy & Qt::TabFocus; - - if ( !tabFocus && window() ) - { - // Removing the activeFocusItem from the focus tab chain is not possible - if ( window()->activeFocusItem() == this ) - { - if ( auto focusItem = nextItemInFocusChain( true ) ) - focusItem->setFocus( true ); - } - } - - Inherited::setActiveFocusOnTab( tabFocus ); - - Q_EMIT focusPolicyChanged(); - } -} - -Qt::FocusPolicy QskControl::focusPolicy() const -{ - uint policy = d_func()->focusPolicy; - if ( Inherited::activeFocusOnTab() ) - policy |= Qt::TabFocus; - - return static_cast< Qt::FocusPolicy >( policy ); -} - void QskControl::setBackgroundColor( const QColor& color ) { setBackground( QskGradient( color ) ); @@ -857,7 +806,7 @@ bool QskControl::childMouseEventFilter( QQuickItem* child, QEvent* event ) { /* The strategy implemented in many classes of the Qt development is - to analyze the events without blocking the handling of the child. + to analyze the events without blocking the handling of the child. Once a gesture is detected the gesture handling trys to steal the mouse grab hoping for the child to abort its operation. diff --git a/src/controls/QskControl.h b/src/controls/QskControl.h index 41793c87..fc87e6f5 100644 --- a/src/controls/QskControl.h +++ b/src/controls/QskControl.h @@ -6,7 +6,7 @@ #ifndef QSK_CONTROL_H #define QSK_CONTROL_H -#include "QskQuickItem.h" +#include "QskItem.h" #include "QskSkinnable.h" #include "QskAspect.h" #include "QskGradient.h" @@ -19,7 +19,7 @@ class QskControlPrivate; class QskGestureEvent; -class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable +class QSK_EXPORT QskControl : public QskItem, public QskSkinnable { Q_OBJECT @@ -32,12 +32,6 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable Q_PROPERTY( bool autoLayoutChildren READ autoLayoutChildren WRITE setAutoLayoutChildren ) - Q_PROPERTY( Qt::FocusPolicy focusPolicy READ focusPolicy - WRITE setFocusPolicy NOTIFY focusPolicyChanged ) - - Q_PROPERTY( bool wheelEnabled READ isWheelEnabled - WRITE setWheelEnabled NOTIFY wheelEnabledChanged ) - Q_PROPERTY( bool visibleToLayout READ isVisibleToLayout ) Q_PROPERTY( QskMargins margins READ margins @@ -55,7 +49,7 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable Q_PROPERTY( QSizeF preferredSize READ preferredSize WRITE setPreferredSize ) Q_PROPERTY( QSizeF sizeConstraint READ sizeConstraint ) - using Inherited = QskQuickItem; + using Inherited = QskItem; public: QSK_SUBCONTROLS( Background ) @@ -95,12 +89,6 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable void setAutoLayoutChildren( bool ); bool autoLayoutChildren() const; - void setWheelEnabled( bool ); - bool isWheelEnabled() const; - - void setFocusPolicy( Qt::FocusPolicy ); - Qt::FocusPolicy focusPolicy() const; - void setSection( QskAspect::Section ); void resetSection(); QskAspect::Section section() const override final; @@ -180,8 +168,6 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable void marginsChanged( const QMarginsF& ); void focusIndicatorRectChanged(); void localeChanged( const QLocale& ); - void focusPolicyChanged(); - void wheelEnabledChanged(); public Q_SLOTS: void setLocale( const QLocale& ); @@ -209,7 +195,6 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable virtual QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const; private: - void setActiveFocusOnTab( bool ) = delete; // use setFocusPolicy void updateInputMethod( Qt::InputMethodQueries ) = delete; // use qskUpdateInputMethod QSGNode* updateItemPaintNode( QSGNode* ) override final; diff --git a/src/controls/QskControlPrivate.cpp b/src/controls/QskControlPrivate.cpp index c8608dc4..d8f402ba 100644 --- a/src/controls/QskControlPrivate.cpp +++ b/src/controls/QskControlPrivate.cpp @@ -150,8 +150,6 @@ QskControlPrivate::QskControlPrivate() , explicitLocale( false ) , explicitSection( false ) , autoLayoutChildren( false ) - , focusPolicy( Qt::NoFocus ) - , isWheelEnabled( false ) , blockLayoutRequestEvents( true ) { } diff --git a/src/controls/QskControlPrivate.h b/src/controls/QskControlPrivate.h index 239201a5..0bf9c2b3 100644 --- a/src/controls/QskControlPrivate.h +++ b/src/controls/QskControlPrivate.h @@ -8,11 +8,11 @@ #include "QskGlobal.h" #include "QskControl.h" -#include "QskQuickItemPrivate.h" +#include "QskItemPrivate.h" -class QskControlPrivate : public QskQuickItemPrivate +class QskControlPrivate : public QskItemPrivate { - using Inherited = QskQuickItemPrivate; + using Inherited = QskItemPrivate; public: static bool inheritLocale( QskControl*, const QLocale& ); @@ -59,9 +59,6 @@ class QskControlPrivate : public QskQuickItemPrivate bool autoLayoutChildren : 1; - uint focusPolicy : 4; - bool isWheelEnabled : 1; - mutable bool blockLayoutRequestEvents : 1; }; diff --git a/src/controls/QskDirtyItemFilter.cpp b/src/controls/QskDirtyItemFilter.cpp index 416f0253..f1189424 100644 --- a/src/controls/QskDirtyItemFilter.cpp +++ b/src/controls/QskDirtyItemFilter.cpp @@ -4,7 +4,7 @@ *****************************************************************************/ #include "QskDirtyItemFilter.h" -#include "QskQuickItem.h" +#include "QskItem.h" QSK_QT_PRIVATE_BEGIN #include @@ -15,8 +15,8 @@ static inline bool qskIsUpdateBlocked( const QQuickItem* item ) { if ( !item->isVisible() ) { - if ( auto qskItem = qobject_cast< const QskQuickItem* >( item ) ) - return qskItem->testUpdateFlag( QskQuickItem::DeferredUpdate ); + if ( auto qskItem = qobject_cast< const QskItem* >( item ) ) + return qskItem->testUpdateFlag( QskItem::DeferredUpdate ); } #if 0 diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index c08cbd69..459a8fba 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -16,7 +16,6 @@ QSK_QT_PRIVATE_BEGIN #include -#include QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskDrawer, Panel ) @@ -110,52 +109,6 @@ static QPointF qskDrawerTranslation( const QskDrawer* drawer, const QSizeF& size namespace { - // Using an eventFilter for QskEvent::GeometryChange instead ??? - - class GeometryListener final : public QQuickItemChangeListener - { - public: - GeometryListener( QQuickItem* item, QQuickItem* adjustedItem ) - : m_item( item ) - , m_adjustedItem( adjustedItem ) - { - adjust(); - setEnabled( true ); - } - - ~GeometryListener() - { - setEnabled( false ); - } - - private: - void itemGeometryChanged( QQuickItem*, - QQuickGeometryChange, const QRectF& ) override - { - adjust(); - } - - private: - void adjust() - { - m_adjustedItem->polish(); - } - - void setEnabled( bool on ) - { - const auto changeTypes = QQuickItemPrivate::Geometry; - - auto d = QQuickItemPrivate::get( m_item ); - if ( on ) - d->addItemChangeListener( this, changeTypes ); - else - d->removeItemChangeListener( this, changeTypes ); - } - - QQuickItem* m_item; - QQuickItem* m_adjustedItem; - }; - class GestureRecognizer : public QskPanGestureRecognizer { using Inherited = QskPanGestureRecognizer; @@ -216,16 +169,6 @@ class QskDrawer::PrivateData { } - inline void resetListener( QskDrawer* drawer ) - { - delete listener; - listener = nullptr; - - if ( drawer->parentItem() && drawer->isVisible() ) - listener = new GeometryListener( drawer->parentItem(), drawer ); - } - - GeometryListener* listener = nullptr; GestureRecognizer* gestureRecognizer = nullptr; qreal dragMargin = qskDefaultDragMargin(); @@ -235,6 +178,7 @@ class QskDrawer::PrivateData QskDrawer::QskDrawer( QQuickItem* parentItem ) : QskDrawer( Qt::LeftEdge, parentItem ) { + setPolishOnParentResize( true ); } QskDrawer::QskDrawer( Qt::Edge edge, QQuickItem* parentItem ) @@ -262,7 +206,6 @@ QskDrawer::QskDrawer( Qt::Edge edge, QQuickItem* parentItem ) QskDrawer::~QskDrawer() { - delete m_data->listener; } Qt::Edge QskDrawer::edge() const @@ -361,12 +304,6 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, if ( parentItem() && isInteractive() ) qskCatchMouseEvents( parentItem() ); - m_data->resetListener( this ); - break; - } - case QQuickItem::ItemVisibleHasChanged: - { - m_data->resetListener( this ); break; } } diff --git a/src/controls/QskGraphicLabel.cpp b/src/controls/QskGraphicLabel.cpp index 0f05e4dc..59846418 100644 --- a/src/controls/QskGraphicLabel.cpp +++ b/src/controls/QskGraphicLabel.cpp @@ -10,6 +10,7 @@ #include "QskGraphicProvider.h" #include "QskSkinManager.h" #include "QskSkin.h" +#include "QskEvent.h" QSK_SUBCONTROL( QskGraphicLabel, Panel ) QSK_SUBCONTROL( QskGraphicLabel, Graphic ) @@ -305,6 +306,25 @@ QSizeF QskGraphicLabel::effectiveSourceSize() const return sz; } +void QskGraphicLabel::geometryChangeEvent( QskGeometryChangeEvent* event ) +{ + /* + textures will finally be scaled into a target rectangle in integer + coordinates. This rectangle might have a different size - depending + on how its coordinates are rounded ( ceiled/floored ) - even if + the site in floating point coordinates is the same. + + So we always trigger updates - even for translations. + + Note that an update triggers the checks, that are needed to decide if + the texture needs to be recreated - it does not necessarily + result in actually doing it. + */ + update(); + + Inherited::geometryChangeEvent( event ); +} + void QskGraphicLabel::changeEvent( QEvent* event ) { if ( event->type() == QEvent::StyleChange ) diff --git a/src/controls/QskGraphicLabel.h b/src/controls/QskGraphicLabel.h index 12a12881..f79ecf39 100644 --- a/src/controls/QskGraphicLabel.h +++ b/src/controls/QskGraphicLabel.h @@ -15,7 +15,7 @@ class QSK_EXPORT QskGraphicLabel : public QskControl { Q_OBJECT - Q_PROPERTY( QUrl source READ source WRITE setSource NOTIFY sourceChanged ) + Q_PROPERTY( QUrl source READ source WRITE setSource NOTIFY sourceChanged USER true ) Q_PROPERTY( bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged ) @@ -105,7 +105,9 @@ class QSK_EXPORT QskGraphicLabel : public QskControl void setGraphic( const QskGraphic& ); protected: + void geometryChangeEvent( QskGeometryChangeEvent* ) override; void changeEvent( QEvent* ) override; + void updateResources() override; virtual QskGraphic loadSource( const QUrl& ) const; diff --git a/src/controls/QskQuickItem.cpp b/src/controls/QskItem.cpp similarity index 68% rename from src/controls/QskQuickItem.cpp rename to src/controls/QskItem.cpp index 75e7b8ae..9b2d1eb7 100644 --- a/src/controls/QskQuickItem.cpp +++ b/src/controls/QskItem.cpp @@ -3,8 +3,8 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#include "QskQuickItem.h" -#include "QskQuickItemPrivate.h" +#include "QskItem.h" +#include "QskItemPrivate.h" #include "QskQuick.h" #include "QskEvent.h" #include "QskSetup.h" @@ -31,6 +31,8 @@ QSK_QT_PRIVATE_BEGIN #include #endif +#include + QSK_QT_PRIVATE_END #endif @@ -44,9 +46,9 @@ static inline void qskSendEventTo( QObject* object, QEvent::Type type ) } static inline void qskApplyUpdateFlags( - QskQuickItem::UpdateFlags flags, QskQuickItem* item ) + QskItem::UpdateFlags flags, QskItem* item ) { - auto d = static_cast< QskQuickItemPrivate* >( QskQuickItemPrivate::get( item ) ); + auto d = static_cast< QskItemPrivate* >( QskItemPrivate::get( item ) ); d->applyUpdateFlags( flags ); } @@ -61,19 +63,15 @@ static inline void qskFilterWindow( QQuickWindow* window ) namespace { - class QskQuickItemRegistry + class QskItemRegistry { public: - QskQuickItemRegistry() + QskItemRegistry() { /* Its faster and saves some memory to have this registry instead of setting up direct connections between qskSetup and each control - */ - QObject::connect( qskSetup, &QskSetup::itemUpdateFlagsChanged, - qskSetup, [ this ] { updateControlFlags(); } ); - /* We would also need to send QEvent::StyleChange, when a window has a new skin. TODO ... */ @@ -84,19 +82,19 @@ namespace qskSkinManager, [ this ] { updateSkin(); } ); } - inline void insert( QskQuickItem* item ) + inline void insert( QskItem* item ) { m_items.insert( item ); } - inline void remove( QskQuickItem* item ) + inline void remove( QskItem* item ) { m_items.erase( item ); } - void updateControlFlags() + void updateItemFlags() { - const auto flags = qskSetup->itemUpdateFlags(); + const auto flags = QskSetup::updateFlags(); for ( auto item : m_items ) qskApplyUpdateFlags( flags, item ); @@ -114,7 +112,7 @@ namespace } private: - std::unordered_set< QskQuickItem* > m_items; + std::unordered_set< QskItem* > m_items; }; } @@ -164,21 +162,86 @@ namespace }; } -Q_GLOBAL_STATIC( QskQuickItemRegistry, qskRegistry ) -Q_GLOBAL_STATIC( QskWindowStore, qskReleasedWindowCounter ) +namespace +{ + // A helper class for the polishOnParentResize feature -QskQuickItem::QskQuickItem( QskQuickItemPrivate& dd, QQuickItem* parent ) + class QskParentListener final : public QQuickItemChangeListener + { + public: + void update( QQuickItem* parentItem ) + { + if ( parentItem == nullptr ) + return; + + const auto changeTypes = + QQuickItemPrivate::Geometry | QQuickItemPrivate::Children; + + auto d = QQuickItemPrivate::get( parentItem ); + + if ( needListening( parentItem ) ) + d->updateOrAddItemChangeListener( this, changeTypes ); + else + d->removeItemChangeListener( this, changeTypes ); + } + + private: + inline bool needListening( const QQuickItem* parentItem ) const + { + const auto children = parentItem->childItems(); + for ( auto child : children ) + { + if ( auto item = qobject_cast< const QskItem* >( child ) ) + { + if ( item->polishOnParentResize() ) + return true; + } + } + + return false; + } + + void itemGeometryChanged( QQuickItem* parentItem, + QQuickGeometryChange, const QRectF& ) override + { + const auto children = parentItem->childItems(); + for ( auto child : children ) + { + if ( auto item = qobject_cast< QskItem* >( child ) ) + { + if ( item->polishOnParentResize() ) + { + item->resetImplicitSize(); + item->polish(); + } + } + } + } + + void itemChildRemoved( QQuickItem* parentItem, QQuickItem* ) + { + update( parentItem ); + } + }; +} + +Q_GLOBAL_STATIC( QskItemRegistry, qskRegistry ) +Q_GLOBAL_STATIC( QskWindowStore, qskReleasedWindowCounter ) +Q_GLOBAL_STATIC( QskParentListener, qskParentListener ) + +QskItem::QskItem( QskItemPrivate& dd, QQuickItem* parent ) : QQuickItem( dd, parent ) { setFlag( QQuickItem::ItemHasContents, true ); + Inherited::setActiveFocusOnTab( false ); - if ( dd.updateFlags & QskQuickItem::DeferredUpdate ) + if ( dd.updateFlags & QskItem::DeferredUpdate ) qskFilterWindow( window() ); qskRegistry->insert( this ); } -QskQuickItem::~QskQuickItem() +QskItem::~QskItem() { /* We set componentComplete to false, so that operations @@ -196,24 +259,24 @@ QskQuickItem::~QskQuickItem() qskRegistry->remove( this ); } -const char* QskQuickItem::className() const +const char* QskItem::className() const { return metaObject()->className(); } -void QskQuickItem::classBegin() +void QskItem::classBegin() { Inherited::classBegin(); } -void QskQuickItem::componentComplete() +void QskItem::componentComplete() { #if defined( QT_DEBUG ) - if ( d_func()->updateFlags & QskQuickItem::DeferredLayout ) + if ( d_func()->updateFlags & QskItem::DeferredLayout ) { if ( qobject_cast< const QQuickBasePositioner* >( parent() ) ) { - qWarning( "QskQuickItem in DeferredLayout mode under control of a positioner" ); + qWarning( "QskItem in DeferredLayout mode under control of a positioner" ); } } #endif @@ -221,7 +284,7 @@ void QskQuickItem::componentComplete() Inherited::componentComplete(); } -void QskQuickItem::releaseResources() +void QskItem::releaseResources() { Inherited::releaseResources(); @@ -234,37 +297,37 @@ void QskQuickItem::releaseResources() qskReleasedWindowCounter->setWindow( window() ); } -void QskQuickItem::setDisabled( bool on ) +void QskItem::setDisabled( bool on ) { Inherited::setEnabled( !on ); } -void QskQuickItem::setHidden( bool on ) +void QskItem::setHidden( bool on ) { Inherited::setVisible( !on ); } -void QskQuickItem::show() +void QskItem::show() { Inherited::setVisible( true ); } -void QskQuickItem::hide() +void QskItem::hide() { Inherited::setVisible( false ); } -bool QskQuickItem::isVisibleTo( const QQuickItem* ancestor ) const +bool QskItem::isVisibleTo( const QQuickItem* ancestor ) const { return qskIsVisibleTo( this, ancestor ); } -bool QskQuickItem::isVisibleToParent() const +bool QskItem::isVisibleToParent() const { return d_func()->explicitVisible; } -void QskQuickItem::setGeometry( qreal x, qreal y, qreal width, qreal height ) +void QskItem::setGeometry( qreal x, qreal y, qreal width, qreal height ) { // QQuickItem does not even offer changing the geometry // in one call - what leads to 2 calls of the updateGeometry @@ -322,19 +385,19 @@ void QskQuickItem::setGeometry( qreal x, qreal y, qreal width, qreal height ) } } -QRectF QskQuickItem::rect() const +QRectF QskItem::rect() const { Q_D( const QQuickItem ); return QRectF( 0, 0, d->width, d->height ); } -QRectF QskQuickItem::geometry() const +QRectF QskItem::geometry() const { Q_D( const QQuickItem ); return QRectF( d->x, d->y, d->width, d->height ); } -void QskQuickItem::setTabFence( bool on ) +void QskItem::setTabFence( bool on ) { Q_D( QQuickItem ); if ( on != d->isTabFence ) @@ -344,14 +407,69 @@ void QskQuickItem::setTabFence( bool on ) } } -bool QskQuickItem::isTabFence() const +bool QskItem::isTabFence() const { return d_func()->isTabFence; } -void QskQuickItem::setPolishOnResize( bool on ) +void QskItem::setFocusPolicy( Qt::FocusPolicy policy ) { - Q_D( QskQuickItem ); + /* + Qt::FocusPolicy has always been there with widgets, got lost with + Qt/Quick and has been reintroduced with Qt/Quick Controls 2 ( QC2 ). + Unfortunately this was done by adding code on top instead of fixing + the foundation. + */ + Q_D( QskItem ); + if ( policy != d->focusPolicy ) + { + d->focusPolicy = ( policy & ~Qt::TabFocus ); + + const bool tabFocus = policy & Qt::TabFocus; + + if ( !tabFocus && window() ) + { + // removing the activeFocusItem from the focus tab chain is not possible + if ( window()->activeFocusItem() == this ) + { + if ( auto focusItem = nextItemInFocusChain( true ) ) + focusItem->setFocus( true ); + } + } + + Inherited::setActiveFocusOnTab( tabFocus ); + + Q_EMIT focusPolicyChanged( focusPolicy() ); + } +} + +Qt::FocusPolicy QskItem::focusPolicy() const +{ + uint policy = d_func()->focusPolicy; + if ( Inherited::activeFocusOnTab() ) + policy |= Qt::TabFocus; + + return static_cast< Qt::FocusPolicy >( policy ); +} + +void QskItem::setWheelEnabled( bool on ) +{ + Q_D( QskItem ); + if ( on != d->wheelEnabled ) + { + d->wheelEnabled = on; + Q_EMIT wheelEnabledChanged( on ); + } +} + +bool QskItem::isWheelEnabled() const +{ + return d_func()->wheelEnabled; +} + +void QskItem::setPolishOnResize( bool on ) +{ + Q_D( QskItem ); if ( on != d->polishOnResize ) { d->polishOnResize = on; @@ -361,12 +479,36 @@ void QskQuickItem::setPolishOnResize( bool on ) } } -bool QskQuickItem::polishOnResize() const +bool QskItem::polishOnResize() const { return d_func()->polishOnResize; } -bool QskQuickItem::layoutMirroring() const +void QskItem::setPolishOnParentResize( bool on ) +{ + Q_D( QskItem ); + if ( on != d->polishOnParentResize ) + { + d->polishOnParentResize = on; + + if ( parentItem() && qskParentListener ) + { + qskParentListener->update( parentItem() ); + + resetImplicitSize(); + polish(); + } + + Q_EMIT itemFlagsChanged(); + } +} + +bool QskItem::polishOnParentResize() const +{ + return d_func()->polishOnParentResize; +} + +bool QskItem::layoutMirroring() const { #if 1 /* @@ -380,7 +522,7 @@ bool QskQuickItem::layoutMirroring() const #endif } -void QskQuickItem::setLayoutMirroring( bool on, bool childrenInherit ) +void QskItem::setLayoutMirroring( bool on, bool childrenInherit ) { // Again we have to deal with an existing API made for QML, // that is weired for C++: LayoutMirroring/QQuickLayoutMirroringAttached @@ -408,7 +550,7 @@ void QskQuickItem::setLayoutMirroring( bool on, bool childrenInherit ) } } -void QskQuickItem::resetLayoutMirroring() +void QskItem::resetLayoutMirroring() { Q_D( QQuickItem ); @@ -420,27 +562,27 @@ void QskQuickItem::resetLayoutMirroring() } } -bool QskQuickItem::isPolishScheduled() const +bool QskItem::isPolishScheduled() const { return d_func()->polishScheduled; } -bool QskQuickItem::isUpdateNodeScheduled() const +bool QskItem::isUpdateNodeScheduled() const { - Q_D( const QskQuickItem ); + Q_D( const QskItem ); return ( d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask ) && ( d->flags & QQuickItem::ItemHasContents ); } -bool QskQuickItem::isInitiallyPainted() const +bool QskItem::isInitiallyPainted() const { return d_func()->initiallyPainted; } -bool QskQuickItem::maybeUnresized() const +bool QskItem::maybeUnresized() const { - Q_D( const QskQuickItem ); + Q_D( const QskItem ); if ( d->width <= 0.0 && d->height <= 0.0 ) { @@ -458,23 +600,23 @@ bool QskQuickItem::maybeUnresized() const return false; } -QskQuickItem::UpdateFlags QskQuickItem::updateFlags() const +QskItem::UpdateFlags QskItem::updateFlags() const { return UpdateFlags( d_func()->updateFlags ); } -void QskQuickItem::resetUpdateFlags() +void QskItem::resetUpdateFlags() { - Q_D( QskQuickItem ); + Q_D( QskItem ); // clear all bits in the mask d->updateFlagsMask = 0; - d->applyUpdateFlags( qskSetup->itemUpdateFlags() ); + d->applyUpdateFlags( QskSetup::updateFlags() ); } -void QskQuickItem::setUpdateFlag( UpdateFlag flag, bool on ) +void QskItem::setUpdateFlag( UpdateFlag flag, bool on ) { - Q_D( QskQuickItem ); + Q_D( QskItem ); d->updateFlagsMask |= flag; @@ -485,13 +627,13 @@ void QskQuickItem::setUpdateFlag( UpdateFlag flag, bool on ) } } -void QskQuickItem::resetUpdateFlag( UpdateFlag flag ) +void QskItem::resetUpdateFlag( UpdateFlag flag ) { - Q_D( QskQuickItem ); + Q_D( QskItem ); d->updateFlagsMask &= ~flag; - const bool on = qskSetup->testItemUpdateFlag( flag ); + const bool on = QskSetup::testUpdateFlag( flag ); if ( testUpdateFlag( flag ) != on ) { @@ -500,14 +642,14 @@ void QskQuickItem::resetUpdateFlag( UpdateFlag flag ) } } -bool QskQuickItem::testUpdateFlag( UpdateFlag flag ) const +bool QskItem::testUpdateFlag( UpdateFlag flag ) const { return d_func()->updateFlags & flag; } -void QskQuickItem::applyUpdateFlag( UpdateFlag flag, bool on ) +void QskItem::applyUpdateFlag( UpdateFlag flag, bool on ) { - Q_D( QskQuickItem ); + Q_D( QskItem ); if ( testUpdateFlag( flag ) == on ) return; @@ -519,7 +661,7 @@ void QskQuickItem::applyUpdateFlag( UpdateFlag flag, bool on ) switch ( flag ) { - case QskQuickItem::DeferredUpdate: + case QskItem::DeferredUpdate: { if ( on ) { @@ -533,14 +675,14 @@ void QskQuickItem::applyUpdateFlag( UpdateFlag flag, bool on ) break; } - case QskQuickItem::DeferredPolish: + case QskItem::DeferredPolish: { if ( !on && d->blockedPolish ) polish(); break; } - case QskQuickItem::DeferredLayout: + case QskItem::DeferredLayout: { if ( !on ) { @@ -557,14 +699,14 @@ void QskQuickItem::applyUpdateFlag( UpdateFlag flag, bool on ) break; } - case QskQuickItem::CleanupOnVisibility: + case QskItem::CleanupOnVisibility: { if ( on && !isVisible() ) d->cleanupNodes(); break; } - case QskQuickItem::DebugForceBackground: + case QskItem::DebugForceBackground: { // no need to mark it dirty if ( flags() & QQuickItem::ItemHasContents ) @@ -576,11 +718,11 @@ void QskQuickItem::applyUpdateFlag( UpdateFlag flag, bool on ) } } -void QskQuickItem::resetImplicitSize() +void QskItem::resetImplicitSize() { - Q_D( QskQuickItem ); + Q_D( QskItem ); - if ( d->updateFlags & QskQuickItem::DeferredLayout ) + if ( d->updateFlags & QskItem::DeferredLayout ) { d->blockedImplicitSize = true; d->layoutConstraintChanged(); @@ -591,7 +733,7 @@ void QskQuickItem::resetImplicitSize() } } -bool QskQuickItem::event( QEvent* event ) +bool QskItem::event( QEvent* event ) { const int eventType = event->type(); const bool hasContents = flags() & QQuickItem::ItemHasContents; @@ -624,33 +766,6 @@ bool QskQuickItem::event( QEvent* event ) changeEvent( event ); return true; } -#if 1 - /* - Font/Palette changes do not fit conceptually into the themeing - system of qskinny. Nevertheless we are handling the corresponding - events - whatever it is good for. - */ - case QEvent::FontChange: - { - resetImplicitSize(); - polish(); - - if ( hasContents ) - update(); - - changeEvent( event ); - return true; - } - case QEvent::PaletteChange: - { - if ( hasContents ) - update(); - - changeEvent( event ); - return true; - } -#endif - case QEvent::ReadOnlyChange: case QEvent::EnabledChange: case QEvent::LocaleChange: @@ -690,6 +805,42 @@ bool QskQuickItem::event( QEvent* event ) return true; } + break; + } + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + { + if ( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus ) + { + if ( QGuiApplication::styleHints()->setFocusOnTouchRelease() ) + { + if ( event->type() == QEvent::MouseButtonRelease ) + forceActiveFocus( Qt::MouseFocusReason ); + } + else + { + if ( event->type() == QEvent::MouseButtonPress ) + forceActiveFocus( Qt::MouseFocusReason ); + } + } + break; + } + case QEvent::Wheel: + { + if ( !isWheelEnabled() ) + { + /* + We block further processing of the event. This is in line + with not receiving any mouse event that have not been + explicitly enabled with setAcceptedMouseButtons(). + */ + event->ignore(); + return true; + } + + if ( ( focusPolicy() & Qt::WheelFocus ) == Qt::WheelFocus ) + forceActiveFocus( Qt::MouseFocusReason ); + break; } } @@ -697,19 +848,19 @@ bool QskQuickItem::event( QEvent* event ) return Inherited::event( event ); } -void QskQuickItem::windowChangeEvent( QskWindowChangeEvent* ) +void QskItem::windowChangeEvent( QskWindowChangeEvent* ) { } -void QskQuickItem::geometryChangeEvent( QskGeometryChangeEvent* ) +void QskItem::geometryChangeEvent( QskGeometryChangeEvent* ) { } -void QskQuickItem::changeEvent( QEvent* ) +void QskItem::changeEvent( QEvent* ) { } -void QskQuickItem::itemChange( QQuickItem::ItemChange change, +void QskItem::itemChange( QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& changeData ) { switch ( change ) @@ -718,8 +869,8 @@ void QskQuickItem::itemChange( QQuickItem::ItemChange change, { if ( changeData.window ) { - Q_D( const QskQuickItem ); - if ( d->updateFlags & QskQuickItem::DeferredUpdate ) + Q_D( const QskItem ); + if ( d->updateFlags & QskItem::DeferredUpdate ) qskFilterWindow( changeData.window ); } @@ -759,7 +910,7 @@ void QskQuickItem::itemChange( QQuickItem::ItemChange change, #if 1 if ( changeData.window == nullptr ) { - Q_D( QskQuickItem ); + Q_D( QskItem ); if( d->focus ) { @@ -789,7 +940,7 @@ void QskQuickItem::itemChange( QQuickItem::ItemChange change, } case QQuickItem::ItemVisibleHasChanged: { - Q_D( QskQuickItem ); + Q_D( QskItem ); #if 1 /* ~QQuickItem sends QQuickItem::ItemVisibleHasChanged recursively @@ -803,7 +954,7 @@ void QskQuickItem::itemChange( QQuickItem::ItemChange change, if ( d->blockedPolish ) polish(); - if ( d->updateFlags & QskQuickItem::DeferredUpdate ) + if ( d->updateFlags & QskItem::DeferredUpdate ) { if ( d->dirtyAttributes && ( d->flags & QQuickItem::ItemHasContents ) ) update(); @@ -811,7 +962,7 @@ void QskQuickItem::itemChange( QQuickItem::ItemChange change, } else { - if ( d->updateFlags & QskQuickItem::CleanupOnVisibility ) + if ( d->updateFlags & QskItem::CleanupOnVisibility ) d->cleanupNodes(); d->initiallyPainted = false; @@ -833,9 +984,13 @@ void QskQuickItem::itemChange( QQuickItem::ItemChange change, break; } - - case QQuickItem::ItemParentHasChanged: + { + if( polishOnParentResize() && qskParentListener ) + qskParentListener->update( parentItem() ); + + break; + } case QQuickItem::ItemChildAddedChange: case QQuickItem::ItemChildRemovedChange: { @@ -858,7 +1013,7 @@ void QskQuickItem::itemChange( QQuickItem::ItemChange change, #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) -void QskQuickItem::geometryChanged( +void QskItem::geometryChanged( const QRectF& newGeometry, const QRectF& oldGeometry ) { geometryChange( newGeometry, oldGeometry ); @@ -866,7 +1021,7 @@ void QskQuickItem::geometryChanged( #endif -void QskQuickItem::geometryChange( +void QskItem::geometryChange( const QRectF& newGeometry, const QRectF& oldGeometry ) { #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) @@ -875,7 +1030,7 @@ void QskQuickItem::geometryChange( Inherited::geometryChange( newGeometry, oldGeometry ); #endif - Q_D( const QskQuickItem ); + Q_D( const QskItem ); if ( !d->polishScheduled && d->polishOnResize ) { if ( newGeometry.size() != oldGeometry.size() ) @@ -886,30 +1041,30 @@ void QskQuickItem::geometryChange( QCoreApplication::sendEvent( this, &event ); } -void QskQuickItem::mouseUngrabEvent() +void QskItem::mouseUngrabEvent() { Inherited::mouseUngrabEvent(); } -void QskQuickItem::touchUngrabEvent() +void QskItem::touchUngrabEvent() { Inherited::touchUngrabEvent(); } #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) -void QskQuickItem::windowDeactivateEvent() +void QskItem::windowDeactivateEvent() { Inherited::windowDeactivateEvent(); } #endif -void QskQuickItem::updatePolish() +void QskItem::updatePolish() { - Q_D( QskQuickItem ); + Q_D( QskItem ); - if ( d->updateFlags & QskQuickItem::DeferredPolish ) + if ( d->updateFlags & QskItem::DeferredPolish ) { if ( !isVisible() ) { @@ -933,21 +1088,21 @@ void QskQuickItem::updatePolish() updateItemPolish(); } -void QskQuickItem::aboutToShow() +void QskItem::aboutToShow() { } -void QskQuickItem::updateItemPolish() +void QskItem::updateItemPolish() { } -QSGNode* QskQuickItem::updatePaintNode( QSGNode* node, UpdatePaintNodeData* data ) +QSGNode* QskItem::updatePaintNode( QSGNode* node, UpdatePaintNodeData* data ) { Q_UNUSED( data ) - Q_D( QskQuickItem ); + Q_D( QskItem ); - Q_ASSERT( isVisible() || !( d->updateFlags & QskQuickItem::DeferredUpdate ) ); + Q_ASSERT( isVisible() || !( d->updateFlags & QskItem::DeferredUpdate ) ); d->initiallyPainted = true; @@ -964,7 +1119,7 @@ QSGNode* QskQuickItem::updatePaintNode( QSGNode* node, UpdatePaintNodeData* data it has already been allocated. When deleting it we have a dangling pointer. instead of the new one. - To avoid creashes for the second situation we manually clear d->paintNode. + To avoid creashes for the second situation we manually clear d->paintNode. */ d->paintNode = nullptr; #endif @@ -975,9 +1130,15 @@ QSGNode* QskQuickItem::updatePaintNode( QSGNode* node, UpdatePaintNodeData* data return updateItemPaintNode( node ); } -QSGNode* QskQuickItem::updateItemPaintNode( QSGNode* node ) +QSGNode* QskItem::updateItemPaintNode( QSGNode* node ) { return node; } -#include "moc_QskQuickItem.cpp" +void qskUpdateItemFlags() +{ + if ( qskRegistry ) + qskRegistry->updateItemFlags(); +} + +#include "moc_QskItem.cpp" diff --git a/src/controls/QskQuickItem.h b/src/controls/QskItem.h similarity index 75% rename from src/controls/QskQuickItem.h rename to src/controls/QskItem.h index 02f1807f..568d7642 100644 --- a/src/controls/QskQuickItem.h +++ b/src/controls/QskItem.h @@ -3,17 +3,17 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#ifndef QSK_QUICK_ITEM_H -#define QSK_QUICK_ITEM_H +#ifndef QSK_ITEM_H +#define QSK_ITEM_H #include "QskGlobal.h" #include -class QskQuickItemPrivate; +class QskItemPrivate; class QskGeometryChangeEvent; class QskWindowChangeEvent; -class QSK_EXPORT QskQuickItem : public QQuickItem +class QSK_EXPORT QskItem : public QQuickItem { Q_OBJECT @@ -26,6 +26,15 @@ class QSK_EXPORT QskQuickItem : public QQuickItem Q_PROPERTY( bool polishOnResize READ polishOnResize WRITE setPolishOnResize NOTIFY itemFlagsChanged FINAL ) + Q_PROPERTY( bool polishOnParentResize READ polishOnParentResize + WRITE setPolishOnParentResize NOTIFY itemFlagsChanged FINAL ) + + Q_PROPERTY( Qt::FocusPolicy focusPolicy READ focusPolicy + WRITE setFocusPolicy NOTIFY focusPolicyChanged ) + + Q_PROPERTY( bool wheelEnabled READ isWheelEnabled + WRITE setWheelEnabled NOTIFY wheelEnabledChanged ) + Q_PROPERTY( bool visibleToParent READ isVisibleToParent ) Q_PROPERTY( bool hasChildItems READ hasChildItems ) Q_PROPERTY( bool initiallyPainted READ isInitiallyPainted ) @@ -50,7 +59,7 @@ class QSK_EXPORT QskQuickItem : public QQuickItem Q_ENUM( UpdateFlag ) Q_DECLARE_FLAGS( UpdateFlags, UpdateFlag ) - ~QskQuickItem() override; + ~QskItem() override; const char* className() const; @@ -75,9 +84,18 @@ class QSK_EXPORT QskQuickItem : public QQuickItem void setPolishOnResize( bool ); bool polishOnResize() const; + void setPolishOnParentResize( bool ); + bool polishOnParentResize() const; + + void setFocusPolicy( Qt::FocusPolicy ); + Qt::FocusPolicy focusPolicy() const; + void setTabFence( bool ); bool isTabFence() const; + void setWheelEnabled( bool ); + bool isWheelEnabled() const; + void setLayoutMirroring( bool on, bool childrenInherit = false ); void resetLayoutMirroring(); bool layoutMirroring() const; @@ -85,9 +103,9 @@ class QSK_EXPORT QskQuickItem : public QQuickItem void resetUpdateFlags(); UpdateFlags updateFlags() const; - Q_INVOKABLE void setUpdateFlag( UpdateFlag, bool on = true ); - Q_INVOKABLE void resetUpdateFlag( UpdateFlag ); - Q_INVOKABLE bool testUpdateFlag( UpdateFlag ) const; + void setUpdateFlag( UpdateFlag, bool on = true ); + void resetUpdateFlag( UpdateFlag ); + bool testUpdateFlag( UpdateFlag ) const; void classBegin() override; void componentComplete() override; @@ -100,6 +118,9 @@ class QSK_EXPORT QskQuickItem : public QQuickItem bool maybeUnresized() const; Q_SIGNALS: + void wheelEnabledChanged( bool ); + void focusPolicyChanged( Qt::FocusPolicy ); + void itemFlagsChanged(); void updateFlagsChanged( UpdateFlags ); @@ -121,7 +142,7 @@ class QSK_EXPORT QskQuickItem : public QQuickItem #endif protected: - QskQuickItem( QskQuickItemPrivate&, QQuickItem* = nullptr ); + QskItem( QskItemPrivate&, QQuickItem* = nullptr ); bool event( QEvent* ) override; @@ -158,6 +179,7 @@ class QSK_EXPORT QskQuickItem : public QQuickItem */ void childrenRect() = delete; + void setActiveFocusOnTab( bool ) = delete; // use setFocusPolicy void applyUpdateFlag( UpdateFlag, bool on ); QSGNode* updatePaintNode( QSGNode*, UpdatePaintNodeData* ) override final; @@ -166,40 +188,40 @@ class QSK_EXPORT QskQuickItem : public QQuickItem void updatePolish() override final; virtual void updateItemPolish(); - Q_DECLARE_PRIVATE( QskQuickItem ) + Q_DECLARE_PRIVATE( QskItem ) }; -inline bool QskQuickItem::hasChildItems() const +inline bool QskItem::hasChildItems() const { return !childItems().isEmpty(); } -inline void QskQuickItem::setGeometry( const QPointF& pos, const QSizeF& size ) +inline void QskItem::setGeometry( const QPointF& pos, const QSizeF& size ) { setGeometry( pos.x(), pos.y(), size.width(), size.height() ); } -inline void QskQuickItem::setGeometry( const QRectF& rect ) +inline void QskItem::setGeometry( const QRectF& rect ) { setGeometry( rect.x(), rect.y(), rect.width(), rect.height() ); } -inline void QskQuickItem::setPosition( qreal x, qreal y ) +inline void QskItem::setPosition( qreal x, qreal y ) { QQuickItem::setPosition( QPointF( x, y ) ); } -inline void QskQuickItem::setSize( qreal width, qreal height ) +inline void QskItem::setSize( qreal width, qreal height ) { QQuickItem::setSize( QSizeF( width, height ) ); } -inline QSizeF QskQuickItem::implicitSize() const +inline QSizeF QskItem::implicitSize() const { return QSizeF( implicitWidth(), implicitHeight() ); } -Q_DECLARE_OPERATORS_FOR_FLAGS( QskQuickItem::UpdateFlags ) -Q_DECLARE_METATYPE( QskQuickItem::UpdateFlags ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QskItem::UpdateFlags ) +Q_DECLARE_METATYPE( QskItem::UpdateFlags ) #endif diff --git a/src/controls/QskQuickItemPrivate.cpp b/src/controls/QskItemPrivate.cpp similarity index 82% rename from src/controls/QskQuickItemPrivate.cpp rename to src/controls/QskItemPrivate.cpp index bdfcb19e..5ff79737 100644 --- a/src/controls/QskQuickItemPrivate.cpp +++ b/src/controls/QskItemPrivate.cpp @@ -3,7 +3,7 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#include "QskQuickItemPrivate.h" +#include "QskItemPrivate.h" #include "QskTreeNode.h" #include "QskSetup.h" @@ -13,16 +13,19 @@ static inline void qskSendEventTo( QObject* object, QEvent::Type type ) QCoreApplication::sendEvent( object, &event ); } -QskQuickItemPrivate::QskQuickItemPrivate() - : updateFlags( qskSetup->itemUpdateFlags() ) +QskItemPrivate::QskItemPrivate() + : updateFlags( QskSetup::updateFlags() ) , updateFlagsMask( 0 ) , polishOnResize( false ) + , polishOnParentResize( false ) , blockedPolish( false ) , blockedImplicitSize( true ) , clearPreviousNodes( false ) , initiallyPainted( false ) + , wheelEnabled( false ) + , focusPolicy( Qt::NoFocus ) { - if ( updateFlags & QskQuickItem::DeferredLayout ) + if ( updateFlags & QskItem::DeferredLayout ) { /* In general the geometry of an item should be the job of @@ -47,16 +50,16 @@ QskQuickItemPrivate::QskQuickItemPrivate() } } -QskQuickItemPrivate::~QskQuickItemPrivate() +QskItemPrivate::~QskItemPrivate() { } -void QskQuickItemPrivate::mirrorChange() +void QskItemPrivate::mirrorChange() { qskSendEventTo( q_func(), QEvent::LayoutDirectionChange ); } -void QskQuickItemPrivate::applyUpdateFlags( QskQuickItem::UpdateFlags flags ) +void QskItemPrivate::applyUpdateFlags( QskItem::UpdateFlags flags ) { /* Replace all flags, that have not been set explicitely by the @@ -68,12 +71,12 @@ void QskQuickItemPrivate::applyUpdateFlags( QskQuickItem::UpdateFlags flags ) if ( flags == oldFlags ) return; - Q_Q( QskQuickItem ); + Q_Q( QskItem ); Q_STATIC_ASSERT( sizeof( updateFlags ) == 1 ); for ( uint i = 0; i < 8; i++ ) { - const auto flag = static_cast< QskQuickItem::UpdateFlag >( 1 << i ); + const auto flag = static_cast< QskItem::UpdateFlag >( 1 << i ); if ( !( this->updateFlagsMask & flag ) ) q->applyUpdateFlag( flag, flags & flag ); @@ -83,40 +86,40 @@ void QskQuickItemPrivate::applyUpdateFlags( QskQuickItem::UpdateFlags flags ) Q_EMIT q->updateFlagsChanged( q->updateFlags() ); } -void QskQuickItemPrivate::layoutConstraintChanged() +void QskItemPrivate::layoutConstraintChanged() { if ( auto item = q_func()->parentItem() ) qskSendEventTo( item, QEvent::LayoutRequest ); } -void QskQuickItemPrivate::implicitSizeChanged() +void QskItemPrivate::implicitSizeChanged() { layoutConstraintChanged(); } -qreal QskQuickItemPrivate::getImplicitWidth() const +qreal QskItemPrivate::getImplicitWidth() const { if ( blockedImplicitSize ) { - auto that = const_cast< QskQuickItemPrivate* >( this ); + auto that = const_cast< QskItemPrivate* >( this ); that->updateImplicitSize( false ); } return implicitWidth; } -qreal QskQuickItemPrivate::getImplicitHeight() const +qreal QskItemPrivate::getImplicitHeight() const { if ( blockedImplicitSize ) { - auto that = const_cast< QskQuickItemPrivate* >( this ); + auto that = const_cast< QskItemPrivate* >( this ); that->updateImplicitSize( false ); } return implicitHeight; } -void QskQuickItemPrivate::updateImplicitSize( bool doNotify ) +void QskItemPrivate::updateImplicitSize( bool doNotify ) { blockedImplicitSize = false; @@ -124,7 +127,7 @@ void QskQuickItemPrivate::updateImplicitSize( bool doNotify ) setImplicitSize( hint.width(), hint.height(), doNotify ); } -void QskQuickItemPrivate::setImplicitSize( qreal w, qreal h, bool doNotify ) +void QskItemPrivate::setImplicitSize( qreal w, qreal h, bool doNotify ) { const bool doWidth = ( w != implicitWidth ); const bool doHeight = ( h != implicitHeight ); @@ -160,7 +163,7 @@ void QskQuickItemPrivate::setImplicitSize( qreal w, qreal h, bool doNotify ) const QRectF oldRect( x, y, oldWidth, oldHeight ); const QRectF newRect( x, y, width, height ); - Q_Q( QskQuickItem ); + Q_Q( QskItem ); q->geometryChange( newRect, oldRect ); } } @@ -179,7 +182,7 @@ void QskQuickItemPrivate::setImplicitSize( qreal w, qreal h, bool doNotify ) In case of the application interferes by calling setImplicitWidth or setImplicitHeight manually: */ -void QskQuickItemPrivate::implicitWidthChanged() +void QskItemPrivate::implicitWidthChanged() { Inherited::implicitWidthChanged(); @@ -187,7 +190,7 @@ void QskQuickItemPrivate::implicitWidthChanged() implicitSizeChanged(); } -void QskQuickItemPrivate::implicitHeightChanged() +void QskItemPrivate::implicitHeightChanged() { Inherited::implicitWidthChanged(); @@ -195,7 +198,7 @@ void QskQuickItemPrivate::implicitHeightChanged() implicitSizeChanged(); } -void QskQuickItemPrivate::cleanupNodes() +void QskItemPrivate::cleanupNodes() { if ( itemNodeInstance == nullptr ) return; @@ -241,7 +244,7 @@ void QskQuickItemPrivate::cleanupNodes() } } -QSGTransformNode* QskQuickItemPrivate::createTransformNode() +QSGTransformNode* QskItemPrivate::createTransformNode() { return new QskItemNode(); } diff --git a/src/controls/QskQuickItemPrivate.h b/src/controls/QskItemPrivate.h similarity index 78% rename from src/controls/QskQuickItemPrivate.h rename to src/controls/QskItemPrivate.h index 615b3df3..db5b0b96 100644 --- a/src/controls/QskQuickItemPrivate.h +++ b/src/controls/QskItemPrivate.h @@ -3,26 +3,26 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#ifndef QSK_QUICK_ITEM_PRIVATE_H -#define QSK_QUICK_ITEM_PRIVATE_H +#ifndef QSK_ITEM_PRIVATE_H +#define QSK_ITEM_PRIVATE_H #include "QskGlobal.h" -#include "QskQuickItem.h" +#include "QskItem.h" QSK_QT_PRIVATE_BEGIN #include QSK_QT_PRIVATE_END -class QskQuickItemPrivate : public QQuickItemPrivate +class QskItemPrivate : public QQuickItemPrivate { using Inherited = QQuickItemPrivate; protected: - QskQuickItemPrivate(); - ~QskQuickItemPrivate() override; + QskItemPrivate(); + ~QskItemPrivate() override; public: - void applyUpdateFlags( QskQuickItem::UpdateFlags ); + void applyUpdateFlags( QskItem::UpdateFlags ); QSGTransformNode* createTransformNode() override; protected: @@ -45,18 +45,22 @@ class QskQuickItemPrivate : public QQuickItemPrivate virtual QSizeF implicitSizeHint() const = 0; private: - Q_DECLARE_PUBLIC( QskQuickItem ) + Q_DECLARE_PUBLIC( QskItem ) quint8 updateFlags; quint8 updateFlagsMask; bool polishOnResize : 1; + bool polishOnParentResize : 1; bool blockedPolish : 1; bool blockedImplicitSize : 1; bool clearPreviousNodes : 1; bool initiallyPainted : 1; + bool wheelEnabled : 1; + + uint focusPolicy : 4; }; #endif diff --git a/src/controls/QskListView.h b/src/controls/QskListView.h index 82a7cfe4..4dc15a11 100644 --- a/src/controls/QskListView.h +++ b/src/controls/QskListView.h @@ -17,7 +17,7 @@ class QSK_EXPORT QskListView : public QskScrollView WRITE setSelectionMode NOTIFY selectionModeChanged FINAL ) Q_PROPERTY( int selectedRow READ selectedRow - WRITE setSelectedRow NOTIFY selectedRowChanged FINAL ) + WRITE setSelectedRow NOTIFY selectedRowChanged USER true FINAL ) Q_PROPERTY( QskTextOptions textOptions READ textOptions WRITE setTextOptions RESET resetTextOptions diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index e690332b..9acf9219 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -53,6 +53,8 @@ class QskMenu::PrivateData int triggeredIndex = -1; int currentIndex = -1; + + bool wrapping = true; bool isPressed = false; }; @@ -105,6 +107,20 @@ QRectF QskMenu::clipRect() const return Inherited::clipRect(); } +bool QskMenu::isWrapping() const +{ + return m_data->wrapping; +} + +void QskMenu::setWrapping( bool on ) +{ + if ( m_data->wrapping != on ) + { + m_data->wrapping = on; + Q_EMIT wrappingChanged( on ); + } +} + #if 1 // has no effect as we do not offer submenus yet. TODO ... @@ -422,6 +438,9 @@ void QskMenu::traverse( int steps ) int action1 = qskActionIndex( actions, m_data->currentIndex ); int action2 = action1 + steps; + if ( !m_data->wrapping ) + action2 = qBound( 0, action2, count - 1 ); + // when cycling we want to slide in int index1; diff --git a/src/controls/QskMenu.h b/src/controls/QskMenu.h index 88670ec6..cd6f3dc6 100644 --- a/src/controls/QskMenu.h +++ b/src/controls/QskMenu.h @@ -23,6 +23,9 @@ class QSK_EXPORT QskMenu : public QskPopup Q_PROPERTY( bool cascading READ isCascading WRITE setCascading RESET resetCascading NOTIFY cascadingChanged ) + Q_PROPERTY( bool wrapping READ isWrapping + WRITE setWrapping NOTIFY wrappingChanged ) + Q_PROPERTY( QVector< QskLabelData > options READ options WRITE setOptions NOTIFY optionsChanged ) @@ -47,6 +50,9 @@ class QSK_EXPORT QskMenu : public QskPopup void setCascading( bool ); void resetCascading(); + bool isWrapping() const; + void setWrapping( bool ); + void setOrigin( const QPointF& ); QPointF origin() const; @@ -87,6 +93,7 @@ class QSK_EXPORT QskMenu : public QskPopup Q_INVOKABLE int exec(); Q_SIGNALS: + void wrappingChanged( bool ); void cascadingChanged( bool ); void originChanged( const QPointF& ); diff --git a/src/controls/QskProgressBarSkinlet.cpp b/src/controls/QskProgressBarSkinlet.cpp index 74fe9e69..225fb44c 100644 --- a/src/controls/QskProgressBarSkinlet.cpp +++ b/src/controls/QskProgressBarSkinlet.cpp @@ -15,13 +15,13 @@ using Q = QskProgressBar; namespace { - QskIntervalF qskFillInterval( const Q* bar ) + QskIntervalF qskFillInterval( const QskProgressIndicator* indicator ) { qreal pos1, pos2; - if ( bar->isIndeterminate() ) + if ( indicator->isIndeterminate() ) { - const auto pos = bar->positionHint( QskProgressIndicator::Fill ); + const auto pos = indicator->positionHint( QskProgressIndicator::Fill ); static const QEasingCurve curve( QEasingCurve::InOutCubic ); @@ -32,10 +32,11 @@ namespace } else { - pos1 = bar->valueAsRatio( bar->origin() ); - pos2 = bar->valueAsRatio( bar->value() ); + pos1 = indicator->valueAsRatio( indicator->origin() ); + pos2 = indicator->valueAsRatio( indicator->value() ); } + auto bar = static_cast< const QskProgressBar* >( indicator ); if( bar->orientation() == Qt::Horizontal ) { if ( bar->layoutMirroring() ) @@ -107,11 +108,11 @@ QSGNode* QskProgressBarSkinlet::updateFillNode( const auto subControl = Q::Fill; - const auto rect = bar->subControlRect( subControl ); + const auto rect = indicator->subControlRect( subControl ); if ( rect.isEmpty() ) return nullptr; - auto gradient = bar->gradientHint( subControl ); + auto gradient = indicator->gradientHint( subControl ); if ( !gradient.isVisible() ) return nullptr; diff --git a/src/controls/QskProgressRing.cpp b/src/controls/QskProgressRing.cpp index dcc1d8d6..af6c414b 100644 --- a/src/controls/QskProgressRing.cpp +++ b/src/controls/QskProgressRing.cpp @@ -4,7 +4,6 @@ *****************************************************************************/ #include "QskProgressRing.h" - #include "QskIntervalF.h" QSK_SUBCONTROL( QskProgressRing, Groove ) @@ -20,6 +19,8 @@ QskProgressRing::QskProgressRing( qreal min, qreal max, QQuickItem* parent ) : Inherited( min, max, parent ) , m_data( new PrivateData ) { + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + m_data->size = NormalSize; setSubcontrolProxy( Inherited::Groove, Groove ); diff --git a/src/controls/QskProgressRingSkinlet.cpp b/src/controls/QskProgressRingSkinlet.cpp index 86e90329..167cb861 100644 --- a/src/controls/QskProgressRingSkinlet.cpp +++ b/src/controls/QskProgressRingSkinlet.cpp @@ -10,31 +10,6 @@ using Q = QskProgressRing; -namespace -{ - QskIntervalF qskFillInterval( const Q* ring ) - { - qreal pos1, pos2; - - if ( ring->isIndeterminate() ) - { - const auto pos = ring->positionHint( QskProgressIndicator::Fill ); - - pos1 = pos2 = pos; - } - else - { - pos1 = ring->valueAsRatio( ring->origin() ); - pos2 = ring->valueAsRatio( ring->value() ); - } - - if ( pos1 > pos2 ) - std::swap( pos1, pos2 ); - - return QskIntervalF( pos1, pos2 ); - } -} - QskProgressRingSkinlet::QskProgressRingSkinlet( QskSkin* skin ) : Inherited( skin ) { @@ -48,27 +23,8 @@ QRectF QskProgressRingSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { - const auto ring = static_cast< const Q* >( skinnable ); - if( subControl == Q::Groove || subControl == Q::Fill ) - { - auto rect = contentsRect; - const auto size = ring->strutSizeHint( Q::Fill ); - - if( ring->layoutMirroring() ) - { - rect.setLeft( rect.right() - size.width() ); - } - else - { - rect.setWidth( size.width() ); - } - - rect.setTop( rect.top() + 0.5 * ( rect.height() - size.height() ) ); - rect.setHeight( size.height() ); - - return rect; - } + return contentsRect; return Inherited::subControlRect( skinnable, contentsRect, subControl ); } @@ -90,36 +46,74 @@ QSGNode* QskProgressRingSkinlet::updateFillNode( if ( rect.isEmpty() ) return nullptr; + const auto metrics = ring->arcMetricsHint( subControl ); + if ( metrics.isNull() ) + return nullptr; + auto gradient = ring->gradientHint( subControl ); if ( !gradient.isVisible() ) return nullptr; + const auto intv = fillInterval( ring ); + if ( ( gradient.type() == QskGradient::Stops ) && !gradient.isMonochrome() ) { - const auto center = rect.center(); - const auto arcMetrics = ring->arcMetricsHint( Q::Fill ); - gradient.setConicDirection( center.x(), center.y(), arcMetrics.startAngle(), arcMetrics.spanAngle() ); + const auto stops = qskExtractedGradientStops( gradient.stops(), + intv.lowerBound(), intv.upperBound() ); + + gradient.setStops( stops ); + + if ( metrics.spanAngle() < 0.0 ) + gradient.reverse(); } - const auto interval = qskFillInterval( ring ); - const auto arcMetrics = ring->arcMetricsHint( subControl ); - - const auto startAngle = arcMetrics.startAngle() + interval.lowerBound() * arcMetrics.spanAngle(); - const auto spanAngle = interval.upperBound() * arcMetrics.spanAngle(); + const auto startAngle = metrics.startAngle() + intv.lowerBound() * metrics.spanAngle(); + const auto spanAngle = intv.upperBound() * metrics.spanAngle(); return updateArcNode( ring, node, rect, gradient, startAngle, spanAngle, subControl ); } QSizeF QskProgressRingSkinlet::sizeHint( const QskSkinnable* skinnable, - Qt::SizeHint which, const QSizeF& ) const + Qt::SizeHint which, const QSizeF& constraint ) const { if ( which != Qt::PreferredSize ) return QSizeF(); - const auto ring = static_cast< const Q* >( skinnable ); + auto hint = skinnable->strutSizeHint( Q::Fill ); + hint = hint.expandedTo( skinnable->strutSizeHint( Q::Groove ) ); - const auto r = ring->strutSizeHint( Q::Fill ); - return r; + if ( !constraint.isEmpty() ) + { + const qreal aspectRatio = hint.isEmpty() ? 1.0 : hint.width() / hint.height(); + + if ( constraint.width() >= 0.0 ) + hint.setHeight( constraint.width() / aspectRatio ); + else + hint.setWidth( constraint.height() * aspectRatio ); + } + + return hint; +} + +QskIntervalF QskProgressRingSkinlet::fillInterval( + const QskProgressIndicator* indicator ) const +{ + qreal pos1, pos2; + + if ( indicator->isIndeterminate() ) + { + pos1 = pos2 = indicator->positionHint( QskProgressIndicator::Fill ); + } + else + { + pos1 = indicator->valueAsRatio( indicator->origin() ); + pos2 = indicator->valueAsRatio( indicator->value() ); + } + + if ( pos1 > pos2 ) + std::swap( pos1, pos2 ); + + return QskIntervalF( pos1, pos2 ); } #include "moc_QskProgressRingSkinlet.cpp" diff --git a/src/controls/QskProgressRingSkinlet.h b/src/controls/QskProgressRingSkinlet.h index 767930b1..c5dc815b 100644 --- a/src/controls/QskProgressRingSkinlet.h +++ b/src/controls/QskProgressRingSkinlet.h @@ -8,7 +8,7 @@ #include "QskProgressIndicatorSkinlet.h" -class QskProgressRing; +class QskIntervalF; class QSK_EXPORT QskProgressRingSkinlet : public QskProgressIndicatorSkinlet { @@ -29,6 +29,8 @@ class QSK_EXPORT QskProgressRingSkinlet : public QskProgressIndicatorSkinlet protected: QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const override; QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; + + QskIntervalF fillInterval( const QskProgressIndicator* ) const; }; #endif diff --git a/src/controls/QskPushButton.cpp b/src/controls/QskPushButton.cpp index f6f8370c..3825458d 100644 --- a/src/controls/QskPushButton.cpp +++ b/src/controls/QskPushButton.cpp @@ -87,6 +87,10 @@ void QskPushButton::setEmphasis( Emphasis emphasis ) if ( emphasis != m_data->emphasis ) { m_data->emphasis = emphasis; + + resetImplicitSize(); + update(); + Q_EMIT emphasisChanged( emphasis ); } } diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index 3c153188..9b97db41 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -65,7 +65,7 @@ bool qskIsItemInDestructor( const QQuickItem* item ) return d->inDestructor; #else /* - QskQuickItem sets componentComplete to false in its destructor, + QskItem sets componentComplete to false in its destructor, but for other items we will will return the wrong information */ return !d->componentComplete; diff --git a/src/controls/QskRadioBox.cpp b/src/controls/QskRadioBox.cpp index 482e3704..63339888 100644 --- a/src/controls/QskRadioBox.cpp +++ b/src/controls/QskRadioBox.cpp @@ -33,7 +33,7 @@ QskRadioBox::QskRadioBox( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Minimum ); + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); setFocusPolicy( Qt::StrongFocus ); setAcceptedMouseButtons( Qt::LeftButton ); diff --git a/src/controls/QskSetup.cpp b/src/controls/QskSetup.cpp index 9ba906d6..e0ffb30e 100644 --- a/src/controls/QskSetup.cpp +++ b/src/controls/QskSetup.cpp @@ -4,233 +4,99 @@ *****************************************************************************/ #include "QskSetup.h" -#include "QskControl.h" -#include "QskControlPrivate.h" -#include "QskGraphicProviderMap.h" -#include "QskSkinManager.h" -#include "QskSkin.h" -#include "QskWindow.h" -#include -#include +extern void qskUpdateItemFlags(); -QskSetup* QskSetup::s_instance = nullptr; - -static inline bool qskHasEnvironment( const char* env ) +namespace { - bool ok; - - const int value = qEnvironmentVariableIntValue( env, &ok ); - if ( ok ) - return value != 0; - - // All other strings are true, apart from "false" - auto result = qgetenv( env ); - return !result.isEmpty() && result != "false"; -} - -static inline const QskQuickItem::UpdateFlags qskEnvironmentUpdateFlags() -{ - QskQuickItem::UpdateFlags flags; - - if ( qskHasEnvironment( "QSK_PREFER_RASTER" ) ) - flags |= QskQuickItem::PreferRasterForTextures; - - if ( qskHasEnvironment( "QSK_FORCE_BACKGROUND" ) ) - flags |= QskQuickItem::DebugForceBackground; - - return flags; -} - -static inline QskQuickItem::UpdateFlags qskDefaultUpdateFlags() -{ - static QskQuickItem::UpdateFlags flags; - - if ( flags == 0 ) + inline bool hasEnvironment( const char* env ) { - flags |= QskQuickItem::DeferredUpdate; - flags |= QskQuickItem::DeferredPolish; - flags |= QskQuickItem::DeferredLayout; - flags |= QskQuickItem::CleanupOnVisibility; - flags |= qskEnvironmentUpdateFlags(); + bool ok; + + const int value = qEnvironmentVariableIntValue( env, &ok ); + if ( ok ) + return value != 0; + + // All other strings are true, apart from "false" + auto result = qgetenv( env ); + return !result.isEmpty() && result != "false"; } - return flags; -} - -static void qskApplicationHook() -{ - QskSetup::setup(); - qAddPostRoutine( QskSetup::cleanup ); -} - -static void qskApplicationFilter() -{ - QCoreApplication::instance()->installEventFilter( QskSetup::instance() ); -} - -Q_CONSTRUCTOR_FUNCTION( qskApplicationHook ) -Q_COREAPP_STARTUP_FUNCTION( qskApplicationFilter ) - -class QskSetup::PrivateData -{ - public: - PrivateData() - : itemUpdateFlags( qskDefaultUpdateFlags() ) + inline const QskItem::UpdateFlags environmentUpdateFlags() { + QskItem::UpdateFlags flags; + + if ( !hasEnvironment( "QSK_PREFER_FBO_PAINTING" ) ) + flags |= QskItem::PreferRasterForTextures; + + if ( hasEnvironment( "QSK_FORCE_BACKGROUND" ) ) + flags |= QskItem::DebugForceBackground; + + return flags; } - QskGraphicProviderMap graphicProviders; - QskQuickItem::UpdateFlags itemUpdateFlags; -}; - -QskSetup::QskSetup() - : m_data( new PrivateData() ) -{ -} - -QskSetup::~QskSetup() -{ - s_instance = nullptr; // we might be destroyed from Qml ! -} - -void QskSetup::setup() -{ - if ( s_instance == nullptr ) - s_instance = new QskSetup(); -} - -void QskSetup::cleanup() -{ - delete s_instance; - s_instance = nullptr; -} - -void QskSetup::setItemUpdateFlags( QskQuickItem::UpdateFlags flags ) -{ - if ( m_data->itemUpdateFlags != flags ) + inline QskItem::UpdateFlags defaultUpdateFlags() { - m_data->itemUpdateFlags = flags; - Q_EMIT itemUpdateFlagsChanged(); + static QskItem::UpdateFlags flags; + + if ( flags == 0 ) + { + flags |= QskItem::DeferredUpdate; + flags |= QskItem::DeferredPolish; + flags |= QskItem::DeferredLayout; + flags |= QskItem::CleanupOnVisibility; + flags |= environmentUpdateFlags(); + } + + return flags; + } + + inline void propagateFlags() + { + qskUpdateItemFlags(); } } -QskQuickItem::UpdateFlags QskSetup::itemUpdateFlags() const +static QskItem::UpdateFlags qskUpdateFlags = defaultUpdateFlags(); + +void QskSetup::setUpdateFlags( QskItem::UpdateFlags flags ) { - return m_data->itemUpdateFlags; + if ( qskUpdateFlags != flags ) + { + qskUpdateFlags = flags; + propagateFlags(); + } } -void QskSetup::resetItemUpdateFlags() +QskItem::UpdateFlags QskSetup::updateFlags() { - setItemUpdateFlags( qskDefaultUpdateFlags() ); + return qskUpdateFlags; } -void QskSetup::setItemUpdateFlag( QskQuickItem::UpdateFlag flag, bool on ) +void QskSetup::resetUpdateFlags() { - if ( m_data->itemUpdateFlags.testFlag( flag ) != on ) + setUpdateFlags( defaultUpdateFlags() ); +} + +void QskSetup::setUpdateFlag( QskItem::UpdateFlag flag, bool on ) +{ + if ( qskUpdateFlags.testFlag( flag ) != on ) { if ( on ) - m_data->itemUpdateFlags |= flag; + qskUpdateFlags |= flag; else - m_data->itemUpdateFlags &= ~flag; + qskUpdateFlags &= ~flag; - Q_EMIT itemUpdateFlagsChanged(); + propagateFlags(); } } -void QskSetup::resetItemUpdateFlag( QskQuickItem::UpdateFlag flag ) +void QskSetup::resetUpdateFlag( QskItem::UpdateFlag flag ) { - setItemUpdateFlag( flag, flag & qskDefaultUpdateFlags() ); + setUpdateFlag( flag, flag & defaultUpdateFlags() ); } -bool QskSetup::testItemUpdateFlag( QskQuickItem::UpdateFlag flag ) +bool QskSetup::testUpdateFlag( QskItem::UpdateFlag flag ) { - return m_data->itemUpdateFlags.testFlag( flag ); + return qskUpdateFlags.testFlag( flag ); } - -void QskSetup::addGraphicProvider( const QString& providerId, QskGraphicProvider* provider ) -{ - m_data->graphicProviders.insert( providerId, provider ); -} - -QskGraphicProvider* QskSetup::graphicProvider( const QString& providerId ) const -{ - if ( auto skin = qskSkinManager->skin() ) - { - if ( auto provider = skin->graphicProvider( providerId ) ) - return provider; - } - - return m_data->graphicProviders.provider( providerId ); -} - -bool QskSetup::eventFilter( QObject* object, QEvent* event ) -{ - if ( auto control = qskControlCast( object ) ) - { - /* - Qt::FocusPolicy has always been there with widgets, got lost with - Qt/Quick and has been reintroduced with Qt/Quick Controls 2 ( QC2 ). - Unfortunately this was done once more by adding code on top instead - of fixing the foundation. - - But we also don't want to have how it is done in QC2 by adding - the focus management in the event handler of the base class. - This implementation reverts the expected default behaviour of when - events are accepted/ignored + is an error prone nightmare, when it - comes to overloading event handlers missing to call the base class. - - That's why we prefer to do the focus management outside of the - event handlers. - */ - switch ( event->type() ) - { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - { - if ( ( control->focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus ) - { - const bool focusOnRelease = - QGuiApplication::styleHints()->setFocusOnTouchRelease(); - - if ( focusOnRelease ) - { - if ( event->type() == QEvent::MouseButtonRelease ) - control->forceActiveFocus( Qt::MouseFocusReason ); - } - else - { - if ( event->type() == QEvent::MouseButtonPress ) - control->forceActiveFocus( Qt::MouseFocusReason ); - } - } - break; - } - case QEvent::Wheel: - { - if ( !control->isWheelEnabled() ) - { - /* - We block further processing of the event. This is in line - with not receiving any mouse event that have not been - explicitly enabled with setAcceptedMouseButtons(). - - */ - event->ignore(); - return true; - } - - if ( ( control->focusPolicy() & Qt::WheelFocus ) == Qt::WheelFocus ) - control->forceActiveFocus( Qt::MouseFocusReason ); - - break; - } - default: - break; - } - } - - return false; -} - -#include "moc_QskSetup.cpp" diff --git a/src/controls/QskSetup.h b/src/controls/QskSetup.h index d541a614..2efa41d4 100644 --- a/src/controls/QskSetup.h +++ b/src/controls/QskSetup.h @@ -7,61 +7,17 @@ #define QSK_SETUP_H #include "QskGlobal.h" -#include "QskQuickItem.h" +#include "QskItem.h" -#include -#include - -class QQuickItem; -class QskGraphicProvider; - -#if defined( qskSetup ) -#undef qskSetup -#endif - -#define qskSetup QskSetup::instance() - -class QSK_EXPORT QskSetup : public QObject +namespace QskSetup { - Q_OBJECT + QSK_EXPORT void setUpdateFlags( QskItem::UpdateFlags ); + QSK_EXPORT void resetUpdateFlags(); + QSK_EXPORT QskItem::UpdateFlags updateFlags(); - public: - - static QskSetup* instance(); - - void setItemUpdateFlags( QskQuickItem::UpdateFlags ); - void resetItemUpdateFlags(); - QskQuickItem::UpdateFlags itemUpdateFlags() const; - - void setItemUpdateFlag( QskQuickItem::UpdateFlag, bool on = true ); - void resetItemUpdateFlag( QskQuickItem::UpdateFlag ); - bool testItemUpdateFlag( QskQuickItem::UpdateFlag ); - - void addGraphicProvider( const QString& providerId, QskGraphicProvider* ); - QskGraphicProvider* graphicProvider( const QString& providerId ) const; - - static void setup(); - static void cleanup(); - - Q_SIGNALS: - void itemUpdateFlagsChanged(); - - private: - QskSetup(); - ~QskSetup() override; - - bool eventFilter( QObject*, QEvent* ) override final; - - static QskSetup* s_instance; - - class PrivateData; - std::unique_ptr< PrivateData > m_data; + QSK_EXPORT void setUpdateFlag( QskItem::UpdateFlag, bool on = true ); + QSK_EXPORT void resetUpdateFlag( QskItem::UpdateFlag ); + QSK_EXPORT bool testUpdateFlag( QskItem::UpdateFlag ); }; -inline QskSetup* QskSetup::instance() -{ - Q_ASSERT( s_instance ); - return s_instance; -} - #endif diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index e2fb11db..61af02a0 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -10,14 +10,15 @@ #include "QskColorFilter.h" #include "QskGraphic.h" #include "QskGraphicProviderMap.h" -#include "QskSkinHintTable.h" #include "QskStandardSymbol.h" #include "QskPlatform.h" +#include "QskMargins.h" +#include "QskFontRole.h" + +#include "QskSkinHintTable.h" #include "QskSkinManager.h" #include "QskSkinTransition.h" -#include "QskMargins.h" - #include #include #include @@ -123,6 +124,22 @@ static inline QskSkinlet* qskNewSkinlet( const QMetaObject* metaObject, QskSkin* return skinlet; } +// also used in QskSkinTransition.cpp TODO ... + +QFont qskResolvedFont( const QHash< QskFontRole, QFont >& fontTable, + const QskFontRole& fontRole ) +{ + auto it = fontTable.constFind( fontRole ); + if ( it != fontTable.constEnd() ) + return it.value(); + + it = fontTable.constFind( QskFontRole() ); + if ( it != fontTable.constEnd() ) + return it.value(); + + return QGuiApplication::font(); +} + namespace { class SkinletData @@ -151,7 +168,7 @@ class QskSkin::PrivateData QskSkinHintTable hintTable; - QHash< int, QFont > fonts; + QHash< QskFontRole, QFont > fonts; QHash< int, QskColorFilter > graphicFilters; QskGraphicProviderMap graphicProviders; @@ -194,7 +211,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskRadioBox, QskRadioBoxSkinlet >(); const QFont font = QGuiApplication::font(); - setupFonts( font.family(), font.weight(), font.italic() ); + setupFontTable( font.family(), font.italic() ); const auto noMargins = QVariant::fromValue( QskMargins( 0 ) ); @@ -236,6 +253,7 @@ void QskSkin::setColorScheme( ColorScheme colorScheme ) if ( transitionHint.isValid() ) { QskSkinTransition transition; + transition.setMask( QskSkinTransition::Color ); transition.setSourceSkin( this ); clearHints(); @@ -287,50 +305,92 @@ void QskSkin::declareSkinlet( const QMetaObject* metaObject, } } -void QskSkin::setupFonts( const QString& family, int weight, bool italic ) +static inline void qskSetFont( QskSkin* skin, + QskFontRole::Category category, QskFontRole::Emphasis emphasis, + QFont font, QFont::Weight weight ) { - const int sizes[] = { 10, 15, 20, 32, 66 }; - static_assert( sizeof( sizes ) / sizeof( sizes[ 0 ] ) == HugeFont, - "QskSkin::setupFonts: bad list size." ); - - QFont font( family, -1, weight, italic ); - - for ( int i = TinyFont; i <= HugeFont; i++ ) - { - font.setPixelSize( qRound( qskDpToPixels( sizes[i - 1] ) ) ); - m_data->fonts[ i ] = font; - } - - const QFont appFont( QGuiApplication::font() ); - if ( appFont.pixelSize() > 0 ) - font.setPixelSize( appFont.pixelSize() ); - else - font.setPointSize( appFont.pointSize() ); - - m_data->fonts[ QskSkin::DefaultFont ] = font; + font.setWeight( weight ); + skin->setFont( { category, emphasis }, font ); } -void QskSkin::setFont( int fontRole, const QFont& font ) +static inline void qskSetNormalFont( QskSkin* skin, + QskFontRole::Category category, QFont font, int pixelSize ) +{ + font.setPixelSize( qRound( qskDpToPixels( pixelSize ) ) ); + qskSetFont( skin, category, QskFontRole::Normal, font, QFont::Normal ); +} + +void QskSkin::setupFontTable( const QString& family, bool italic ) +{ + const QFont font( family, -1, -1, italic ); + + qskSetNormalFont( this, QskFontRole::Caption, font, 10 ); + qskSetNormalFont( this, QskFontRole::Subtitle, font, 15 ); + qskSetNormalFont( this, QskFontRole::Body, font, 20 ); + qskSetNormalFont( this, QskFontRole::Title, font, 24 ); + qskSetNormalFont( this, QskFontRole::Headline, font, 32 ); + qskSetNormalFont( this, QskFontRole::Display, font, 66 ); + + completeFontTable(); +} + +void QskSkin::completeFontTable() +{ + // varying the weight of QskFontRole::Normal + + for ( int i = QskFontRole::Caption; i <= QskFontRole::Display; i++ ) + { + auto& table = m_data->fonts; + + const auto category = static_cast< QskFontRole::Category >( i ); + + const auto it = table.constFind( { category, QskFontRole::Normal } ); + if ( it == table.constEnd() ) + continue; + + const auto normalFont = it.value(); + + for ( int j = QskFontRole::VeryLow; j <= QskFontRole::VeryHigh; j++ ) + { + const auto emphasis = static_cast< QskFontRole::Emphasis >( j ); + + if ( emphasis == QskFontRole::Normal + || table.contains( { category, emphasis } ) ) + { + continue; + } + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + const auto step = 10; // weight: [0-99] +#else + const auto step = 100; // weight: [1-1000] +#endif + + int weight = normalFont.weight() + ( j - 2 ) * step; + weight = qBound( static_cast( QFont::Thin ), + weight, static_cast( QFont::Black ) ); + + auto font = normalFont; + font.setWeight( static_cast< QFont::Weight >( weight ) ); + + m_data->fonts[ { category, emphasis } ] = font; + } + } +} + +void QskSkin::setFont( const QskFontRole& fontRole, const QFont& font ) { m_data->fonts[ fontRole ] = font; } -void QskSkin::resetFont( int fontRole ) +void QskSkin::resetFont( const QskFontRole& fontRole ) { m_data->fonts.remove( fontRole ); } -QFont QskSkin::font( int fontRole ) const +QFont QskSkin::font( const QskFontRole& fontRole ) const { - auto it = m_data->fonts.constFind( fontRole ); - if ( it != m_data->fonts.constEnd() ) - return it.value(); - - it = m_data->fonts.constFind( QskSkin::DefaultFont ); - if ( it != m_data->fonts.constEnd() ) - return it.value(); - - return QGuiApplication::font(); + return qskResolvedFont( m_data->fonts, fontRole ); } void QskSkin::setGraphicFilter( int graphicRole, const QskColorFilter& colorFilter ) @@ -358,7 +418,7 @@ QskSkinHintTable& QskSkin::hintTable() return m_data->hintTable; } -const QHash< int, QFont >& QskSkin::fonts() const +const QHash< QskFontRole, QFont >& QskSkin::fontTable() const { return m_data->fonts; } diff --git a/src/controls/QskSkin.h b/src/controls/QskSkin.h index f70636b4..1a3012c3 100644 --- a/src/controls/QskSkin.h +++ b/src/controls/QskSkin.h @@ -20,6 +20,7 @@ class QskSkinlet; class QskColorFilter; class QskGraphic; class QskGraphicProvider; +class QskFontRole; class QskSkinHintTable; @@ -33,19 +34,6 @@ class QSK_EXPORT QskSkin : public QObject using Inherited = QObject; public: - enum SkinFontRole - { - DefaultFont = 0, - - TinyFont, - SmallFont, - MediumFont, - LargeFont, - HugeFont - }; - - Q_ENUM( SkinFontRole ) - #if 1 // Use Qt::ColorScheme once minimum version is Qt 6.5 enum ColorScheme @@ -71,12 +59,9 @@ class QSK_EXPORT QskSkin : public QObject void resetGraphicFilter( int graphicRole ); QskColorFilter graphicFilter( int graphicRole ) const; - void setFont( int fontRole, const QFont& ); - void resetFont( int fontRole ); - QFont font( int fontRole ) const; - - void setupFonts( const QString& family, - int weight = -1, bool italic = false ); + void setFont( const QskFontRole&, const QFont& ); + void resetFont( const QskFontRole& ); + QFont font( const QskFontRole& ) const; void addGraphicProvider( const QString& providerId, QskGraphicProvider* ); QskGraphicProvider* graphicProvider( const QString& providerId ) const; @@ -91,7 +76,7 @@ class QSK_EXPORT QskSkin : public QObject const QskSkinHintTable& hintTable() const; QskSkinHintTable& hintTable(); - const QHash< int, QFont >& fonts() const; + const QHash< QskFontRole, QFont >& fontTable() const; const QHash< int, QskColorFilter >& graphicFilters() const; ColorScheme colorScheme() const; @@ -106,6 +91,9 @@ class QSK_EXPORT QskSkin : public QObject void clearHints(); virtual void initHints() = 0; + void setupFontTable( const QString& family, bool italic = false ); + void completeFontTable(); + private: void declareSkinlet( const QMetaObject* metaObject, const QMetaObject* skinletMetaObject ); diff --git a/src/controls/QskSkinHintTableEditor.cpp b/src/controls/QskSkinHintTableEditor.cpp index a89ed995..393762db 100644 --- a/src/controls/QskSkinHintTableEditor.cpp +++ b/src/controls/QskSkinHintTableEditor.cpp @@ -15,6 +15,7 @@ #include "QskShadowMetrics.h" #include "QskStippleMetrics.h" #include "QskGraphic.h" +#include "QskFontRole.h" namespace { @@ -405,7 +406,7 @@ Qt::Alignment QskSkinHintTableEditor::alignment( QskAspect aspect ) const } void QskSkinHintTableEditor::setFontRole( - QskAspect aspect, int fontRole, QskStateCombination combination ) + QskAspect aspect, const QskFontRole& fontRole, QskStateCombination combination ) { setHint( aspectFontRole( aspect ), fontRole, combination ); } @@ -416,9 +417,9 @@ bool QskSkinHintTableEditor::removeFontRole( return removeHint( aspectFontRole( aspect ), combination ); } -int QskSkinHintTableEditor::fontRole( QskAspect aspect ) const +QskFontRole QskSkinHintTableEditor::fontRole( QskAspect aspect ) const { - return hint< int >( aspectFontRole( aspect ) ); + return hint< QskFontRole >( aspectFontRole( aspect ) ); } void QskSkinHintTableEditor::setGraphicRole( diff --git a/src/controls/QskSkinHintTableEditor.h b/src/controls/QskSkinHintTableEditor.h index b91255c4..7aff2805 100644 --- a/src/controls/QskSkinHintTableEditor.h +++ b/src/controls/QskSkinHintTableEditor.h @@ -26,6 +26,7 @@ class QskGraduationMetrics; class QskShadowMetrics; class QskStippleMetrics; class QskGraphic; +class QskFontRole; class QSK_EXPORT QskSkinHintTableEditor { @@ -178,9 +179,10 @@ class QSK_EXPORT QskSkinHintTableEditor // fontRole - void setFontRole( QskAspect, int, QskStateCombination = QskStateCombination() ); + void setFontRole( QskAspect, const QskFontRole&, + QskStateCombination = QskStateCombination() ); bool removeFontRole( QskAspect, QskStateCombination = QskStateCombination() ); - int fontRole( QskAspect ) const; + QskFontRole fontRole( QskAspect ) const; // graphicRole diff --git a/src/controls/QskSkinTransition.cpp b/src/controls/QskSkinTransition.cpp index 131440ec..4f7dc399 100644 --- a/src/controls/QskSkinTransition.cpp +++ b/src/controls/QskSkinTransition.cpp @@ -11,6 +11,8 @@ #include "QskHintAnimator.h" #include "QskSkin.h" #include "QskSkinHintTable.h" +#include "QskFontRole.h" +#include "QskAspect.h" #include #include @@ -20,6 +22,37 @@ #include +#if 1 + +/* + We need to find a way how to retrieve the extra states that are provided + by QskSkinlet::sampleStates from the control in a generic way. For the moment we + return a hardcoded list of states that we know because we know. TODO ... + */ +#include "QskMenu.h" +#include "QskRadioBox.h" +#include "QskSegmentedBar.h" +#include "QskListView.h" + +static QskAspect::State qskSelectedSampleState( const QskControl* control ) +{ + if ( qobject_cast< const QskMenu* >( control ) ) + return QskMenu::Selected; + + if ( qobject_cast< const QskRadioBox* >( control ) ) + return QskRadioBox::Selected; + + if ( qobject_cast< const QskSegmentedBar* >( control ) ) + return QskSegmentedBar::Selected; + + if ( qobject_cast< const QskListView* >( control ) ) + return QskListView::Selected; + + return QskAspect::NoState; +} + +#endif + static bool qskHasHintTable( const QskSkin* skin, const QskSkinHintTable& hintTable ) { return skin->hintTable().hints().isSharedWith( hintTable.hints() ); @@ -55,12 +88,10 @@ static void qskAddCandidates( const QskSkinTransition::Type mask, { isCandidate = mask & QskSkinTransition::Color; } -#if 0 else if ( aspect.primitive() == QskAspect::FontRole ) { isCandidate = mask & QskSkinTransition::Metric; } -#endif break; } case QskAspect::Color: @@ -130,10 +161,14 @@ namespace QVariant animatedHint( QskAspect ) const; QVariant animatedGraphicFilter( int graphicRole ) const; + QVariant animatedFontSize( const QskFontRole& ) const; void addGraphicFilterAnimators( const QskAnimationHint&, const QHash< int, QskColorFilter >&, const QHash< int, QskColorFilter >& ); + void addFontSizeAnimators( const QskAnimationHint&, + const QHash< QskFontRole, QFont >&, const QHash< QskFontRole, QFont >& ); + void addItemAspects( QQuickItem*, const QskAnimationHint&, const QSet< QskAspect >&, const QskSkinHintTable&, const QskSkinHintTable& ); @@ -145,8 +180,8 @@ namespace bool isControlAffected( const QskControl*, const QVector< QskAspect::Subcontrol >&, QskAspect ) const; - void addHints( const QskControl*, - const QskAnimationHint&, const QSet< QskAspect >& candidates, + void addHint( const QskControl*, + const QskAnimationHint&, QskAspect, const QskSkinHintTable&, const QskSkinHintTable& ); void storeAnimator( const QskControl*, const QskAspect, @@ -155,8 +190,11 @@ namespace void storeUpdateInfo( const QskControl*, QskAspect ); QQuickWindow* m_window; + QHash< QskAspect, HintAnimator > m_animatorMap; QHash< int, QskVariantAnimator > m_graphicFilterAnimatorMap; + QHash< QskFontRole, QskVariantAnimator > m_fontSizeAnimatorMap; + std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration }; @@ -212,6 +250,9 @@ void WindowAnimator::start() for ( auto& it : m_graphicFilterAnimatorMap ) it.start(); + + for ( auto& it : m_fontSizeAnimatorMap ) + it.start(); } bool WindowAnimator::isRunning() const @@ -230,6 +271,13 @@ bool WindowAnimator::isRunning() const return true; } + if ( !m_fontSizeAnimatorMap.empty() ) + { + const auto& animator = m_fontSizeAnimatorMap.constBegin().value(); + if ( animator.isRunning() ) + return true; + } + return false; } @@ -259,6 +307,19 @@ inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const return QVariant(); } +inline QVariant WindowAnimator::animatedFontSize( const QskFontRole& fontRole ) const +{ + auto it = m_fontSizeAnimatorMap.constFind( fontRole ); + if ( it != m_fontSizeAnimatorMap.constEnd() ) + { + const auto& animator = it.value(); + if ( animator.isRunning() ) + return animator.currentValue(); + } + + return QVariant(); +} + void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint, const QHash< int, QskColorFilter >& filters1, @@ -289,26 +350,82 @@ void WindowAnimator::addGraphicFilterAnimators( } } +// from QskSkin.cpp +extern QFont qskResolvedFont( + const QHash< QskFontRole, QFont >&, const QskFontRole& ); + +void WindowAnimator::addFontSizeAnimators( + const QskAnimationHint& animatorHint, + const QHash< QskFontRole, QFont >& fonts1, + const QHash< QskFontRole, QFont >& fonts2 ) +{ + for ( int i = 0; i <= QskFontRole::Display; i++ ) + { + for ( int j = 0; j <= QskFontRole::VeryHigh; j++ ) + { + const QskFontRole fontRole( + static_cast< QskFontRole::Category >( i ), + static_cast< QskFontRole::Emphasis >( j ) + ); + + const auto size1 = qskResolvedFont( fonts1, fontRole ).pixelSize(); + const auto size2 = qskResolvedFont( fonts2, fontRole ).pixelSize(); + + if ( ( size1 > 0 ) && ( size2 > 0 ) && ( size1 != size2 ) ) + { + QskVariantAnimator animator; + animator.setWindow( m_window ); + animator.setDuration( animatorHint.duration ); + animator.setEasingCurve( animatorHint.type ); + animator.setStartValue( QVariant::fromValue( size1 ) ); + animator.setEndValue( QVariant::fromValue( size2 ) ); + + m_fontSizeAnimatorMap.insert( fontRole, animator ); + } + } + } +} + void WindowAnimator::addItemAspects( QQuickItem* item, const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, const QskSkinHintTable& table1, const QskSkinHintTable& table2 ) { - if ( !item->isVisible() ) - return; - - if ( auto control = qskControlCast( item ) ) + if ( auto control = qskControlCast( ( const QQuickItem* )item ) ) { - if ( control->isInitiallyPainted() && + if ( control->isVisible() && control->isInitiallyPainted() && qskHasHintTable( control->effectiveSkin(), table2 ) ) { - addHints( control, animatorHint, candidates, table1, table2 ); + const auto subControls = control->subControls(); + + const auto& localTable = control->hintTable(); + + for ( auto aspect : candidates ) + { + if ( isControlAffected( control, subControls, aspect ) ) + { + aspect.setVariation( control->effectiveVariation() ); + aspect.setStates( control->skinStates() ); + aspect.setSection( control->section() ); + + if ( !localTable.resolvedHint( aspect ) ) + addHint( control, animatorHint, aspect, table1, table2 ); + + if ( auto state = qskSelectedSampleState( control ) ) + { + aspect.addStates( state ); + if ( !localTable.resolvedHint( aspect ) ) + addHint( control, animatorHint, aspect, table1, table2 ); + } + } + } + #if 1 /* As it is hard to identify which controls depend on the animated graphic filters we schedule an initial update and let the controls do the rest: see QskSkinnable::effectiveGraphicFilter */ - control->update(); + item->update(); #endif } } @@ -336,63 +453,59 @@ void WindowAnimator::update() } } -void WindowAnimator::addHints( const QskControl* control, - const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, +void WindowAnimator::addHint( const QskControl* control, + const QskAnimationHint& animatorHint, QskAspect aspect, const QskSkinHintTable& table1, const QskSkinHintTable& table2 ) { - const auto subControls = control->subControls(); + QskAspect r1, r2; - const auto& localTable = control->hintTable(); + const auto v1 = table1.resolvedHint( aspect, &r1 ); + const auto v2 = table2.resolvedHint( aspect, &r2 ); - for ( auto aspect : candidates ) + if ( v1 && v2 ) { - if ( !isControlAffected( control, subControls, aspect ) ) - continue; + if ( r1.section() == r2.section() ) + aspect.setSection( r2.section() ); - aspect.setVariation( control->effectiveVariation() ); - aspect.setStates( control->skinStates() ); + if ( r1.variation() == r2.variation() ) + aspect.setVariation( r2.variation() ); - if ( localTable.resolvedHint( aspect ) ) + if ( r1.states() == r2.states() ) + aspect.setStates( r2.states() ); + + bool accecptIdentity = false; + if ( aspect != aspect.trunk() ) { - // value is not from the skin - ignored - continue; + /* + We might need an animator even if the values do not differ + to prevent effectiveSkinHint to find its value from + another animator that might have been started with + less extra bits. + */ + accecptIdentity = true; } - QskAspect r1, r2; - - const auto v1 = table1.resolvedHint( aspect, &r1 ); - const auto v2 = table2.resolvedHint( aspect, &r2 ); - - if ( v1 && v2 ) + if ( QskVariantAnimator::maybeInterpolate( *v1, *v2, accecptIdentity ) ) { - if ( QskVariantAnimator::maybeInterpolate( *v1, *v2 ) ) - { - if ( r1.variation() == r2.variation() ) - aspect.setVariation( r2.variation() ); - - if ( r1.states() == r2.states() ) - aspect.setStates( r2.states() ); - - storeAnimator( control, aspect, *v1, *v2, animatorHint ); - storeUpdateInfo( control, aspect ); - } - } - else if ( v1 ) - { - aspect.setVariation( r1.variation() ); - aspect.setStates( r1.states() ); - - storeAnimator( control, aspect, *v1, QVariant(), animatorHint ); + storeAnimator( control, aspect, *v1, *v2, animatorHint ); storeUpdateInfo( control, aspect ); } - else if ( v2 ) - { - aspect.setVariation( r1.variation() ); - aspect.setStates( r1.states() ); + } + else if ( v1 ) + { + aspect.setVariation( r1.variation() ); + aspect.setStates( r1.states() ); - storeAnimator( control, aspect, QVariant(), *v2, animatorHint ); - storeUpdateInfo( control, aspect ); - } + storeAnimator( control, aspect, *v1, QVariant(), animatorHint ); + storeUpdateInfo( control, aspect ); + } + else if ( v2 ) + { + aspect.setVariation( r1.variation() ); + aspect.setStates( r1.states() ); + + storeAnimator( control, aspect, QVariant(), *v2, animatorHint ); + storeUpdateInfo( control, aspect ); } } @@ -430,7 +543,8 @@ inline bool WindowAnimator::isControlAffected( const QskControl* control, return true; } -inline void WindowAnimator::storeAnimator( const QskControl* control, const QskAspect aspect, +inline void WindowAnimator::storeAnimator( + const QskControl* control, const QskAspect aspect, const QVariant& value1, const QVariant& value2, QskAnimationHint hint ) { if ( m_animatorMap.find( aspect ) == m_animatorMap.cend() ) @@ -554,6 +668,7 @@ class QskSkinTransition::PrivateData { QskSkinHintTable hintTable; QHash< int, QskColorFilter > graphicFilters; + QHash< QskFontRole, QFont > fontTable; } tables[ 2 ]; Type mask = QskSkinTransition::AllTypes; @@ -584,6 +699,7 @@ void QskSkinTransition::setSourceSkin( const QskSkin* skin ) tables.hintTable = skin->hintTable(); tables.graphicFilters = skin->graphicFilters(); + tables.fontTable = skin->fontTable(); } void QskSkinTransition::setTargetSkin( const QskSkin* skin ) @@ -592,6 +708,7 @@ void QskSkinTransition::setTargetSkin( const QskSkin* skin ) tables.hintTable = skin->hintTable(); tables.graphicFilters = skin->graphicFilters(); + tables.fontTable = skin->fontTable(); } void QskSkinTransition::run( const QskAnimationHint& animationHint ) @@ -604,6 +721,9 @@ void QskSkinTransition::run( const QskAnimationHint& animationHint ) const auto& graphicFilters1 = m_data->tables[ 0 ].graphicFilters; const auto& graphicFilters2 = m_data->tables[ 1 ].graphicFilters; + const auto& fontTable1 = m_data->tables[ 0 ].fontTable; + const auto& fontTable2 = m_data->tables[ 1 ].fontTable; + QSet< QskAspect > candidates; if ( ( animationHint.duration > 0 ) && ( m_data->mask != 0 ) ) @@ -615,6 +735,7 @@ void QskSkinTransition::run( const QskAnimationHint& animationHint ) if ( !candidates.isEmpty() ) { bool doGraphicFilter = m_data->mask & QskSkinTransition::Color; + bool doFont = m_data->mask & QskSkinTransition::Metric; const auto windows = qGuiApp->topLevelWindows(); @@ -638,6 +759,12 @@ void QskSkinTransition::run( const QskAnimationHint& animationHint ) doGraphicFilter = false; } + if ( doFont ) + { + animator->addFontSizeAnimators( animationHint, + fontTable1, fontTable2 ); + } + /* finally we schedule the animators the hard way by running over the the item trees. @@ -686,4 +813,16 @@ QVariant QskSkinTransition::animatedGraphicFilter( return QVariant(); } +QVariant QskSkinTransition::animatedFontSize( + const QQuickWindow* window, const QskFontRole& fontRole ) +{ + if ( qskApplicationAnimator.exists() ) + { + if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) ) + return animator->animatedFontSize( fontRole ); + } + + return QVariant(); +} + #include "QskSkinTransition.moc" diff --git a/src/controls/QskSkinTransition.h b/src/controls/QskSkinTransition.h index 58d5da6f..fad3dd38 100644 --- a/src/controls/QskSkinTransition.h +++ b/src/controls/QskSkinTransition.h @@ -6,14 +6,17 @@ #ifndef QSK_SKIN_TRANSITION_H #define QSK_SKIN_TRANSITION_H -#include "QskAspect.h" +#include "QskGlobal.h" #include class QskAnimationHint; +class QskFontRole; +class QskAspect; class QskSkin; class QQuickWindow; class QVariant; + template< typename Key, typename T > class QHash; class QSK_EXPORT QskSkinTransition @@ -39,8 +42,10 @@ class QSK_EXPORT QskSkinTransition void run( const QskAnimationHint& ); static bool isRunning(); + static QVariant animatedHint( const QQuickWindow*, QskAspect ); static QVariant animatedGraphicFilter( const QQuickWindow*, int graphicRole ); + static QVariant animatedFontSize( const QQuickWindow*, const QskFontRole& ); private: Q_DISABLE_COPY( QskSkinTransition ) diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 93b86dce..48c456a9 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -126,10 +126,10 @@ static inline QSGNode* qskUpdateGraphicNode( if ( graphicNode == nullptr ) graphicNode = new QskGraphicNode(); - const auto flag = QskQuickItem::PreferRasterForTextures; + const auto flag = QskItem::PreferRasterForTextures; - bool useRaster = qskSetup->testItemUpdateFlag( flag ); - if ( auto qItem = qobject_cast< const QskQuickItem* >( item ) ) + bool useRaster = QskSetup::testUpdateFlag( flag ); + if ( auto qItem = qobject_cast< const QskItem* >( item ) ) useRaster = qItem->testUpdateFlag( flag ); graphicNode->setRenderHint( useRaster ? QskPaintedNode::Raster : QskPaintedNode::OpenGL ); @@ -345,7 +345,7 @@ void QskSkinlet::updateNode( QskSkinnable* skinnable, QSGNode* parentNode ) cons oldNode = findChildNode( parentNode, DebugRole ); newNode = nullptr; - if ( control->testUpdateFlag( QskQuickItem::DebugForceBackground ) ) + if ( control->testUpdateFlag( QskItem::DebugForceBackground ) ) newNode = updateDebugNode( control, oldNode ); replaceChildNode( DebugRole, parentNode, oldNode, newNode ); diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index cf73ea7c..f1c79795 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -28,6 +28,7 @@ #include "QskGradient.h" #include "QskTextOptions.h" #include "QskGraphic.h" +#include "QskFontRole.h" #include #include @@ -129,7 +130,7 @@ static inline constexpr QskAspect qskAnimatorAspect( const QskAspect aspect ) the effective aspect in animatedHint. */ - return aspect.type() | aspect.subControl() | aspect.primitive(); + return aspect.type() | aspect.subControl() | aspect.primitive(); } static inline void qskTriggerUpdates( QskAspect aspect, QQuickItem* item ) @@ -704,9 +705,11 @@ QskTextOptions QskSkinnable::textOptionsHint( aspect | QskAspect::Option, status ).value< QskTextOptions >(); } -bool QskSkinnable::setFontRoleHint( const QskAspect aspect, int role ) +bool QskSkinnable::setFontRoleHint( + const QskAspect aspect, const QskFontRole& role ) { - return qskSetFlag( this, aspect | QskAspect::FontRole, role ); + return setSkinHint( aspect | QskAspect::FontRole, + QVariant::fromValue( role ) ); } bool QskSkinnable::resetFontRoleHint( const QskAspect aspect ) @@ -714,15 +717,43 @@ bool QskSkinnable::resetFontRoleHint( const QskAspect aspect ) return resetSkinHint( aspect | QskAspect::FontRole ); } -int QskSkinnable::fontRoleHint( +QskFontRole QskSkinnable::fontRoleHint( const QskAspect aspect, QskSkinHintStatus* status ) const { - return qskFlag( this, aspect | QskAspect::FontRole, status ); + return effectiveSkinHint( + aspect | QskAspect::FontRole, status ).value< QskFontRole >(); } -QFont QskSkinnable::effectiveFont( const QskAspect aspect ) const +QFont QskSkinnable::effectiveFont( QskAspect aspect ) const { - return effectiveSkin()->font( fontRoleHint( aspect ) ); + const auto hint = effectiveSkinHint( aspect | QskAspect::FontRole ); + if ( hint.canConvert< QFont >() ) + { + /* + The provided skins/controls use font roles only - however + application code might want to assign fonts without defining + font roles. + */ + return hint.value< QFont >(); + } + + const auto fontRole = hint.value< QskFontRole >(); + + auto font = effectiveSkin()->font( fontRole ); + + if ( auto item = owningItem() ) + { + const auto v = QskSkinTransition::animatedFontSize( + item->window(), fontRole ); + + if ( v.canConvert< int >() ) + { + font.setPixelSize( v.value< int >() ); + item->update(); // design flaw: see effectiveGraphicFilter + } + } + + return font; } qreal QskSkinnable::effectiveFontHeight( const QskAspect aspect ) const @@ -1316,7 +1347,7 @@ void QskSkinnable::startHintTransition( QskAspect aspect, int index, if ( control->window() == nullptr || !isTransitionAccepted( aspect ) ) return; - if ( !QskVariantAnimator::maybeInterpolate( from, to ) ) + if ( !QskVariantAnimator::maybeInterpolate( from, to, false ) ) return; auto v1 = from; diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index bed91135..d7ec2d6b 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -36,6 +36,7 @@ class QskTextOptions; class QskBoxHints; class QskGradient; class QskGraphic; +class QskFontRole; class QskSkin; class QskSkinlet; @@ -245,9 +246,9 @@ class QSK_EXPORT QskSkinnable bool resetTextOptionsHint( QskAspect ); QskTextOptions textOptionsHint( QskAspect, QskSkinHintStatus* = nullptr ) const; - bool setFontRoleHint( QskAspect, int role ); + bool setFontRoleHint( QskAspect, const QskFontRole& ); bool resetFontRoleHint( QskAspect ); - int fontRoleHint( QskAspect, QskSkinHintStatus* = nullptr ) const; + QskFontRole fontRoleHint( QskAspect, QskSkinHintStatus* = nullptr ) const; bool setGraphicRoleHint( QskAspect, int role ); bool resetGraphicRoleHint( QskAspect ); diff --git a/src/controls/QskSlider.cpp b/src/controls/QskSlider.cpp index d92366c3..1c3822fb 100644 --- a/src/controls/QskSlider.cpp +++ b/src/controls/QskSlider.cpp @@ -17,8 +17,6 @@ QSK_SUBCONTROL( QskSlider, Handle ) QSK_SUBCONTROL( QskSlider, Ripple ) QSK_SYSTEM_STATE( QskSlider, Pressed, QskAspect::FirstSystemState << 2 ) -QSK_SYSTEM_STATE( QskSlider, Minimum, QskAspect::LastUserState << 1 ) -QSK_SYSTEM_STATE( QskSlider, Maximum, QskAspect::LastUserState << 2 ) class QskSlider::PrivateData { @@ -233,9 +231,6 @@ void QskSlider::moveHandle() void QskSlider::moveHandleTo( qreal value, const QskAnimationHint& hint ) { - setSkinStateFlag( QskSlider::Minimum, value <= minimum() ); - setSkinStateFlag( QskSlider::Maximum, value >= maximum() ); - const qreal pos = valueAsRatio( value ); if ( hint.isValid() ) diff --git a/src/controls/QskSlider.h b/src/controls/QskSlider.h index 88418d80..908e96f8 100644 --- a/src/controls/QskSlider.h +++ b/src/controls/QskSlider.h @@ -26,7 +26,7 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput public: QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Handle, Ripple ) - QSK_STATES( Pressed, Minimum, Maximum ) + QSK_STATES( Pressed ) explicit QskSlider( QQuickItem* parent = nullptr ); explicit QskSlider( Qt::Orientation, QQuickItem* parent = nullptr ); diff --git a/src/controls/QskSpinBox.cpp b/src/controls/QskSpinBox.cpp index 56ae6e7f..6195062f 100644 --- a/src/controls/QskSpinBox.cpp +++ b/src/controls/QskSpinBox.cpp @@ -41,12 +41,7 @@ namespace inline QskAspect aspectDecoration() { - return QskSpinBox::Panel | QskAspect::NoType | QskAspect::Style; - } - - inline QskAspect aspectTextAlignment() - { - return QskSpinBox::TextPanel | QskAspect::NoType | QskAspect::Alignment; + return QskSpinBox::Panel | QskAspect::Style; } } @@ -176,26 +171,6 @@ QskSpinBox::Decoration QskSpinBox::decoration() const return flagHint< QskSpinBox::Decoration >( aspectDecoration(), ButtonsLeftAndRight ); } -void QskSpinBox::setTextAlignment( Qt::Alignment alignment ) -{ - alignment &= Qt::AlignHorizontal_Mask; - - if ( setFlagHint( aspectTextAlignment(), alignment ) ) - Q_EMIT textAlignmentChanged( alignment ); -} - -void QskSpinBox::resetTextAlignment() -{ - if ( resetSkinHint( aspectTextAlignment() ) ) - Q_EMIT textAlignmentChanged( textAlignment() ); -} - -Qt::Alignment QskSpinBox::textAlignment() const -{ - return flagHint< Qt::Alignment >( - aspectTextAlignment(), Qt::AlignLeft ) & Qt::AlignHorizontal_Mask; -} - void QskSpinBox::setWrapping( bool on ) { if ( on != m_data->wrapping ) diff --git a/src/controls/QskSpinBox.h b/src/controls/QskSpinBox.h index 518537c1..38f62c35 100644 --- a/src/controls/QskSpinBox.h +++ b/src/controls/QskSpinBox.h @@ -22,9 +22,6 @@ class QSK_EXPORT QskSpinBox : public QskBoundedValueInput Q_PROPERTY( int decimals READ decimals WRITE setDecimals NOTIFY decimalsChanged ) - Q_PROPERTY( Qt::Alignment textAlignment READ textAlignment - WRITE setTextAlignment RESET textAlignment NOTIFY textAlignmentChanged ) - Q_PROPERTY( QString text READ text NOTIFY textChanged ) public: @@ -53,11 +50,6 @@ class QSK_EXPORT QskSpinBox : public QskBoundedValueInput void resetDecoration(); Decoration decoration() const; - // Qt::AlignLeft, Qt::AlignRight or Qt::AlignHCenter. - void setTextAlignment( Qt::Alignment ); - void resetTextAlignment(); - Qt::Alignment textAlignment() const; - void setWrapping( bool ); bool isWrapping() const; @@ -71,10 +63,7 @@ class QSK_EXPORT QskSpinBox : public QskBoundedValueInput Q_SIGNALS: void decorationChanged( Decoration ); - void textAlignmentChanged( Qt::Alignment ); - void wrappingChanged( bool ); - void decimalsChanged( int ); void textChanged(); diff --git a/src/controls/QskSpinBoxSkinlet.cpp b/src/controls/QskSpinBoxSkinlet.cpp index 8970f703..3af7272c 100644 --- a/src/controls/QskSpinBoxSkinlet.cpp +++ b/src/controls/QskSpinBoxSkinlet.cpp @@ -117,11 +117,7 @@ QSGNode* QskSpinBoxSkinlet::updateSubNode( case TextRole: { auto spinBox = static_cast< const QskSpinBox* >( skinnable ); - - const auto rect = subControlRect( spinBox, spinBox->contentsRect(), Q::Text ); - - return updateTextNode( spinBox, node, rect, - spinBox->textAlignment(), spinBox->text(), Q::Text ); + return updateTextNode( spinBox, node, spinBox->text(), Q::Text ); } } @@ -135,11 +131,11 @@ QRectF QskSpinBoxSkinlet::textPanelRect( auto spinBox = static_cast< const QskSpinBox* >( skinnable ); + auto r = spinBox->innerBox( Q::Panel, rect ); + const auto decoration = spinBox->decoration(); if ( decoration == Q::NoDecoration ) - return rect; - - auto r = rect; + return r; const auto spacing = spinBox->spacingHint( Q::Panel ); @@ -174,12 +170,14 @@ QRectF QskSpinBoxSkinlet::textPanelRect( } QRectF QskSpinBoxSkinlet::buttonRect( const QskSkinnable* skinnable, - const QRectF& rect, QskAspect::Subcontrol subControl ) const + const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { using Q = QskSpinBox; const auto spinBox = static_cast< const QskSpinBox* >( skinnable ); + const auto rect = spinBox->innerBox( Q::Panel, contentsRect ); + if ( const auto decoration = spinBox->decoration() ) { qreal x, y, w, h; @@ -212,7 +210,7 @@ QRectF QskSpinBoxSkinlet::buttonRect( const QskSkinnable* skinnable, if( subControl == Q::UpPanel ) { - const auto downRect = buttonRect( skinnable, rect, Q::DownPanel ); + const auto downRect = subControlRect( skinnable, contentsRect, Q::DownPanel ); x = downRect.left() - w; } else @@ -220,7 +218,7 @@ QRectF QskSpinBoxSkinlet::buttonRect( const QskSkinnable* skinnable, x = rect.right() - w; } - y = 0.5 * ( rect.height() - h ); + y = rect.top() + 0.5 * ( rect.height() - h ); } else { @@ -235,7 +233,7 @@ QRectF QskSpinBoxSkinlet::buttonRect( const QskSkinnable* skinnable, w = h; x = ( subControl == Q::UpPanel ) ? rect.right() - w : rect.left(); - y = 0.5 * ( rect.height() - h ); + y = rect.top() + 0.5 * ( rect.height() - h ); } return QRectF( x, y, w, h ); @@ -308,7 +306,9 @@ QSizeF QskSpinBoxSkinlet::sizeHint( const QskSkinnable* skinnable, } } + hint = hint.grownBy( spinBox->paddingHint( Q::Panel ) ); hint = hint.expandedTo( spinBox->strutSizeHint( Q::Panel ) ); + return hint; } diff --git a/src/controls/QskSubWindow.cpp b/src/controls/QskSubWindow.cpp index 90833ef6..5daa8c2b 100644 --- a/src/controls/QskSubWindow.cpp +++ b/src/controls/QskSubWindow.cpp @@ -22,7 +22,7 @@ namespace { inline QskAspect aspectDecoration() { - return QskSubWindow::TitleBarPanel | QskAspect::NoType | QskAspect::Style; + return QskSubWindow::TitleBarPanel | QskAspect::Style; } } diff --git a/src/controls/QskSubWindow.h b/src/controls/QskSubWindow.h index a23932b0..e3f0b949 100644 --- a/src/controls/QskSubWindow.h +++ b/src/controls/QskSubWindow.h @@ -108,4 +108,7 @@ class QSK_EXPORT QskSubWindow : public QskPopup std::unique_ptr< PrivateData > m_data; }; +Q_DECLARE_OPERATORS_FOR_FLAGS( QskSubWindow::Decorations ) +Q_DECLARE_METATYPE( QskSubWindow::Decorations ) + #endif diff --git a/src/controls/QskTabBar.h b/src/controls/QskTabBar.h index e80c24fd..07515941 100644 --- a/src/controls/QskTabBar.h +++ b/src/controls/QskTabBar.h @@ -30,7 +30,7 @@ class QSK_EXPORT QskTabBar : public QskBox Q_PROPERTY( int count READ count NOTIFY countChanged FINAL ) Q_PROPERTY( int currentIndex READ currentIndex - WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL ) + WRITE setCurrentIndex NOTIFY currentIndexChanged USER true FINAL ) Q_PROPERTY( QskTextOptions textOptions READ textOptions WRITE setTextOptions NOTIFY textOptionsChanged ) diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 91491377..da21c40b 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -4,6 +4,7 @@ *****************************************************************************/ #include "QskTextInput.h" +#include "QskFontRole.h" #include "QskQuick.h" QSK_QT_PRIVATE_BEGIN @@ -561,7 +562,7 @@ static inline void qskUpdateInputMethodFont( const QskTextInput* input ) qskUpdateInputMethod( input, queries ); } -void QskTextInput::setFontRole( int role ) +void QskTextInput::setFontRole( const QskFontRole& role ) { if ( setFontRoleHint( Text, role ) ) { @@ -579,7 +580,7 @@ void QskTextInput::resetFontRole() } } -int QskTextInput::fontRole() const +QskFontRole QskTextInput::fontRole() const { return fontRoleHint( Text ); } diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index f26e6a6b..c1a9710d 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -9,17 +9,18 @@ #include "QskControl.h" class QValidator; +class QskFontRole; class QSK_EXPORT QskTextInput : public QskControl { Q_OBJECT - Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged ) + Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged USER true) Q_PROPERTY( QString description READ description WRITE setDescription NOTIFY descriptionChanged ) - Q_PROPERTY( int fontRole READ fontRole + Q_PROPERTY( QskFontRole fontRole READ fontRole WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged ) Q_PROPERTY( QFont font READ font ) @@ -93,9 +94,9 @@ class QSK_EXPORT QskTextInput : public QskControl void setPanel( bool ); bool hasPanel() const; - void setFontRole( int role ); + void setFontRole( const QskFontRole& role ); void resetFontRole(); - int fontRole() const; + QskFontRole fontRole() const; void setAlignment( Qt::Alignment ); void resetAlignment(); diff --git a/src/controls/QskTextLabel.cpp b/src/controls/QskTextLabel.cpp index d5fd6d24..d660a7f2 100644 --- a/src/controls/QskTextLabel.cpp +++ b/src/controls/QskTextLabel.cpp @@ -6,6 +6,7 @@ #include "QskTextLabel.h" #include "QskAspect.h" #include "QskTextOptions.h" +#include "QskFontRole.h" QSK_SUBCONTROL( QskTextLabel, Panel ) QSK_SUBCONTROL( QskTextLabel, Text ) @@ -161,7 +162,7 @@ Qt::TextElideMode QskTextLabel::elideMode() const return textOptions().elideMode(); } -void QskTextLabel::setFontRole( int role ) +void QskTextLabel::setFontRole( const QskFontRole& role ) { if ( setFontRoleHint( Text, role ) ) Q_EMIT fontRoleChanged( role ); @@ -173,7 +174,7 @@ void QskTextLabel::resetFontRole() Q_EMIT fontRoleChanged( fontRoleHint( Text ) ); } -int QskTextLabel::fontRole() const +QskFontRole QskTextLabel::fontRole() const { return fontRoleHint( Text ); } diff --git a/src/controls/QskTextLabel.h b/src/controls/QskTextLabel.h index 4911a8dd..3eef62f1 100644 --- a/src/controls/QskTextLabel.h +++ b/src/controls/QskTextLabel.h @@ -9,13 +9,15 @@ #include "QskControl.h" #include "QskTextOptions.h" +class QskFontRole; + class QSK_EXPORT QskTextLabel : public QskControl { Q_OBJECT - Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged ) + Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged USER true ) - Q_PROPERTY( int fontRole READ fontRole + Q_PROPERTY( QskFontRole fontRole READ fontRole WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged ) Q_PROPERTY( QFont font READ font ) @@ -44,9 +46,9 @@ class QSK_EXPORT QskTextLabel : public QskControl QString text() const; - void setFontRole( int role ); + void setFontRole( const QskFontRole& ); void resetFontRole(); - int fontRole() const; + QskFontRole fontRole() const; void setTextColor( const QColor& ); void resetTextColor(); @@ -80,7 +82,7 @@ class QSK_EXPORT QskTextLabel : public QskControl void textChanged( const QString& ); void textColorChanged( const QColor& ); void textOptionsChanged( const QskTextOptions& ); - void fontRoleChanged( int ); + void fontRoleChanged( const QskFontRole& ); void alignmentChanged( Qt::Alignment ); void panelChanged( bool ); diff --git a/src/controls/QskVariantAnimator.cpp b/src/controls/QskVariantAnimator.cpp index 7518cc9a..1b2b986a 100644 --- a/src/controls/QskVariantAnimator.cpp +++ b/src/controls/QskVariantAnimator.cpp @@ -210,30 +210,46 @@ void QskVariantAnimator::done() } bool QskVariantAnimator::maybeInterpolate( - const QVariant& value1, const QVariant& value2 ) + const QVariant& value1, const QVariant& value2, bool acceptIdentity ) { if ( !value1.isValid() && !value2.isValid() ) return false; - const auto type1 = qskMetaType( value1 ); - const auto type2 = qskMetaType( value2 ); - - if ( !value1.isValid() ) - return value2 != qskDefaultVariant( type2 ); - - if ( !value2.isValid() ) - return value1 != qskDefaultVariant( type1 ); - - if ( type1 != type2 ) + if ( acceptIdentity ) { - if ( value1.canConvert( type2 ) ) - return value2 != qskConvertedVariant( value1, type2 ); + if ( value1.isValid() && value2.isValid() ) + { + const auto type1 = qskMetaType( value1 ); + const auto type2 = qskMetaType( value2 ); - if ( value2.canConvert( type1 ) ) - return value1 != qskConvertedVariant( value2, type1 ); + if ( type1 != type2 ) + return value1.canConvert( type2 ) || value2.canConvert( type1 ); + } - return false; + return true; } + else + { + const auto type1 = qskMetaType( value1 ); + const auto type2 = qskMetaType( value2 ); - return value1 != value2; + if ( !value1.isValid() ) + return value2 != qskDefaultVariant( type2 ); + + if ( !value2.isValid() ) + return value1 != qskDefaultVariant( type1 ); + + if ( type1 != type2 ) + { + if ( value1.canConvert( type2 ) ) + return value2 != qskConvertedVariant( value1, type2 ); + + if ( value2.canConvert( type1 ) ) + return value1 != qskConvertedVariant( value2, type1 ); + + return false; + } + + return value1 != value2; + } } diff --git a/src/controls/QskVariantAnimator.h b/src/controls/QskVariantAnimator.h index 0f86e78a..5f233f63 100644 --- a/src/controls/QskVariantAnimator.h +++ b/src/controls/QskVariantAnimator.h @@ -24,7 +24,9 @@ class QSK_EXPORT QskVariantAnimator : public QskAnimator void setEndValue( const QVariant& ); QVariant endValue() const; - static bool maybeInterpolate( const QVariant&, const QVariant& ); + static bool maybeInterpolate( + const QVariant&, const QVariant&, bool acceptIdentity ); + static bool convertValues( QVariant&, QVariant& ); protected: diff --git a/src/dialogs/QskDialog.cpp b/src/dialogs/QskDialog.cpp index c3f21a97..fc2493c8 100644 --- a/src/dialogs/QskDialog.cpp +++ b/src/dialogs/QskDialog.cpp @@ -13,7 +13,6 @@ #include "QskSelectionWindow.h" #include "QskFocusIndicator.h" -#include "QskStandardSymbol.h" #include #include @@ -79,7 +78,10 @@ static void qskSetupSubWindow( { subWindow->setPopupFlag( QskPopup::DeleteOnClose ); subWindow->setModal( true ); - subWindow->setWindowTitle( title ); +#if 0 + subWindow->setWindowTitle( ... ); +#endif + subWindow->setTitle( title ); subWindow->setDialogActions( actions ); if ( actions != QskDialog::NoAction && defaultAction == QskDialog::NoAction ) @@ -126,11 +128,11 @@ static void qskSetupWindow( static QskDialog::Action qskMessageSubWindow( QQuickWindow* window, const QString& title, - const QString& text, int symbolType, QskDialog::Actions actions, + const QString& text, uint priority, QskDialog::Actions actions, QskDialog::Action defaultAction ) { auto subWindow = new QskMessageSubWindow( window->contentItem() ); - subWindow->setSymbolType( symbolType ); + subWindow->setPriority( priority ); subWindow->setText( text ); qskSetupSubWindow( title, actions, defaultAction, subWindow ); @@ -148,11 +150,12 @@ static QskDialog::Action qskMessageSubWindow( static QskDialog::Action qskMessageWindow( QWindow* transientParent, const QString& title, - const QString& text, int symbolType, QskDialog::Actions actions, + const QString& text, uint priority, QskDialog::Actions actions, QskDialog::Action defaultAction ) { + Q_UNUSED( priority ); // can we do something with it ? + QskMessageWindow messageWindow; - messageWindow.setSymbolType( symbolType ); messageWindow.setText( text ); qskSetupWindow( transientParent, title, actions, defaultAction, &messageWindow ); @@ -169,12 +172,11 @@ static QskDialog::Action qskMessageWindow( } static QString qskSelectSubWindow( - QQuickWindow* window, const QString& title, const QString& text, + QQuickWindow* window, const QString& title, QskDialog::Actions actions, QskDialog::Action defaultAction, const QStringList& entries, int selectedRow ) { auto subWindow = new QskSelectionSubWindow( window->contentItem() ); - subWindow->setInfoText( text ); subWindow->setEntries( entries ); subWindow->setSelectedRow( selectedRow ); @@ -188,12 +190,11 @@ static QString qskSelectSubWindow( } static QString qskSelectWindow( - QWindow* transientParent, const QString& title, const QString& text, + QWindow* transientParent, const QString& title, QskDialog::Actions actions, QskDialog::Action defaultAction, const QStringList& entries, int selectedRow ) { QskSelectionWindow window; - window.setInfoText( text ); window.setEntries( entries ); window.setSelectedRow( selectedRow ); @@ -257,7 +258,7 @@ QWindow* QskDialog::transientParent() const } QskDialog::Action QskDialog::message( - const QString& title, const QString& text, int symbolType, + const QString& title, const QString& text, uint priority, Actions actions, Action defaultAction ) const { if ( m_data->policy == EmbeddedBox ) @@ -270,48 +271,29 @@ QskDialog::Action QskDialog::message( if ( quickWindow ) { return qskMessageSubWindow( quickWindow, - title, text, symbolType, actions, defaultAction ); + title, text, priority, actions, defaultAction ); } } return qskMessageWindow( m_data->transientParent, - title, text, symbolType, actions, defaultAction ); + title, text, priority, actions, defaultAction ); } QskDialog::Action QskDialog::information( const QString& title, const QString& text, Actions actions, Action defaultAction ) const { - return QskDialog::message( title, text, - QskStandardSymbol::Information, actions, defaultAction ); -} - -QskDialog::Action QskDialog::warning( - const QString& title, const QString& text, - Actions actions, Action defaultAction ) const -{ - return QskDialog::message( title, text, - QskStandardSymbol::Warning, actions, defaultAction ); -} - -QskDialog::Action QskDialog::critical( - const QString& title, const QString& text, - Actions actions, Action defaultAction ) const -{ - return QskDialog::message( title, text, - QskStandardSymbol::Critical, actions, defaultAction ); + return QskDialog::message( title, text, 0, actions, defaultAction ); } QskDialog::Action QskDialog::question( const QString& title, const QString& text, Actions actions, Action defaultAction ) const { - return QskDialog::message( title, text, - QskStandardSymbol::Question, actions, defaultAction ); + return QskDialog::message( title, text, 0, actions, defaultAction ); } -QString QskDialog::select( - const QString& title, const QString& text, +QString QskDialog::select( const QString& title, const QStringList& entries, int selectedRow ) const { #if 1 @@ -330,11 +312,11 @@ QString QskDialog::select( if ( quickWindow ) { return qskSelectSubWindow( quickWindow, - title, text, actions, defaultAction, entries, selectedRow ); + title, actions, defaultAction, entries, selectedRow ); } } - return qskSelectWindow( m_data->transientParent, title, text, + return qskSelectWindow( m_data->transientParent, title, actions, defaultAction, entries, selectedRow ); } diff --git a/src/dialogs/QskDialog.h b/src/dialogs/QskDialog.h index 988e9162..26f4bbda 100644 --- a/src/dialogs/QskDialog.h +++ b/src/dialogs/QskDialog.h @@ -27,7 +27,7 @@ class QSK_EXPORT QskDialog : public QObject Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged ) - Q_PROPERTY( QWindow* transientParent READ transientParent + Q_PROPERTY( QWindow * transientParent READ transientParent WRITE setTransientParent NOTIFY transientParentChanged ) public: @@ -115,28 +115,19 @@ class QSK_EXPORT QskDialog : public QObject Q_INVOKABLE QWindow* transientParent() const; Q_INVOKABLE Action message( - const QString& title, const QString& text, int symbolType, + const QString& title, const QString& text, uint priority = 0, Actions actions = Ok, Action defaultAction = NoAction ) const; Q_INVOKABLE Action information( const QString& title, const QString& text, Actions actions = Ok, Action defaultAction = NoAction ) const; - Q_INVOKABLE Action warning( - const QString& title, const QString& text, - Actions actions = Ok, Action defaultAction = NoAction ) const; - - Q_INVOKABLE Action critical( - const QString& title, const QString& text, - Actions actions = Ok, Action defaultAction = NoAction ) const; - Q_INVOKABLE Action question( const QString& title, const QString& text, Actions actions = Actions( Yes | No ), Action defaultAction = NoAction ) const; - Q_INVOKABLE QString select( - const QString& title, const QString& text, + Q_INVOKABLE QString select( const QString& title, const QStringList& entries, int selectedRow = 0 ) const; static ActionRole actionRole( Action action ); diff --git a/src/dialogs/QskDialogSubWindow.cpp b/src/dialogs/QskDialogSubWindow.cpp index 22dd751f..3cde7a12 100644 --- a/src/dialogs/QskDialogSubWindow.cpp +++ b/src/dialogs/QskDialogSubWindow.cpp @@ -5,13 +5,21 @@ #include "QskDialogSubWindow.h" #include "QskDialogButtonBox.h" +#include "QskTextLabel.h" #include "QskPushButton.h" +#include "QskLinearBox.h" #include "QskQuick.h" #include "QskEvent.h" +#if 1 +#include "QskSkin.h" +#include +#endif #include #include +QSK_SUBCONTROL( QskDialogSubWindow, DialogTitle ) + static inline void qskSetRejectOnClose( QskDialogSubWindow* subWindow, bool on ) { if ( on ) @@ -26,15 +34,33 @@ static inline void qskSetRejectOnClose( QskDialogSubWindow* subWindow, bool on ) } } +namespace +{ + class TitleLabel final : public QskTextLabel + { + protected: + QskAspect::Subcontrol substitutedSubcontrol( + QskAspect::Subcontrol subControl ) const override + { + if ( subControl == QskTextLabel::Text ) + return QskDialogSubWindow::DialogTitle; + + return QskTextLabel::substitutedSubcontrol( subControl ); + } + }; +} + class QskDialogSubWindow::PrivateData { public: QskDialog::Actions actions = QskDialog::NoAction; - QPointer< QQuickItem > contentItem; + QskTextLabel* titleLabel = nullptr; QskDialogButtonBox* buttonBox = nullptr; - QMarginsF contentPadding; + QPointer< QQuickItem > contentItem; + + QskLinearBox* layout = nullptr; QskDialog::DialogCode result = QskDialog::Rejected; }; @@ -43,8 +69,14 @@ QskDialogSubWindow::QskDialogSubWindow( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - setPolishOnResize( true ); qskSetRejectOnClose( this, true ); + + m_data->layout = new QskLinearBox( Qt::Vertical, this ); + m_data->layout->setSizePolicy( + QskSizePolicy::MinimumExpanding, QskSizePolicy::Constrained ); + + setPolishOnResize( true ); + setPolishOnParentResize( true ); } QskDialogSubWindow::~QskDialogSubWindow() @@ -58,8 +90,7 @@ void QskDialogSubWindow::addDialogAction( QskDialog::Action action ) if ( m_data->buttonBox == nullptr ) initButtonBox(); - if ( m_data->buttonBox ) - m_data->buttonBox->addAction( action ); + m_data->buttonBox->addAction( action ); } } @@ -71,8 +102,7 @@ void QskDialogSubWindow::addDialogButton( if ( m_data->buttonBox == nullptr ) initButtonBox(); - if ( m_data->buttonBox ) - m_data->buttonBox->addButton( button, actionRole ); + m_data->buttonBox->addButton( button, actionRole ); } } @@ -85,37 +115,17 @@ void QskDialogSubWindow::setDialogActions( QskDialog::Actions actions ) if ( actions == QskDialog::NoAction ) { - if ( m_data->buttonBox->parent() == this ) - { - delete m_data->buttonBox; - } - else - { - m_data->buttonBox->setParentItem( nullptr ); - - disconnect( m_data->buttonBox, &QskDialogButtonBox::accepted, - this, &QskDialogSubWindow::accept ); - - disconnect( m_data->buttonBox, &QskDialogButtonBox::rejected, - this, &QskDialogSubWindow::reject ); - - } - + delete m_data->buttonBox; m_data->buttonBox = nullptr; } else { if ( m_data->buttonBox == nullptr ) - { initButtonBox(); - } if ( m_data->buttonBox ) m_data->buttonBox->setActions( actions ); } - - resetImplicitSize(); - polish(); } QskDialog::Actions QskDialogSubWindow::dialogActions() const @@ -126,6 +136,62 @@ QskDialog::Actions QskDialogSubWindow::dialogActions() const return QskDialog::NoAction; } +void QskDialogSubWindow::setTitle( const QString& title ) +{ + bool changed = false; + auto& titleLabel = m_data->titleLabel; + + if ( title.isEmpty() ) + { + changed = ( titleLabel != nullptr ); + + delete titleLabel; + titleLabel = nullptr; + } + else + { + if ( titleLabel == nullptr ) + { + titleLabel = new TitleLabel(); + m_data->layout->insertItem( 0, titleLabel ); + changed = true; + } + else + { + changed = ( titleLabel->text() != title ); + } + + if ( changed ) + titleLabel->setText( title ); + } + + if ( changed ) + { + resetImplicitSize(); + polish(); + + Q_EMIT titleChanged( title ); + } +} + +QString QskDialogSubWindow::title() const +{ + if ( auto label = m_data->titleLabel ) + return label->text(); + + return QString(); +} + +QskTextLabel* QskDialogSubWindow::titleLabel() +{ + return m_data->titleLabel; +} + +const QskTextLabel* QskDialogSubWindow::titleLabel() const +{ + return m_data->titleLabel; +} + void QskDialogSubWindow::setContentItem( QQuickItem* item ) { if ( item == m_data->contentItem ) @@ -133,24 +199,19 @@ void QskDialogSubWindow::setContentItem( QQuickItem* item ) if ( m_data->contentItem ) { - if ( m_data->contentItem->parent() == this ) + if ( m_data->contentItem->parent() == m_data->layout ) delete m_data->contentItem; else - m_data->contentItem->setParentItem( nullptr ); + m_data->layout->removeItem( m_data->contentItem ); } m_data->contentItem = item; if ( item ) { - item->setParentItem( this ); - - if ( item->parent() == nullptr ) - item->setParent( this ); + const int index = m_data->titleLabel ? 1 : 0; + m_data->layout->insertItem( index, m_data->contentItem ); } - - resetImplicitSize(); - polish(); } QQuickItem* QskDialogSubWindow::contentItem() const @@ -161,19 +222,12 @@ QQuickItem* QskDialogSubWindow::contentItem() const void QskDialogSubWindow::setContentPadding( const QMarginsF& padding ) { // should be a skin hint ??? - - if ( m_data->contentPadding != padding ) - { - m_data->contentPadding = padding; - - resetImplicitSize(); - polish(); - } + m_data->layout->setMargins( padding ); } QMarginsF QskDialogSubWindow::contentPadding() const { - return m_data->contentPadding; + return m_data->layout->margins(); } void QskDialogSubWindow::setDefaultDialogAction( QskDialog::Action action ) @@ -287,134 +341,64 @@ void QskDialogSubWindow::keyPressEvent( QKeyEvent* event ) Inherited::keyPressEvent( event ); } -QskDialogButtonBox* QskDialogSubWindow::createButtonBox() -{ - return new QskDialogButtonBox(); -} - void QskDialogSubWindow::initButtonBox() { - m_data->buttonBox = createButtonBox(); + auto buttonBox = new QskDialogButtonBox(); + m_data->layout->addItem( buttonBox ); - if ( m_data->buttonBox ) - { - m_data->buttonBox->setParentItem( this ); + connect( buttonBox, &QskDialogButtonBox::accepted, + this, &QskDialogSubWindow::accept ); - if ( m_data->buttonBox->parent() == nullptr ) - m_data->buttonBox->setParent( this ); + connect( buttonBox, &QskDialogButtonBox::rejected, + this, &QskDialogSubWindow::reject ); - connect( m_data->buttonBox, &QskDialogButtonBox::accepted, - this, &QskDialogSubWindow::accept, Qt::UniqueConnection ); - - connect( m_data->buttonBox, &QskDialogButtonBox::rejected, - this, &QskDialogSubWindow::reject, Qt::UniqueConnection ); - } -} - -void QskDialogSubWindow::aboutToShow() -{ - if ( size().isEmpty() ) - { - // setting an initial size from the hint, centered inside the window - - const qreal cx = 0.5 * parentItem()->width(); - const qreal cy = 0.5 * parentItem()->height(); - - QRectF rect; - rect.setSize( sizeConstraint() ); - rect.moveCenter( QPointF( cx, cy ) ); - - setGeometry( rect ); - } - - Inherited::aboutToShow(); + m_data->buttonBox = buttonBox; } void QskDialogSubWindow::updateLayout() { - Inherited::updateLayout(); + updateGeometry(); + m_data->layout->setGeometry( layoutRect() ); +} - auto rect = layoutRect(); +void QskDialogSubWindow::updateGeometry() +{ + /* + This code is for Preferred/Constrained without checking + the actual sizePolicy. TODO ... + */ + const auto minSize = minimumSize(); + const auto maxSize = maximumSize(); - if ( m_data->buttonBox && m_data->buttonBox->isVisibleToParent() ) - { - const auto h = m_data->buttonBox->sizeConstraint().height(); - rect.setBottom( rect.bottom() - h ); + auto width = sizeHint().width(); + width = qMin( width, maxSize.width() ); + width = qMax( width, minSize.width() ); - m_data->buttonBox->setGeometry( rect.x(), rect.bottom(), rect.width(), h ); - } + auto height = heightForWidth( width ); + height = qMin( height, maxSize.height() ); + height = qMax( height, minSize.height() ); - if ( m_data->contentItem ) - { - rect = rect.marginsRemoved( m_data->contentPadding ); - qskSetItemGeometry( m_data->contentItem, rect ); - } + QRectF rect( 0.0, 0.0, width, height ); + rect.moveCenter( qskItemRect( parentItem() ).center() ); + + setGeometry( rect ); } QSizeF QskDialogSubWindow::layoutSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const { - if ( which != Qt::PreferredSize ) - return QSizeF(); + if ( which == Qt::MaximumSize && polishOnParentResize() ) + return 0.9 * parentItem()->size(); - QSizeF buttonBoxHint; + auto size = m_data->layout->effectiveSizeHint( which, constraint ); - qreal constraintHeight = constraint.height(); - - if ( auto buttonBox = m_data->buttonBox ) + if ( which == Qt::MinimumSize ) { - if ( buttonBox->isVisibleToLayout() ) - { - buttonBoxHint = buttonBox->sizeConstraint( - which, QSizeF( constraint.width(), -1 ) ); - - if ( constraint.width() >= 0.0 ) - buttonBoxHint.rwidth() = constraint.width(); - - if ( constraintHeight >= 0.0 && buttonBoxHint.height() >= 0.0 ) - { - constraintHeight -= buttonBoxHint.height(); - constraintHeight = qMax( constraintHeight, 0.0 ); - } - } + const auto w = qMax( qskDpToPixels( 300.0 ), size.width() ); + size.setWidth( w ); } - QSizeF contentHint; - - if ( qskIsVisibleToLayout( m_data->contentItem ) ) - { - const auto& m = m_data->contentPadding; - const qreal dw = m.left() + m.right(); - const qreal dh = m.top() + m.bottom(); - - qreal constraintWidth = constraint.width(); - - if ( constraintWidth > 0.0 ) - constraintWidth = qMax( constraintWidth - dw, 0.0 ); - - if ( constraintHeight > 0.0 ) - constraintHeight = qMax( constraintHeight - dh, 0.0 ); - - contentHint = qskSizeConstraint( m_data->contentItem, - which, QSizeF( constraintWidth, constraintHeight ) ); - - if ( contentHint.width() >= 0 ) - contentHint.rwidth() += dw; - - if ( contentHint.height() >= 0 ) - contentHint.rheight() += dh; - } - - qreal w = -1; - w = qMax( w, buttonBoxHint.width() ); - w = qMax( w, contentHint.width() ); - - qreal h = -1; - - if ( buttonBoxHint.height() > 0.0 && contentHint.height() > 0.0 ) - h = buttonBoxHint.height() + contentHint.height(); - - return QSizeF( w, h ); + return size; } #include "moc_QskDialogSubWindow.cpp" diff --git a/src/dialogs/QskDialogSubWindow.h b/src/dialogs/QskDialogSubWindow.h index 2a045e90..077ef65f 100644 --- a/src/dialogs/QskDialogSubWindow.h +++ b/src/dialogs/QskDialogSubWindow.h @@ -11,6 +11,7 @@ class QskDialogButtonBox; class QskPushButton; +class QskTextLabel; class QSK_EXPORT QskDialogSubWindow : public QskSubWindow { @@ -19,9 +20,14 @@ class QSK_EXPORT QskDialogSubWindow : public QskSubWindow Q_PROPERTY( QskDialog::Actions dialogActions READ dialogActions WRITE setDialogActions ) + Q_PROPERTY( QString title READ title + WRITE setTitle NOTIFY titleChanged ) + using Inherited = QskSubWindow; public: + QSK_SUBCONTROLS( DialogTitle ) + QskDialogSubWindow( QQuickItem* parent = nullptr ); ~QskDialogSubWindow() override; @@ -47,11 +53,19 @@ class QSK_EXPORT QskDialogSubWindow : public QskSubWindow void setContentItem( QQuickItem* ); QQuickItem* contentItem() const; + void setTitle( const QString& ); + QString title() const; + + QskTextLabel* titleLabel(); + const QskTextLabel* titleLabel() const; + // padding around the contentItem void setContentPadding( const QMarginsF& ); QMarginsF contentPadding() const; Q_SIGNALS: + void titleChanged( const QString& ); + void finished( QskDialog::DialogCode ); void accepted(); void rejected(); @@ -64,14 +78,14 @@ class QSK_EXPORT QskDialogSubWindow : public QskSubWindow protected: void setResult( QskDialog::DialogCode ); + void keyPressEvent( QKeyEvent* ) override; + virtual void updateGeometry(); + void updateLayout() override; - void aboutToShow() override; - QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const override; - - virtual QskDialogButtonBox* createButtonBox(); + QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const; private: void initButtonBox(); diff --git a/src/dialogs/QskMessageSubWindow.cpp b/src/dialogs/QskMessageSubWindow.cpp index ad9fde29..68ba7ab1 100644 --- a/src/dialogs/QskMessageSubWindow.cpp +++ b/src/dialogs/QskMessageSubWindow.cpp @@ -4,95 +4,39 @@ *****************************************************************************/ #include "QskMessageSubWindow.h" -#include "QskGraphic.h" -#include "QskGraphicLabel.h" -#include "QskLinearBox.h" -#include "QskStandardSymbol.h" #include "QskTextLabel.h" -#include - namespace { class TextLabel final : public QskTextLabel { public: - TextLabel( QskMessageSubWindow* box ) + TextLabel() { setObjectName( QStringLiteral( "QskMessageSubWindowTextLabel" ) ); - initSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Preferred ); + + initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::ConstrainedExpanding ); + setLayoutAlignmentHint( Qt::AlignLeft | Qt::AlignTop ); setAlignment( Qt::AlignLeft | Qt::AlignTop ); setWrapMode( QskTextOptions::WordWrap ); - - connect( this, &QskTextLabel::textChanged, - box, &QskMessageSubWindow::textChanged ); - - connect( this, &QskTextLabel::textOptionsChanged, - box, &QskMessageSubWindow::textOptionsChanged ); - } - }; - - class SymbolLabel final : public QskGraphicLabel - { - public: - SymbolLabel( QskMessageSubWindow* ) - { - setObjectName( QStringLiteral( "QskMessageSubWindowSymbolLabel" ) ); - initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - - setAlignment( Qt::AlignTop | Qt::AlignHCenter ); - updatePreferredSize(); - } - - protected: - void changeEvent( QEvent* event ) override - { - if ( event->type() == QEvent::FontChange ) - updatePreferredSize(); - - QskGraphicLabel::changeEvent( event ); - } - - private: - void updatePreferredSize() - { - // when there is no explicit size known, - // we always adjust the icon according to the font - - if ( graphicStrutSize().isEmpty() ) - { - const QFont font = effectiveFont( QskTextLabel::Text ); - setPreferredSize( -1.0, 1.5 * QFontMetricsF( font ).height() ); - } + setElideMode( Qt::ElideRight ); } }; } -class QskMessageSubWindow::PrivateData -{ - public: - QskGraphicLabel* symbolLabel; - QskTextLabel* textLabel; -}; - QskMessageSubWindow::QskMessageSubWindow( QQuickItem* parent ) : Inherited( parent ) - , m_data( new PrivateData() ) { - m_data->textLabel = new TextLabel( this ); + auto label = new TextLabel(); - m_data->symbolLabel = new SymbolLabel( this ); - m_data->symbolLabel->hide(); + connect( label, &QskTextLabel::textChanged, + this, &QskMessageSubWindow::textChanged ); - auto box = new QskLinearBox( Qt::Horizontal ); - box->setDefaultAlignment( Qt::AlignTop | Qt::AlignHCenter ); - box->setSpacing( 0 ); - box->addItem( m_data->symbolLabel ); - box->addItem( m_data->textLabel ); - box->setStretchFactor( m_data->textLabel, 10 ); + connect( label, &QskTextLabel::textOptionsChanged, + this, &QskMessageSubWindow::textOptionsChanged ); - setContentItem( box ); + setContentItem( label ); } QskMessageSubWindow::~QskMessageSubWindow() @@ -101,58 +45,30 @@ QskMessageSubWindow::~QskMessageSubWindow() void QskMessageSubWindow::setText( const QString& text ) { - m_data->textLabel->setText( text ); + if ( auto label = qobject_cast< QskTextLabel* >( contentItem() ) ) + label->setText( text ); } QString QskMessageSubWindow::text() const { - return m_data->textLabel->text(); + if ( auto label = qobject_cast< const QskTextLabel* >( contentItem() ) ) + return label->text(); + + return QString(); } void QskMessageSubWindow::setTextOptions( const QskTextOptions& options ) { - m_data->textLabel->setTextOptions( options ); + if ( auto label = qobject_cast< QskTextLabel* >( contentItem() ) ) + label->setTextOptions( options ); } QskTextOptions QskMessageSubWindow::textOptions() const { - return m_data->textLabel->textOptions(); -} + if ( auto label = qobject_cast< const QskTextLabel* >( contentItem() ) ) + return label->textOptions(); -void QskMessageSubWindow::setSymbolSource( const QUrl& url ) -{ - m_data->symbolLabel->setSource( url ); - m_data->symbolLabel->setVisible( !url.isEmpty() ); - - if ( auto box = qobject_cast< QskLinearBox* >( contentItem() ) ) - box->setSpacing( m_data->symbolLabel->isVisible() ? 15 : 0 ); // metrics !! -} - -void QskMessageSubWindow::setSymbolType( int symbolType ) -{ -#if 1 - const auto graphic = QskStandardSymbol::graphic( - static_cast< QskStandardSymbol::Type >( symbolType ) ); -#else - const auto graphic = symbolHint( ... ); // TODO -#endif - setSymbol( graphic ); -} - -QUrl QskMessageSubWindow::symbolSource() const -{ - return m_data->symbolLabel->source(); -} - -void QskMessageSubWindow::setSymbol( const QskGraphic& symbol ) -{ - m_data->symbolLabel->setVisible( !symbol.isNull() ); - m_data->symbolLabel->setGraphic( symbol ); -} - -QskGraphic QskMessageSubWindow::symbol() const -{ - return m_data->symbolLabel->graphic(); + return QskTextOptions(); } #include "moc_QskMessageSubWindow.cpp" diff --git a/src/dialogs/QskMessageSubWindow.h b/src/dialogs/QskMessageSubWindow.h index fe4cb75b..22d6d197 100644 --- a/src/dialogs/QskMessageSubWindow.h +++ b/src/dialogs/QskMessageSubWindow.h @@ -8,7 +8,6 @@ #include "QskDialogSubWindow.h" -class QskGraphic; class QskTextOptions; class QSK_EXPORT QskMessageSubWindow : public QskDialogSubWindow @@ -21,8 +20,6 @@ class QSK_EXPORT QskMessageSubWindow : public QskDialogSubWindow Q_PROPERTY( QskTextOptions textOptions READ textOptions WRITE setTextOptions NOTIFY textOptionsChanged ) - Q_PROPERTY( QUrl symbolSource READ symbolSource WRITE setSymbolSource ) - using Inherited = QskDialogSubWindow; public: @@ -34,24 +31,12 @@ class QSK_EXPORT QskMessageSubWindow : public QskDialogSubWindow QString text() const; - void setSymbolSource( const QUrl& url ); - QUrl symbolSource() const; - - void setSymbolType( int symbolType ); - - void setSymbol( const QskGraphic& ); - QskGraphic symbol() const; - public Q_SLOTS: void setText( const QString& ); Q_SIGNALS: void textChanged( const QString& ); void textOptionsChanged( const QskTextOptions& ); - - private: - class PrivateData; - std::unique_ptr< PrivateData > m_data; }; #endif diff --git a/src/dialogs/QskMessageWindow.cpp b/src/dialogs/QskMessageWindow.cpp index e22d3e55..1a49a3f1 100644 --- a/src/dialogs/QskMessageWindow.cpp +++ b/src/dialogs/QskMessageWindow.cpp @@ -4,15 +4,9 @@ *****************************************************************************/ #include "QskMessageWindow.h" -#include "QskGraphicLabel.h" -#include "QskGraphic.h" -#include "QskLinearBox.h" -#include "QskStandardSymbol.h" #include "QskTextLabel.h" #include "QskTextOptions.h" -#include - namespace { class TextLabel final : public QskTextLabel @@ -33,47 +27,11 @@ namespace box, &QskMessageWindow::textOptionsChanged ); } }; - - class SymbolLabel final : public QskGraphicLabel - { - public: - SymbolLabel( QskMessageWindow* ) - { - setObjectName( QStringLiteral( "QskMessageWindowSymbolLabel" ) ); - initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - - setAlignment( Qt::AlignTop | Qt::AlignHCenter ); - updatePreferredSize(); - } - - protected: - void changeEvent( QEvent* event ) override - { - if ( event->type() == QEvent::FontChange ) - updatePreferredSize(); - - QskGraphicLabel::changeEvent( event ); - } - - private: - void updatePreferredSize() - { - // when there is no explicit size known, - // we always adjust the icon according to the font - - if ( graphicStrutSize().isEmpty() ) - { - const QFont font = effectiveFont( QskTextLabel::Text ); - setPreferredSize( -1.0, 1.5 * QFontMetricsF( font ).height() ); - } - } - }; } class QskMessageWindow::PrivateData { public: - QskGraphicLabel* symbolLabel; QskTextLabel* textLabel; }; @@ -85,19 +43,7 @@ QskMessageWindow::QskMessageWindow( QWindow* parent ) Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint ); m_data->textLabel = new TextLabel( this ); - - m_data->symbolLabel = new SymbolLabel( this ); - m_data->symbolLabel->hide(); - - auto box = new QskLinearBox( Qt::Horizontal ); - - box->setDefaultAlignment( Qt::AlignTop | Qt::AlignHCenter ); - box->setSpacing( 0 ); - box->addItem( m_data->symbolLabel ); - box->addItem( m_data->textLabel ); - box->setStretchFactor( m_data->textLabel, 10 ); - - setDialogContentItem( box ); + setDialogContentItem( m_data->textLabel ); } QskMessageWindow::~QskMessageWindow() @@ -124,41 +70,4 @@ QskTextOptions QskMessageWindow::textOptions() const return m_data->textLabel->textOptions(); } -void QskMessageWindow::setSymbolSource( const QUrl& url ) -{ - m_data->symbolLabel->setSource( url ); - m_data->symbolLabel->setVisible( !url.isEmpty() ); - - if ( auto box = qobject_cast< QskLinearBox* >( contentItem() ) ) - box->setSpacing( m_data->symbolLabel->isVisible() ? 15 : 0 ); // metrics !! -} - -void QskMessageWindow::setSymbolType( int symbolType ) -{ -#if 1 - const auto graphic = QskStandardSymbol::graphic( - static_cast< QskStandardSymbol::Type >( symbolType ) ); -#else - const auto graphic = symbolHint( ... ); // TODO -#endif - - setSymbol( graphic ); -} - -QUrl QskMessageWindow::symbolSource() const -{ - return m_data->symbolLabel->source(); -} - -void QskMessageWindow::setSymbol( const QskGraphic& symbol ) -{ - m_data->symbolLabel->setVisible( !symbol.isNull() ); - m_data->symbolLabel->setGraphic( symbol ); -} - -QskGraphic QskMessageWindow::symbol() const -{ - return m_data->symbolLabel->graphic(); -} - #include "moc_QskMessageWindow.cpp" diff --git a/src/dialogs/QskMessageWindow.h b/src/dialogs/QskMessageWindow.h index 1a65fca3..69086451 100644 --- a/src/dialogs/QskMessageWindow.h +++ b/src/dialogs/QskMessageWindow.h @@ -9,7 +9,6 @@ #include "QskDialogWindow.h" class QskTextOptions; -class QskGraphic; class QSK_EXPORT QskMessageWindow : public QskDialogWindow { @@ -21,8 +20,6 @@ class QSK_EXPORT QskMessageWindow : public QskDialogWindow Q_PROPERTY( QskTextOptions textOptions READ textOptions WRITE setTextOptions NOTIFY textOptionsChanged ) - Q_PROPERTY( QUrl symbolSource READ symbolSource WRITE setSymbolSource ) - using Inherited = QskDialogWindow; public: @@ -34,14 +31,6 @@ class QSK_EXPORT QskMessageWindow : public QskDialogWindow QString text() const; - void setSymbolSource( const QUrl& url ); - QUrl symbolSource() const; - - void setSymbolType( int symbolType ); - - void setSymbol( const QskGraphic& ); - QskGraphic symbol() const; - public Q_SLOTS: void setText( const QString& ); diff --git a/src/dialogs/QskSelectionSubWindow.cpp b/src/dialogs/QskSelectionSubWindow.cpp index 91a22f38..44e44b72 100644 --- a/src/dialogs/QskSelectionSubWindow.cpp +++ b/src/dialogs/QskSelectionSubWindow.cpp @@ -4,31 +4,26 @@ *****************************************************************************/ #include "QskSelectionSubWindow.h" -#include "QskLinearBox.h" #include "QskSimpleListBox.h" -#include "QskTextLabel.h" + +/* + QInputDialog uses a combo box instead of a list widget + Guess we should do the same TODO ... + */ +static inline QskSimpleListBox* qskListBox( + QskSelectionSubWindow* subWindow ) +{ + return qobject_cast< QskSimpleListBox* >( subWindow->contentItem() ); +} + +static inline const QskSimpleListBox* qskListBox( + const QskSelectionSubWindow* subWindow ) +{ + return qobject_cast< QskSimpleListBox* >( subWindow->contentItem() ); +} namespace { - class TextLabel final : public QskTextLabel - { - public: - TextLabel( QskSelectionSubWindow* subWindow ) - { - setObjectName( QStringLiteral( "QskSelectionSubWindowTextLabel" ) ); - initSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Constrained ); - - setAlignment( Qt::AlignLeft | Qt::AlignTop ); - setWrapMode( QskTextOptions::WordWrap ); - - connect( this, &QskTextLabel::textChanged, - subWindow, &QskSelectionSubWindow::infoTextChanged ); - - connect( this, &QskTextLabel::textOptionsChanged, - subWindow, &QskSelectionSubWindow::infoTextOptionsChanged ); - } - }; - class ListBox final : public QskSimpleListBox { public: @@ -48,32 +43,15 @@ namespace }; } -class QskSelectionSubWindow::PrivateData -{ - public: - QskTextLabel* textLabel; - QskSimpleListBox* listBox; -}; - QskSelectionSubWindow::QskSelectionSubWindow( QQuickItem* parent ) : Inherited( parent ) - , m_data( new PrivateData ) { - m_data->textLabel = new TextLabel( this ); - m_data->textLabel->setVisible( false ); - - m_data->listBox = new ListBox( this ); + auto listBox = new ListBox( this ); #if 1 - m_data->listBox->setPreferredSize( 500, 500 ); + listBox->setPreferredSize( 500, 500 ); #endif - auto box = new QskLinearBox( Qt::Vertical ); - box->setSpacing( 10 ); // hint - box->addItem( m_data->textLabel ); - box->addItem( m_data->listBox ); - box->setStretchFactor( 1, 10 ); - - setContentItem( box ); + setContentItem( listBox ); setDialogActions( QskDialog::Ok | QskDialog::Cancel ); } @@ -81,54 +59,40 @@ QskSelectionSubWindow::~QskSelectionSubWindow() { } -void QskSelectionSubWindow::setInfoText( const QString& text ) -{ - m_data->textLabel->setText( text ); - m_data->textLabel->setVisible( !text.isEmpty() ); -} - -QString QskSelectionSubWindow::infoText() const -{ - return m_data->textLabel->text(); -} - -void QskSelectionSubWindow::setInfoTextOptions( const QskTextOptions& options ) -{ - if ( options != infoTextOptions() ) - { - m_data->textLabel->setTextOptions( options ); - Q_EMIT infoTextOptionsChanged( options ); - } -} - -QskTextOptions QskSelectionSubWindow::infoTextOptions() const -{ - return m_data->textLabel->textOptions(); -} - void QskSelectionSubWindow::setEntries( const QStringList& entries ) { - m_data->listBox->setEntries( entries ); + if ( auto listBox = qskListBox( this ) ) + listBox->setEntries( entries ); } QStringList QskSelectionSubWindow::entries() const { - return m_data->listBox->entries(); + if ( auto listBox = qskListBox( this ) ) + return listBox->entries(); + + return QStringList(); } void QskSelectionSubWindow::setSelectedRow( int row ) { - m_data->listBox->setSelectedRow( row ); + if ( auto listBox = qskListBox( this ) ) + listBox->setSelectedRow( row ); } int QskSelectionSubWindow::selectedRow() const { - return m_data->listBox->selectedRow(); + if ( auto listBox = qskListBox( this ) ) + return listBox->selectedRow(); + + return -1; } QString QskSelectionSubWindow::selectedEntry() const { - return m_data->listBox->selectedEntry(); + if ( auto listBox = qskListBox( this ) ) + listBox->selectedEntry(); + + return QString(); } #include "moc_QskSelectionSubWindow.cpp" diff --git a/src/dialogs/QskSelectionSubWindow.h b/src/dialogs/QskSelectionSubWindow.h index a8373bfa..d459bd05 100644 --- a/src/dialogs/QskSelectionSubWindow.h +++ b/src/dialogs/QskSelectionSubWindow.h @@ -8,18 +8,10 @@ #include "QskDialogSubWindow.h" -class QskTextOptions; - class QSK_EXPORT QskSelectionSubWindow : public QskDialogSubWindow { Q_OBJECT - Q_PROPERTY( QString infoText READ infoText - WRITE setInfoText NOTIFY infoTextChanged ) - - Q_PROPERTY( QskTextOptions infoTextOptions READ infoTextOptions - WRITE setInfoTextOptions NOTIFY infoTextOptionsChanged ) - Q_PROPERTY( QStringList entries READ entries WRITE setEntries NOTIFY entriesChanged FINAL ) @@ -32,30 +24,18 @@ class QSK_EXPORT QskSelectionSubWindow : public QskDialogSubWindow QskSelectionSubWindow( QQuickItem* parent = nullptr ); ~QskSelectionSubWindow() override; - void setInfoTextOptions( const QskTextOptions& ); - QskTextOptions infoTextOptions() const; - - QString infoText() const; - Q_INVOKABLE QStringList entries() const; Q_INVOKABLE int selectedRow() const; Q_INVOKABLE QString selectedEntry() const; public Q_SLOTS: - void setInfoText( const QString& ); void setEntries( const QStringList& ); void setSelectedRow( int row ); Q_SIGNALS: - void infoTextChanged( const QString& ); - void infoTextOptionsChanged( const QskTextOptions& ); void selectedRowChanged( int ); void selectedEntryChanged( const QString& ); void entriesChanged(); - - private: - class PrivateData; - std::unique_ptr< PrivateData > m_data; }; #endif diff --git a/src/dialogs/QskSelectionWindow.cpp b/src/dialogs/QskSelectionWindow.cpp index f14674e3..1d0baa1d 100644 --- a/src/dialogs/QskSelectionWindow.cpp +++ b/src/dialogs/QskSelectionWindow.cpp @@ -4,34 +4,26 @@ *****************************************************************************/ #include "QskSelectionWindow.h" -#include "QskGraphicLabel.h" -#include "QskGraphic.h" -#include "QskLinearBox.h" #include "QskSimpleListBox.h" -#include "QskTextLabel.h" -#include "QskTextOptions.h" + +/* + QInputDialog uses a combo box instead of a list widget + Guess we should do the same TODO ... + */ +static inline QskSimpleListBox* qskListBox( + QskSelectionWindow* window ) +{ + return qobject_cast< QskSimpleListBox* >( window->dialogContentItem() ); +} + +static inline const QskSimpleListBox* qskListBox( + const QskSelectionWindow* window ) +{ + return qobject_cast< QskSimpleListBox* >( window->dialogContentItem() ); +} namespace { - class TextLabel final : public QskTextLabel - { - public: - TextLabel( QskSelectionWindow* window ) - { - setObjectName( QStringLiteral( "QskSelectionWindowTextLabel" ) ); - initSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Preferred ); - - setAlignment( Qt::AlignLeft | Qt::AlignTop ); - setWrapMode( QskTextOptions::WordWrap ); - - connect( this, &QskTextLabel::textChanged, - window, &QskSelectionWindow::infoTextChanged ); - - connect( this, &QskTextLabel::textOptionsChanged, - window, &QskSelectionWindow::infoTextOptionsChanged ); - } - }; - class ListBox final : public QskSimpleListBox { public: @@ -51,36 +43,18 @@ namespace }; } -class QskSelectionWindow::PrivateData -{ - public: - QskTextLabel* textLabel; - QskSimpleListBox* listBox; -}; - QskSelectionWindow::QskSelectionWindow( QWindow* parent ) : Inherited( parent ) - , m_data( new PrivateData ) { setFlags( Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint ); - m_data->textLabel = new TextLabel( this ); - m_data->textLabel->setVisible( false ); - - m_data->listBox = new ListBox( this ); + auto listBox = new ListBox( this ); #if 1 - m_data->listBox->setPreferredSize( 500, 500 ); + listBox->setPreferredSize( 500, 500 ); #endif - auto box = new QskLinearBox( Qt::Vertical ); - box->setMargins( 5 ); - box->setSpacing( 10 ); - box->addItem( m_data->textLabel ); - box->addItem( m_data->listBox ); - box->setStretchFactor( m_data->listBox, 10 ); - - setDialogContentItem( box ); + setDialogContentItem( listBox ); setDialogActions( QskDialog::Ok | QskDialog::Cancel ); } @@ -88,54 +62,40 @@ QskSelectionWindow::~QskSelectionWindow() { } -void QskSelectionWindow::setInfoText( const QString& text ) -{ - m_data->textLabel->setText( text ); - m_data->textLabel->setVisible( !text.isEmpty() ); -} - -QString QskSelectionWindow::infoText() const -{ - return m_data->textLabel->text(); -} - -void QskSelectionWindow::setInfoTextOptions( const QskTextOptions& options ) -{ - if ( options != infoTextOptions() ) - { - m_data->textLabel->setTextOptions( options ); - Q_EMIT infoTextOptionsChanged( options ); - } -} - -QskTextOptions QskSelectionWindow::infoTextOptions() const -{ - return m_data->textLabel->textOptions(); -} - void QskSelectionWindow::setEntries( const QStringList& entries ) { - m_data->listBox->setEntries( entries ); + if ( auto listBox = qskListBox( this ) ) + listBox->setEntries( entries ); } QStringList QskSelectionWindow::entries() const { - return m_data->listBox->entries(); + if ( auto listBox = qskListBox( this ) ) + return listBox->entries(); + + return QStringList(); } void QskSelectionWindow::setSelectedRow( int row ) { - m_data->listBox->setSelectedRow( row ); + if ( auto listBox = qskListBox( this ) ) + listBox->setSelectedRow( row ); } int QskSelectionWindow::selectedRow() const { - return m_data->listBox->selectedRow(); + if ( auto listBox = qskListBox( this ) ) + return listBox->selectedRow(); + + return -1; } QString QskSelectionWindow::selectedEntry() const { - return m_data->listBox->selectedEntry(); + if ( auto listBox = qskListBox( this ) ) + listBox->selectedEntry(); + + return QString(); } #include "moc_QskSelectionWindow.cpp" diff --git a/src/dialogs/QskSelectionWindow.h b/src/dialogs/QskSelectionWindow.h index c54e9879..4d9e51cf 100644 --- a/src/dialogs/QskSelectionWindow.h +++ b/src/dialogs/QskSelectionWindow.h @@ -8,18 +8,10 @@ #include "QskDialogWindow.h" -class QskTextOptions; - class QSK_EXPORT QskSelectionWindow : public QskDialogWindow { Q_OBJECT - Q_PROPERTY( QString infoText READ infoText - WRITE setInfoText NOTIFY infoTextChanged ) - - Q_PROPERTY( QskTextOptions infoTextOptions READ infoTextOptions - WRITE setInfoTextOptions NOTIFY infoTextOptionsChanged ) - Q_PROPERTY( QStringList entries READ entries WRITE setEntries NOTIFY entriesChanged FINAL ) @@ -32,30 +24,18 @@ class QSK_EXPORT QskSelectionWindow : public QskDialogWindow QskSelectionWindow( QWindow* parent = nullptr ); ~QskSelectionWindow() override; - void setInfoTextOptions( const QskTextOptions& ); - QskTextOptions infoTextOptions() const; - - QString infoText() const; - Q_INVOKABLE QStringList entries() const; Q_INVOKABLE int selectedRow() const; Q_INVOKABLE QString selectedEntry() const; public Q_SLOTS: - void setInfoText( const QString& ); void setEntries( const QStringList& ); void setSelectedRow( int row ); Q_SIGNALS: - void infoTextChanged( const QString& ); - void infoTextOptionsChanged( const QskTextOptions& ); void selectedRowChanged( int ); void selectedEntryChanged( const QString& ); void entriesChanged(); - - private: - class PrivateData; - std::unique_ptr< PrivateData > m_data; }; #endif diff --git a/src/graphic/QskGraphic.cpp b/src/graphic/QskGraphic.cpp index 791e79ae..aed3f610 100644 --- a/src/graphic/QskGraphic.cpp +++ b/src/graphic/QskGraphic.cpp @@ -339,7 +339,7 @@ class QskGraphic::PrivateData : public QSharedData PrivateData( const PrivateData& other ) : QSharedData( other ) - , defaultSize( other.defaultSize ) + , viewBox( other.viewBox ) , commands( other.commands ) , pathInfos( other.pathInfos ) , boundingRect( other.boundingRect ) @@ -354,7 +354,18 @@ class QskGraphic::PrivateData : public QSharedData { return ( modificationId == other.modificationId ) && ( renderHints == other.renderHints ) && - ( defaultSize == other.defaultSize ); + ( viewBox == other.viewBox ); + } + + void resetCommands() + { + commands.clear(); + pathInfos.clear(); + + commandTypes = 0; + boundingRect = pointRect = { 0.0, 0.0, -1.0, -1.0 }; + + modificationId = 0; } inline void addCommand( const QskPainterCommand& command ) @@ -365,7 +376,7 @@ class QskGraphic::PrivateData : public QSharedData modificationId = nextId.fetchAndAddRelaxed( 1 ); } - QSizeF defaultSize; + QRectF viewBox = { 0.0, 0.0, -1.0, -1.0 }; QVector< QskPainterCommand > commands; QVector< QskGraphicPrivate::PathInfo > pathInfos; @@ -485,6 +496,16 @@ int QskGraphic::metric( PaintDeviceMetric deviceMetric ) const value = metric( PdmDevicePixelRatio ) * devicePixelRatioFScale(); break; } +#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 ) + case PdmDevicePixelRatioF_EncodedA: + case PdmDevicePixelRatioF_EncodedB: + { +#if 0 + value = QPaintDevice::encodeMetricF( metric, devicePixelRatio() ); +#endif + break; + } +#endif } return value; @@ -492,16 +513,9 @@ int QskGraphic::metric( PaintDeviceMetric deviceMetric ) const void QskGraphic::reset() { - m_data->commands.clear(); - m_data->pathInfos.clear(); + m_data->resetCommands(); - m_data->commandTypes = 0; - - m_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); - m_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); - m_data->defaultSize = QSizeF(); - - m_data->modificationId = 0; + m_data->viewBox = { 0.0, 0.0, -1.0, -1.0 }; delete m_paintEngine; m_paintEngine = nullptr; @@ -580,18 +594,20 @@ QSize QskGraphic::sizeMetrics() const return QSize( qCeil( sz.width() ), qCeil( sz.height() ) ); } -void QskGraphic::setDefaultSize( const QSizeF& size ) +void QskGraphic::setViewBox( const QRectF& rect ) { - const double w = qMax( qreal( 0.0 ), size.width() ); - const double h = qMax( qreal( 0.0 ), size.height() ); + m_data->viewBox = rect; +} - m_data->defaultSize = QSizeF( w, h ); +QRectF QskGraphic::viewBox() const +{ + return m_data->viewBox; } QSizeF QskGraphic::defaultSize() const { - if ( !m_data->defaultSize.isEmpty() ) - return m_data->defaultSize; + if ( !m_data->viewBox.isEmpty() ) + return m_data->viewBox.size(); return boundingRect().size(); } @@ -670,30 +686,42 @@ void QskGraphic::render( QPainter* painter, const QRectF& rect, if ( isEmpty() || rect.isEmpty() ) return; + const bool scalePens = !( m_data->renderHints & RenderPensUnscaled ); + qreal sx = 1.0; qreal sy = 1.0; - if ( m_data->pointRect.width() > 0.0 ) - sx = rect.width() / m_data->pointRect.width(); + QRectF boundingBox = m_data->viewBox; - if ( m_data->pointRect.height() > 0.0 ) - sy = rect.height() / m_data->pointRect.height(); - - const bool scalePens = !( m_data->renderHints & RenderPensUnscaled ); - - for ( const auto& info : std::as_const( m_data->pathInfos ) ) + if ( !boundingBox.isEmpty() ) { - const qreal ssx = info.scaleFactorX( m_data->pointRect, - rect, m_data->boundingRect, scalePens ); + sx = rect.width() / boundingBox.width(); + sy = rect.height() / boundingBox.height(); + } + else + { + boundingBox = m_data->boundingRect; - if ( ssx > 0.0 ) - sx = qMin( sx, ssx ); + if ( m_data->pointRect.width() > 0.0 ) + sx = rect.width() / m_data->pointRect.width(); - const qreal ssy = info.scaleFactorY( m_data->pointRect, - rect, m_data->boundingRect, scalePens ); + if ( m_data->pointRect.height() > 0.0 ) + sy = rect.height() / m_data->pointRect.height(); - if ( ssy > 0.0 ) - sy = qMin( sy, ssy ); + for ( const auto& info : std::as_const( m_data->pathInfos ) ) + { + const qreal ssx = info.scaleFactorX( m_data->pointRect, + rect, m_data->boundingRect, scalePens ); + + if ( ssx > 0.0 ) + sx = qMin( sx, ssx ); + + const qreal ssy = info.scaleFactorY( m_data->pointRect, + rect, m_data->boundingRect, scalePens ); + + if ( ssy > 0.0 ) + sy = qMin( sy, ssy ); + } } if ( aspectRatioMode == Qt::KeepAspectRatio ) @@ -705,15 +733,17 @@ void QskGraphic::render( QPainter* painter, const QRectF& rect, sx = sy = qMax( sx, sy ); } - const auto& br = m_data->boundingRect; - const auto rc = rect.center(); - QTransform tr; - tr.translate( - rc.x() - 0.5 * sx * br.width(), - rc.y() - 0.5 * sy * br.height() ); - tr.scale( sx, sy ); - tr.translate( -br.x(), -br.y() ); + + { + const auto rc = rect.center(); + + tr.translate( + rc.x() - 0.5 * sx * boundingBox.width(), + rc.y() - 0.5 * sy * boundingBox.height() ); + tr.scale( sx, sy ); + tr.translate( -boundingBox.x(), -boundingBox.y() ); + } const auto transform = painter->transform(); @@ -986,7 +1016,7 @@ const QVector< QskPainterCommand >& QskGraphic::commands() const void QskGraphic::setCommands( const QVector< QskPainterCommand >& commands ) { - reset(); + m_data->resetCommands(); const int numCommands = commands.size(); if ( numCommands <= 0 ) @@ -1019,9 +1049,7 @@ quint64 QskGraphic::modificationId() const QskHashValue QskGraphic::hash( QskHashValue seed ) const { auto hash = qHash( m_data->renderHints, seed ); - - hash = qHash( m_data->defaultSize.width(), hash ); - hash = qHash( m_data->defaultSize.height(), hash ); + hash = qHashBits( &m_data->viewBox, sizeof( QRectF ), hash ); return qHash( m_data->modificationId, hash ); } diff --git a/src/graphic/QskGraphic.h b/src/graphic/QskGraphic.h index bd678397..34565aa0 100644 --- a/src/graphic/QskGraphic.h +++ b/src/graphic/QskGraphic.h @@ -29,11 +29,14 @@ class QSK_EXPORT QskGraphic : public QPaintDevice Q_GADGET Q_PROPERTY( qreal aspectRatio READ aspectRatio ) + Q_PROPERTY( QRectF viewBox READ viewBox WRITE setViewBox ) Q_PROPERTY( QRectF boundingRect READ boundingRect ) Q_PROPERTY( QRectF controlPointRect READ controlPointRect ) Q_PROPERTY( QSizeF defaultSize READ defaultSize ) Q_PROPERTY( quint64 modificationId READ modificationId ) + using Inherited = QPaintDevice; + public: enum RenderHint { @@ -106,9 +109,11 @@ class QSK_EXPORT QskGraphic : public QPaintDevice const QVector< QskPainterCommand >& commands() const; void setCommands( const QVector< QskPainterCommand >& ); - void setDefaultSize( const QSizeF& ); QSizeF defaultSize() const; + void setViewBox( const QRectF& ); + QRectF viewBox() const; + qreal aspectRatio() const; qreal heightForWidth( qreal width ) const; diff --git a/src/graphic/QskGraphicIO.cpp b/src/graphic/QskGraphicIO.cpp index 77d21785..b654d6f9 100644 --- a/src/graphic/QskGraphicIO.cpp +++ b/src/graphic/QskGraphicIO.cpp @@ -19,10 +19,9 @@ static const char qskMagicNumber[] = "QSKG"; /* To avoid crashes ( fonts ), when svg2qvg was running with a different Qt version, than the one of the application we hardcode - the datastream format to Qt 5.6. Once everything is running with - Qt 5.15 we can increase the version. TODO ... + the datastream format. */ -static const int qskDataStreamVersion = QDataStream::Qt_5_6; +static const int qskDataStreamVersion = QDataStream::Qt_5_15; static inline void qskWritePathData( const QPainterPath& path, QDataStream& s ) @@ -242,6 +241,9 @@ QskGraphic QskGraphicIO::read( QIODevice* dev ) return QskGraphic(); } + QRectF viewBox; + stream >> viewBox; + quint32 numCommands; stream >> numCommands; @@ -281,6 +283,7 @@ QskGraphic QskGraphicIO::read( QIODevice* dev ) } QskGraphic graphic; + graphic.setViewBox( viewBox ); graphic.setCommands( commands ); return graphic; @@ -316,6 +319,8 @@ bool QskGraphicIO::write( const QskGraphic& graphic, QIODevice* dev ) stream.setByteOrder( QDataStream::BigEndian ); stream.writeRawData( qskMagicNumber, 4 ); + stream << graphic.viewBox(); + const auto numCommands = graphic.commands().size(); const QskPainterCommand* cmds = graphic.commands().constData(); diff --git a/src/graphic/QskGraphicProvider.cpp b/src/graphic/QskGraphicProvider.cpp index da3767e2..0736e7b4 100644 --- a/src/graphic/QskGraphicProvider.cpp +++ b/src/graphic/QskGraphicProvider.cpp @@ -4,13 +4,18 @@ *****************************************************************************/ #include "QskGraphicProvider.h" +#include "QskGraphicProviderMap.h" #include "QskGraphic.h" -#include "QskSetup.h" +#include "QskSkinManager.h" +#include "QskSkin.h" #include #include #include #include +#include + +Q_GLOBAL_STATIC( QskGraphicProviderMap, qskGraphicProviders ) class QskGraphicProvider::PrivateData { @@ -92,12 +97,22 @@ const QskGraphic* QskGraphicProvider::requestGraphic( const QString& id ) const void Qsk::addGraphicProvider( const QString& providerId, QskGraphicProvider* provider ) { - qskSetup->addGraphicProvider( providerId, provider ); + if ( qskGraphicProviders ) + qskGraphicProviders->insert( providerId, provider ); } QskGraphicProvider* Qsk::graphicProvider( const QString& providerId ) { - return qskSetup->graphicProvider( providerId ); + if ( auto skin = qskSkinManager->skin() ) + { + if ( auto provider = skin->graphicProvider( providerId ) ) + return provider; + } + + if ( qskGraphicProviders ) + return qskGraphicProviders->provider( providerId ); + + return nullptr; } QskGraphic Qsk::loadGraphic( const char* source ) @@ -122,7 +137,7 @@ QskGraphic Qsk::loadGraphic( const QUrl& url ) const QskGraphic* graphic = nullptr; - if ( const auto provider = qskSetup->graphicProvider( providerId ) ) + if ( const auto provider = Qsk::graphicProvider( providerId ) ) graphic = provider->requestGraphic( imageId ); return graphic ? *graphic : nullGraphic; diff --git a/src/graphic/QskStandardSymbol.cpp b/src/graphic/QskStandardSymbol.cpp index 80e63574..59d0b9f9 100644 --- a/src/graphic/QskStandardSymbol.cpp +++ b/src/graphic/QskStandardSymbol.cpp @@ -61,132 +61,6 @@ static void qskCancelGraphic( QPainter* painter ) painter->drawPath( path ); } -static void qskCriticalGraphic( QPainter* painter ) -{ - QPainterPath path; - path.addEllipse( 0, 5, 40, 40 ); - path.addRect( 5, 22, 30, 5 ); - - painter->setPen( Qt::NoPen ); - // painter->setBrush( QColor( Qt::black ) ); - painter->setBrush( QColor( Qt::red ) ); - painter->drawPath( path ); -} - -static void qskWarningGraphic( QPainter* painter ) -{ - const QRectF outerRect( 0, 2, 40, 36 ); - - const double off = 0.2 * qMin( outerRect.width(), outerRect.height() ); - - const double offBottom = 0.5 * ( outerRect.width() / outerRect.height() ) * off; - const QRectF innerRect = outerRect.adjusted( off, off, -off, -offBottom ); - - QPainterPath path; - - path.moveTo( outerRect.center().x(), outerRect.top() ); - path.lineTo( outerRect.right(), outerRect.bottom() ); - path.lineTo( outerRect.left(), outerRect.bottom() ); - path.closeSubpath(); - - path.moveTo( innerRect.left(), innerRect.bottom() ); - path.lineTo( innerRect.right(), innerRect.bottom() ); - path.lineTo( innerRect.center().x(), innerRect.top() ); - path.closeSubpath(); - - painter->setPen( Qt::NoPen ); - painter->setBrush( QColor( Qt::red ) ); - painter->drawPath( path ); - - const double d = 0.25 * off; - - const QRectF r1( - innerRect.center().x() - d, - innerRect.top() + 0.25 * innerRect.height(), - 2 * d, - 0.5 * innerRect.height() ); - - painter->fillRect( r1, Qt::black ); - - const QRectF r2( - r1.x(), - r1.bottom() + 0.05 * innerRect.height(), - r1.width(), - r1.width() ); - - painter->fillRect( r2, Qt::black ); -} - -static void qskQuestionGraphic( QPainter* painter ) -{ - const double w = 40; - const double h = 50; - - QPainterPath path; - - path.addRect( 0, 0, 1.0, 1.0 ); - - path.moveTo( 0.55, 0.63 ); - path.lineTo( 0.325, 0.63 ); - path.cubicTo( 0.325, 0.578, 0.33, 0.554, 0.3425, 0.532 ); - path.cubicTo( 0.3575, 0.51, 0.385, 0.484, 0.43, 0.452 ); - path.cubicTo( 0.495, 0.406, 0.5125, 0.388, 0.5225, 0.374 ); - path.cubicTo( 0.5325, 0.358, 0.54, 0.342, 0.54, 0.328 ); - path.cubicTo( 0.54, 0.304, 0.53, 0.286, 0.51, 0.272 ); - path.cubicTo( 0.49, 0.258, 0.46, 0.252, 0.425, 0.252 ); - path.cubicTo( 0.3925, 0.252, 0.355, 0.258, 0.315, 0.27 ); - path.cubicTo( 0.275, 0.28, 0.235, 0.298, 0.1925, 0.32 ); - path.lineTo( 0.195, 0.162 ); - path.cubicTo( 0.245, 0.148, 0.29, 0.138, 0.3325, 0.132 ); - path.cubicTo( 0.3725, 0.124, 0.4125, 0.122, 0.4525, 0.122 ); - path.cubicTo( 0.5525, 0.122, 0.63, 0.138, 0.685, 0.172 ); - path.cubicTo( 0.7375, 0.204, 0.765, 0.252, 0.765, 0.316 ); - path.cubicTo( 0.7625, 0.348, 0.755, 0.378, 0.74, 0.404 ); - path.cubicTo( 0.725, 0.43, 0.695, 0.458, 0.6575, 0.488 ); - path.cubicTo( 0.5875, 0.536, 0.57, 0.552, 0.5625, 0.566 ); - path.cubicTo( 0.55, 0.578, 0.55, 0.592, 0.55, 0.63 ); - - path.closeSubpath(); - - path.addRect( 0.325, 0.66, 0.55 - 0.325, ( 0.55 - 0.325 ) * w / h ); - - painter->scale( w, h ); - painter->setPen( Qt::NoPen ); - painter->setBrush( QColor( Qt::black ) ); - painter->drawPath( path ); -} - -static void qskInformationGraphic( QPainter* painter ) -{ - const double w = 40; - const double h = 50; - - const QRectF dotRect( 0.3 * w, 0.15 * h, 0.4 * w, 0.2 * h ); - const QRectF barRect( 0.3 * w, 0.4 * h, 0.4 * w, 0.5 * h ); - - QPainterPath path; - - path.addRect( 0, 0, w, h ); - path.addEllipse( dotRect ); - - const double dx = 0.33 * barRect.width(); - const double dy = 0.25 * dotRect.height(); - - path.moveTo( barRect.left(), barRect.top() ); - path.cubicTo( - barRect.left() + dx, barRect.top() + dy, - barRect.left() + 2 * dx, barRect.top() + dy, - barRect.right(), barRect.top() ); - - path.lineTo( barRect.right(), barRect.bottom() ); - path.lineTo( barRect.left(), barRect.bottom() ); - path.closeSubpath(); - - painter->setPen( Qt::NoPen ); - painter->setBrush( QColor( Qt::black ) ); - painter->drawPath( path ); -} - static void qskCheckMarkGraphic( QPainter* painter ) { QPainterPath path; @@ -288,26 +162,6 @@ QskGraphic QskStandardSymbol::graphic( Type symbolType ) qskCancelGraphic( &painter ); break; } - case QskStandardSymbol::Warning: - { - qskWarningGraphic( &painter ); - break; - } - case QskStandardSymbol::Critical: - { - qskCriticalGraphic( &painter ); - break; - } - case QskStandardSymbol::Question: - { - qskQuestionGraphic( &painter ); - break; - } - case QskStandardSymbol::Information: - { - qskInformationGraphic( &painter ); - break; - } case QskStandardSymbol::CheckMark: { qskCheckMarkGraphic( &painter ); diff --git a/src/graphic/QskStandardSymbol.h b/src/graphic/QskStandardSymbol.h index c79a6bbb..eea5708b 100644 --- a/src/graphic/QskStandardSymbol.h +++ b/src/graphic/QskStandardSymbol.h @@ -22,11 +22,6 @@ namespace QskStandardSymbol Ok, Cancel, - Information, - Warning, - Critical, - Question, - CheckMark, CrossMark, diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index e3de5c0a..c2dc3f7a 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -6,14 +6,14 @@ #include "QskArcNode.h" #include "QskArcMetrics.h" #include "QskArcShadowNode.h" +#include "QskArcRenderNode.h" +#include "QskArcRenderer.h" #include "QskMargins.h" #include "QskGradient.h" #include "QskShapeNode.h" -#include "QskStrokeNode.h" #include "QskSGNode.h" #include "QskShadowMetrics.h" -#include #include namespace @@ -21,8 +21,9 @@ namespace enum NodeRole { ShadowRole, - FillRole, - BorderRole + + PathRole, + ArcRole }; } @@ -43,35 +44,18 @@ static inline QskGradient qskEffectiveGradient( return gradient; } -static inline QskArcMetrics qskEffectiveMetrics( - const QskArcMetrics& metrics, const QRectF& rect ) +template< typename Node > +inline Node* qskInsertOrRemoveNode( QSGNode* parentNode, quint8 role, bool isValid ) { - if ( metrics.sizeMode() == Qt::RelativeSize ) - { - const auto rx = 0.5 * rect.width(); - const auto ry = 0.5 * rect.height(); + using namespace QskSGNode; - return metrics.toAbsolute( rx, ry ); - } + Node* oldNode = static_cast< Node* >( findChildNode( parentNode, role ) ); + Node* newNode = isValid ? ensureNode< Node >( oldNode ) : nullptr; - return metrics; -} + static const QVector< quint8 > roles = { ShadowRole, PathRole, ArcRole }; + replaceChildNode( roles, role, parentNode, oldNode, newNode ); -static inline QRectF qskEffectiveRect( - const QRectF& rect, const qreal borderWidth ) -{ - if ( borderWidth <= 0.0 ) - return rect; - - return qskValidOrEmptyInnerRect( rect, QskMargins( 0.5 * borderWidth ) ); -} - -static void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node ) -{ - static const QVector< quint8 > roles = { ShadowRole, FillRole, BorderRole }; - - auto oldNode = QskSGNode::findChildNode( parentNode, role ); - QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node ); + return newNode; } QskArcNode::QskArcNode() @@ -83,56 +67,44 @@ QskArcNode::~QskArcNode() } void QskArcNode::setArcData( const QRectF& rect, - const QskArcMetrics& arcMetrics, const QskGradient& fillGradient ) + const QskArcMetrics& arcMetrics, const QskGradient& gradient ) { - setArcData( rect, arcMetrics, 0.0, QColor(), fillGradient, {}, {} ); + setArcData( rect, arcMetrics, 0.0, QColor(), gradient, {}, {} ); } void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics, - const qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient ) + const qreal borderWidth, const QColor& borderColor, const QskGradient& gradient ) { - setArcData( rect, arcMetrics, borderWidth, borderColor, fillGradient, {}, {} ); + setArcData( rect, arcMetrics, borderWidth, borderColor, gradient, {}, {} ); } void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics, - const qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient, + const qreal borderWidth, const QColor& borderColor, const QskGradient& gradient, const QColor& shadowColor, const QskShadowMetrics& shadowMetrics ) { - const auto metricsArc = qskEffectiveMetrics( arcMetrics, rect ); - const auto gradient = qskEffectiveGradient( fillGradient, metricsArc ); + const bool radial = false; - auto shadowNode = static_cast< QskArcShadowNode* >( - QskSGNode::findChildNode( this, ShadowRole ) ); + const auto metricsArc = arcMetrics.toAbsolute( rect.size() ); - auto fillNode = static_cast< QskShapeNode* >( - QskSGNode::findChildNode( this, FillRole ) ); - - auto borderNode = static_cast< QskStrokeNode* >( - QskSGNode::findChildNode( this, BorderRole ) ); - - const auto arcRect = qskEffectiveRect( rect, borderWidth ); - if ( arcRect.isEmpty() ) + if ( metricsArc.isNull() || rect.isEmpty() ) { - delete shadowNode; - delete fillNode; - delete borderNode; + delete QskSGNode::findChildNode( this, ShadowRole ); + delete QskSGNode::findChildNode( this, PathRole ); + delete QskSGNode::findChildNode( this, ArcRole ); + return; } - const auto isFillNodeVisible = gradient.isVisible() && !metricsArc.isNull(); - const auto isStrokeNodeVisible = borderWidth > 0.0 && borderColor.alpha() > 0; - const auto isShadowNodeVisible = shadowColor.alpha() > 0.0 && isFillNodeVisible; + const auto hasFilling = gradient.isVisible(); + const auto hasBorder = ( borderWidth > 0.0 ) + && borderColor.isValid() && ( borderColor.alpha() > 0 ); + const auto hasShadow = shadowColor.isValid() && ( shadowColor.alpha() > 0 ); - const auto path = metricsArc.painterPath( arcRect ); + auto shadowNode = qskInsertOrRemoveNode< QskArcShadowNode >( + this, ShadowRole, hasFilling && hasShadow ); - if ( isShadowNodeVisible ) + if ( shadowNode ) { - if ( shadowNode == nullptr ) - { - shadowNode = new QskArcShadowNode; - QskSGNode::setNodeRole( shadowNode, ShadowRole ); - } - /* The shader of the shadow node is for circular arcs and we have some unwanted scaling issues for the spread/blur values when having ellipsoid @@ -140,55 +112,30 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics and not only to its radius. TODO ... */ - const auto sm = shadowMetrics.toAbsolute( arcRect.size() ); - const auto shadowRect = sm.shadowRect( arcRect ); + const auto sm = shadowMetrics.toAbsolute( rect.size() ); + const auto shadowRect = sm.shadowRect( rect ); const auto spreadRadius = sm.spreadRadius() + 0.5 * metricsArc.thickness(); shadowNode->setShadowData( shadowRect, spreadRadius, sm.blurRadius(), metricsArc.startAngle(), metricsArc.spanAngle(), shadowColor ); } - else + + auto pathNode = qskInsertOrRemoveNode< QskShapeNode >( this, PathRole, + hasFilling && !QskArcRenderer::isGradientSupported( rect, metricsArc, gradient ) ); + + if ( pathNode ) { - delete shadowNode; - shadowNode = nullptr; + const auto path = metricsArc.painterPath( rect, radial ); + pathNode->updateNode( path, QTransform(), rect, + qskEffectiveGradient( gradient, metricsArc ) ); } - if ( isFillNodeVisible ) + auto arcNode = qskInsertOrRemoveNode< QskArcRenderNode >( + this, ArcRole, hasBorder || ( hasFilling && !pathNode ) ); + + if ( arcNode ) { - if ( fillNode == nullptr ) - { - fillNode = new QskShapeNode; - QskSGNode::setNodeRole( fillNode, FillRole ); - } - - fillNode->updateNode( path, QTransform(), arcRect, gradient ); + arcNode->updateNode( rect, metricsArc, radial, + borderWidth, borderColor, pathNode ? QskGradient() : gradient ); } - else - { - delete fillNode; - fillNode = nullptr; - } - - if ( isStrokeNodeVisible ) - { - if ( borderNode == nullptr ) - { - borderNode = new QskStrokeNode; - QskSGNode::setNodeRole( borderNode, BorderRole ); - } - - QPen pen( borderColor, borderWidth ); - pen.setCapStyle( Qt::FlatCap ); - - borderNode->updateNode( path, QTransform(), pen ); - } - else - { - delete borderNode; - borderNode = nullptr; - } - - qskUpdateChildren(this, ShadowRole, shadowNode); - qskUpdateChildren(this, FillRole, fillNode); - qskUpdateChildren(this, BorderRole, borderNode); } diff --git a/src/nodes/QskArcNode.h b/src/nodes/QskArcNode.h index c4fa330a..b641533e 100644 --- a/src/nodes/QskArcNode.h +++ b/src/nodes/QskArcNode.h @@ -6,18 +6,14 @@ #ifndef QSK_ARC_NODE_H #define QSK_ARC_NODE_H -#include "QskShapeNode.h" +#include "QskGlobal.h" +#include class QskArcMetrics; class QskGradient; class QskShadowMetrics; -/* - For the moment a QPainterPath/QskShapeNode. - But we can do better by creatig vertex lists manually - like what is done by the box renderer. TODO ... - */ -class QSK_EXPORT QskArcNode : public QskShapeNode +class QSK_EXPORT QskArcNode : public QSGNode { public: QskArcNode(); diff --git a/src/nodes/QskArcRenderNode.cpp b/src/nodes/QskArcRenderNode.cpp new file mode 100644 index 00000000..0d7ffddd --- /dev/null +++ b/src/nodes/QskArcRenderNode.cpp @@ -0,0 +1,121 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskArcRenderNode.h" +#include "QskGradient.h" +#include "QskArcRenderer.h" +#include "QskArcMetrics.h" +#include "QskGradient.h" +#include "QskSGNode.h" +#include "QskFillNodePrivate.h" + +class QskArcRenderNodePrivate final : public QskFillNodePrivate +{ + public: + inline void resetValues() { hash = 0; } + + QskHashValue hash = 0; +}; + +QskArcRenderNode::QskArcRenderNode() + : QskFillNode( *new QskArcRenderNodePrivate ) +{ +} + +QskArcRenderNode::~QskArcRenderNode() +{ +} + +void QskArcRenderNode::updateNode( const QRectF& rect, + const QskArcMetrics& metrics, const QskGradient& gradient ) +{ + updateNode( rect, metrics, false, 0.0, QColor(), gradient ); +} + +void QskArcRenderNode::updateNode( const QRectF& rect, + const QskArcMetrics& metrics, qreal borderWidth, const QColor& borderColor ) +{ + updateNode( rect, metrics, false, borderWidth, borderColor, QskGradient() ); +} + +void QskArcRenderNode::updateNode( + const QRectF& rect, const QskArcMetrics& arcMetrics, bool radial, + qreal borderWidth, const QColor& borderColor, const QskGradient& gradient ) +{ + Q_D( QskArcRenderNode ); + + const auto metrics = arcMetrics.toAbsolute( rect.size() ); + const auto borderMax = 0.5 * metrics.thickness(); + + const bool hasFill = gradient.isVisible() && ( borderWidth < borderMax ); + const bool hasBorder = ( borderWidth > 0.0 ) + && borderColor.isValid() && ( borderColor.alpha() > 0 ); + + if ( rect.isEmpty() || metrics.isNull() || !( hasFill || hasBorder ) ) + { + d->resetValues(); + QskSGNode::resetGeometry( this ); + + return; + } + + borderWidth = qMin( borderWidth, borderMax ); + + QskHashValue hash = 3496; + + hash = qHashBits( &rect, sizeof( QRectF ), hash ); + hash = qHash( borderWidth, hash ); + hash = qHashBits( &borderColor, sizeof( borderColor ), hash ); + hash = metrics.hash( hash ); + hash = gradient.hash( hash ); + hash = qHash( radial, hash ); + + if ( hash == d->hash ) + return; + + d->hash = hash; + + auto coloring = QskFillNode::Polychrome; + +#if 0 + if ( !( hasFill && hasBorder ) ) + { + if ( hasBorder || ( hasFill && gradient.isMonochrome() ) ) + coloring = QskFillNode::Monochrome; + } +#endif + + auto& geometry = *this->geometry(); + + if ( coloring == QskFillNode::Polychrome ) + { + setColoring( coloring ); + + QskArcRenderer::renderArc( rect, metrics, radial, + borderWidth, gradient, borderColor, geometry ); + } + else + { + if ( hasFill ) + { + setColoring( gradient.rgbStart() ); + + QskArcRenderer::renderArc( rect, metrics, radial, + borderWidth, gradient, borderColor, geometry ); + } + else + { + setColoring( borderColor ); + + QskArcRenderer::renderArc( rect, metrics, radial, + borderWidth, gradient, borderColor, geometry ); + } + } + + markDirty( QSGNode::DirtyGeometry ); + markDirty( QSGNode::DirtyMaterial ); + + geometry.markVertexDataDirty(); +} diff --git a/src/nodes/QskArcRenderNode.h b/src/nodes/QskArcRenderNode.h new file mode 100644 index 00000000..f0d248ab --- /dev/null +++ b/src/nodes/QskArcRenderNode.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_ARC_RENDER_NODE_H +#define QSK_ARC_RENDER_NODE_H + +#include "QskGlobal.h" +#include "QskFillNode.h" + +class QskGradient; +class QskArcMetrics; + +class QskArcRenderNodePrivate; + +class QSK_EXPORT QskArcRenderNode : public QskFillNode +{ + using Inherited = QskFillNode; + + public: + QskArcRenderNode(); + ~QskArcRenderNode() override; + + void updateNode( const QRectF&, const QskArcMetrics&, const QskGradient& ); + + void updateNode( const QRectF&, const QskArcMetrics&, + qreal borderWidth, const QColor& borderColor ); + + void updateNode( const QRectF&, const QskArcMetrics&, bool radial, + qreal borderWidth, const QColor& borderColor, const QskGradient& ); + + private: + Q_DECLARE_PRIVATE( QskArcRenderNode ) +}; + +#endif diff --git a/src/nodes/QskArcRenderer.cpp b/src/nodes/QskArcRenderer.cpp new file mode 100644 index 00000000..1a7963ce --- /dev/null +++ b/src/nodes/QskArcRenderer.cpp @@ -0,0 +1,554 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskArcRenderer.h" +#include "QskArcMetrics.h" +#include "QskGradient.h" +#include "QskVertex.h" +#include "QskBoxColorMap.h" +#include "QskRgbValue.h" + +#include +#include + +static inline QskVertex::Line* qskAllocateLines( + QSGGeometry& geometry, int lineCount ) +{ + geometry.allocate( 2 * lineCount ); // 2 points per line + return reinterpret_cast< QskVertex::Line* >( geometry.vertexData() ); +} + +static inline QskVertex::ColoredLine* qskAllocateColoredLines( + QSGGeometry& geometry, int lineCount ) +{ + geometry.allocate( 2 * lineCount ); // 2 points per line + return reinterpret_cast< QskVertex::ColoredLine* >( geometry.vertexData() ); +} + +namespace +{ + template< class Line > + class OrthogonalStroker + { + public: + OrthogonalStroker( const QRectF& rect, qreal thickness, qreal border ) + : m_thickness( thickness ) + , m_border( border ) + , m_rx( 0.5 * ( rect.width() - m_thickness ) ) + , m_ry( 0.5 * ( rect.height() - m_thickness ) ) + , m_offsetToBorder( 0.5 * m_thickness - border ) + , m_aspectRatio( m_rx / m_ry ) + , m_cx( rect.x() + 0.5 * rect.width() ) + , m_cy( rect.y() + 0.5 * rect.height() ) + { + } + + inline void setLinesAt( const qreal radians, + const QskVertex::Color fillColor, const QskVertex::Color borderColor, + Line* fill, Line* outerBorder, Line* innerBorder ) const + { + const auto cos = qFastCos( radians ); + const auto sin = qFastSin( radians ); + + const auto v = normalVector( cos, sin ); + + const QPointF p0( m_cx + m_rx * cos, m_cy - m_ry * sin ); + + const auto v1 = v * m_offsetToBorder; + + const auto p1 = p0 + v1; + const auto p2 = p0 - v1; + + if ( fill ) + fill->setLine( p1, p2, fillColor ); + + if ( outerBorder ) + { + const auto v2 = v * m_border; + + outerBorder->setLine( p1 + v2, p1, borderColor ); + innerBorder->setLine( p2 - v2, p2, borderColor ); + } + } + + inline void setClosingBorderLines( const Line& l, + Line* lines, qreal sign, const QskVertex::Color color ) const + { + const auto& pos = l.p1; + const auto& l0 = lines[0]; + + const auto dx = sign * l0.dy(); + const auto dy = sign * l0.dx(); + + lines[-3].setLine( pos.x, pos.y, pos.x, pos.y, color ); + lines[-2].setLine( pos.x + dx, pos.y - dy, pos.x, pos.y, color ); + lines[-1].setLine( l0.x1() + dx, l0.y1() - dy, l0.x1(), l0.y1(), color ); + } + + private: + inline QPointF normalVector( const qreal cos, const qreal sin ) const + { + /* + The inner/outer points are found by shifting orthogonally along the + ellipse tangent: + + m = w / h * tan( angle ) + y = m * x; + x² + y² = 1.0 + + => x = 1.0 / sqrt( 1.0 + m² ); + + Note: the angle of the orthogonal vector could + also be found ( first quadrant ) by: + + atan2( tan( angle ), h / w ); + + Note: we return the vector mirrored vertically, so that it + matches the coordinate system used by Qt. + */ + + if ( qFuzzyIsNull( cos ) ) + return { 0.0, ( sin < 0.0 ) ? 1.0 : -1.0 }; + + const qreal m = m_aspectRatio * ( sin / cos ); + const qreal t = 1.0 / qSqrt( 1.0 + m * m ); + + const auto dx = ( cos >= 0.0 ) ? t : -t; + return { dx, -m * dx }; + } + + const qreal m_thickness; + const qreal m_border; + + // radii t the middle of the arc + const qreal m_rx, m_ry; + + // distances between the middle and the beginning of the border + const qreal m_offsetToBorder; + + const qreal m_aspectRatio; // m_rx / m_ry + + // center + const qreal m_cx, m_cy; + }; + + template< class Line > + class RadialStroker + { + public: + RadialStroker( const QRectF& rect, qreal thickness, qreal border ) + : m_sx( qMax( rect.width() / rect.height(), 1.0 ) ) + , m_sy( qMax( rect.height() / rect.width(), 1.0 ) ) + , m_rx1( 0.5 * rect.width() ) + , m_ry1( 0.5 * rect.height() ) + , m_rx2( m_rx1 - m_sx * border ) + , m_ry2( m_ry1 - m_sy * border ) + , m_rx3( m_rx1 - m_sx * ( thickness - border ) ) + , m_ry3( m_ry1 - m_sy * ( thickness - border ) ) + , m_rx4( m_rx1 - m_sx * thickness ) + , m_ry4( m_ry1 - m_sy * thickness ) + , m_center( rect.x() + m_rx1, rect.y() + m_ry1 ) + { + } + + inline void setLinesAt( const qreal radians, + const QskVertex::Color fillColor, const QskVertex::Color borderColor, + Line* fill, Line* outer, Line* inner ) const + { + const QPointF v( qFastCos( radians ), -qFastSin( radians ) ); + + const auto x1 = m_center.x() + m_rx2 * v.x(); + const auto y1 = m_center.y() + m_ry2 * v.y(); + + const auto x2 = m_center.x() + m_rx3 * v.x(); + const auto y2 = m_center.y() + m_ry3 * v.y(); + + if ( fill ) + fill->setLine( x1, y1, x2, y2, fillColor ); + + if ( outer ) + { + const auto x3 = m_center.x() + m_rx1 * v.x(); + const auto y3 = m_center.y() + m_ry1 * v.y(); + + const auto x4 = m_center.x() + m_rx4 * v.x(); + const auto y4 = m_center.y() + m_ry4 * v.y(); + + outer->setLine( x3, y3, x1, y1, borderColor ); + inner->setLine( x4, y4, x2, y2, borderColor ); + } + } + + inline void setClosingBorderLines( const Line& l, + Line* lines, qreal sign, const QskVertex::Color color ) const + { + const auto& pos = l.p1; + + // Good enough until it is decided if we want to keep the radial mode. + const auto& l0 = lines[0]; + + const auto s = m_sx / m_sy; + const auto dx = sign * l0.dy() * s; + const auto dy = sign * l0.dx() / s; + + lines[-3].setLine( pos.x, pos.y, pos.x, pos.y, color ); + lines[-2].setLine( pos.x + dx, pos.y - dy, pos.x, pos.y, color ); + lines[-1].setLine( l0.x1() + dx, l0.y1() - dy, l0.x1(), l0.y1(), color ); + } + + private: + // stretch factors of the ellipse + const qreal m_sx, m_sy; + + // radii: out->in + const qreal m_rx1, m_ry1, m_rx2, m_ry2, m_rx3, m_ry3, m_rx4, m_ry4; + + // center point + const QPointF m_center; + }; + + template< class Line > + class CircularStroker + { + public: + CircularStroker( const QRectF& rect, qreal thickness, qreal border ) + : m_center( rect.center() ) + , m_radius( 0.5 * ( rect.width() - thickness ) ) + , m_distOut( 0.5 * thickness ) + , m_distIn( m_distOut - border ) + { + } + + inline void setLinesAt( const qreal radians, + const QskVertex::Color fillColor, const QskVertex::Color borderColor, + Line* fill, Line* outer, Line* inner ) const + { + const QPointF v( qFastCos( radians ), -qFastSin( radians ) ); + + const auto p0 = m_center + m_radius * v; + const auto dv1 = v * m_distIn; + + const auto p1 = p0 + dv1; + const auto p2 = p0 - dv1; + + if ( fill ) + fill->setLine( p1, p2, fillColor ); + + if ( outer ) + { + const auto dv2 = v * m_distOut; + + const auto p3 = p0 + dv2; + const auto p4 = p0 - dv2; + + outer->setLine( p3, p1, borderColor ); + inner->setLine( p4, p2, borderColor ); + } + } + + inline void setClosingBorderLines( const Line& l, + Line* lines, qreal sign, const QskVertex::Color color ) const + { + const auto& pos = l.p1; + const auto& l0 = lines[0]; + + const auto dx = sign * l0.dy(); + const auto dy = sign * l0.dx(); + + lines[-3].setLine( pos.x, pos.y, pos.x, pos.y, color ); + lines[-2].setLine( pos.x + dx, pos.y - dy, pos.x, pos.y, color ); + lines[-1].setLine( l0.x1() + dx, l0.y1() - dy, l0.x1(), l0.y1(), color ); + } + + private: + // center point + const QPointF m_center; + const qreal m_radius; // middle of the arc + + // distances from the middle to the inner/outer side of the border + const qreal m_distOut, m_distIn; + }; +} + +namespace +{ + class Renderer + { + public: + Renderer( const QRectF&, const QskArcMetrics&, + bool radial, const QskGradient&, const QskVertex::Color& ); + + int fillCount() const; + int borderCount() const; + + template< class Line > + void renderArc( const qreal thickness, const qreal border, Line*, Line* ) const; + + private: + int arcLineCount() const; + + template< class LineStroker, class Line > + void renderLines( const LineStroker&, Line*, Line* ) const; + + const QRectF& m_rect; + + const qreal m_radians1; + const qreal m_radians2; + + const bool m_radial; // for circular arcs radial/orthogonal does not differ + const bool m_closed; + + const QskGradient& m_gradient; + const QskVertex::Color m_borderColor; + }; + + Renderer::Renderer( const QRectF& rect, const QskArcMetrics& metrics, + bool radial, const QskGradient& gradient, const QskVertex::Color& borderColor ) + : m_rect( rect ) + , m_radians1( qDegreesToRadians( metrics.startAngle() ) ) + , m_radians2( qDegreesToRadians( metrics.endAngle() ) ) + , m_radial( radial ) + , m_closed( metrics.isClosed() ) + , m_gradient( gradient ) + , m_borderColor( borderColor ) + { + } + + int Renderer::arcLineCount() const + { + // not very sophisticated - TODO ... + + const auto radius = 0.5 * qMax( m_rect.width(), m_rect.height() ); + const auto radians = qAbs( m_radians2 - m_radians1 ); + + const auto count = qCeil( ( radius * radians ) / 3.0 ); + return qBound( 3, count, 160 ); + } + + int Renderer::fillCount() const + { + if ( !m_gradient.isVisible() ) + return 0; + + return arcLineCount() + m_gradient.stepCount() - 1; + } + + template< class Line > + void Renderer::renderArc( const qreal thickness, const qreal border, + Line* fillLines, Line* borderLines ) const + { + if ( qskFuzzyCompare( m_rect.width(), m_rect.height() ) ) + { + const CircularStroker< Line > stroker( m_rect, thickness, border ); + renderLines( stroker, fillLines, borderLines ); + } + else if ( m_radial ) + { + const RadialStroker< Line > stroker( m_rect, thickness, border ); + renderLines( stroker, fillLines, borderLines ); + } + else + { + const OrthogonalStroker< Line > stroker( m_rect, thickness, border ); + renderLines( stroker, fillLines, borderLines ); + } + } + + template< class LineStroker, class Line > + void Renderer::renderLines( const LineStroker& lineStroker, + Line* fillLines, Line* borderLines ) const + { + QskBoxRenderer::GradientIterator it; + + if ( fillLines ) + { + if ( m_gradient.stepCount() <= 1 ) + { + it.reset( m_gradient.rgbStart(), m_gradient.rgbEnd() ); + } + else + { + it.reset( m_gradient.stops() ); + it.advance(); // the first stop is always covered by the contour + } + } + + const auto count = arcLineCount(); + const auto radiansSpan = m_radians2 - m_radians1; + + const qreal stepMax = count - 1; + const auto stepSize = radiansSpan / stepMax; + + auto l = fillLines; + + auto outer = borderLines; + auto inner = borderLines; + + if ( borderLines ) + { + outer = borderLines; + if ( !m_closed ) + outer += 3; + + inner = outer + count; + if ( !m_closed ) + inner += 3; + } + + for ( int i = 0; i < count; i++ ) + { + const auto progress = i / stepMax; + + while( !it.isDone() && ( it.position() < progress ) ) + { + const auto radians = m_radians1 + it.position() * radiansSpan; + lineStroker.setLinesAt( radians, it.color(), m_borderColor, + l++, nullptr, nullptr ); + + it.advance(); + } + + const auto radians = m_radians1 + i * stepSize; + const auto color = it.colorAt( progress ); + + lineStroker.setLinesAt( radians, color, m_borderColor, + l ? l++ : nullptr, + outer ? outer + i : nullptr, + inner ? inner + count - 1 - i : nullptr + ); + } + + if ( borderLines && !m_closed ) + { + const auto sign = ( radiansSpan > 0.0 ) ? 1.0 : -1.0; + + lineStroker.setClosingBorderLines( inner[count - 1], outer, sign, m_borderColor ); + lineStroker.setClosingBorderLines( outer[count - 1], inner, sign, m_borderColor ); + } + } + + int Renderer::borderCount() const + { + if ( m_borderColor.a == 0 ) + return 0; + + auto count = 2 * arcLineCount(); + if ( !m_closed ) + count += 2 * 3; + + return count; + } +} + +bool QskArcRenderer::isGradientSupported( const QRectF& rect, + const QskArcMetrics& metrics, const QskGradient& gradient ) +{ + if ( rect.isEmpty() || metrics.isNull() ) + return true; + + if ( !gradient.isVisible() || gradient.isMonochrome() ) + return true; + + switch( gradient.type() ) + { + case QskGradient::Stops: + { + return true; + } + case QskGradient::Conic: + { + const auto direction = gradient.conicDirection(); + if ( direction.center() == rect.center() ) + { + const auto aspectRatio = rect.width() / rect.height(); + if ( qskFuzzyCompare( direction.aspectRatio(), aspectRatio ) ) + { + /* + we should be able to create a list of stops from + this gradient that works for the renderer. TODO ... + */ + } + } + + return false; + } + default: + { + return false; + } + } + + return false; +} + +void QskArcRenderer::renderArc( const QRectF& rect, const QskArcMetrics& metrics, + bool radial, const QskGradient& gradient, QSGGeometry& geometry ) +{ + renderArc( rect, metrics, radial, 0, gradient, QColor(), geometry ); +} + +void QskArcRenderer::renderArc( const QRectF& rect, const QskArcMetrics& metrics, + bool radial, qreal borderWidth, const QskGradient& gradient, + const QColor& borderColor, QSGGeometry& geometry ) +{ + geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + + const Renderer renderer( rect, metrics, radial, gradient, + borderColor.isValid() ? borderColor : QColor( 0, 0, 0, 0 ) ); + + const auto borderCount = renderer.borderCount(); + const auto fillCount = renderer.fillCount(); + + auto lineCount = borderCount + fillCount; + if ( borderCount && fillCount ) + lineCount++; // connecting line + + const auto lines = qskAllocateColoredLines( geometry, lineCount ); + if ( lines ) + { + const auto fillLines = fillCount ? lines : nullptr; + const auto borderLines = borderCount ? lines + lineCount - borderCount : nullptr; + + renderer.renderArc( metrics.thickness(), borderWidth, fillLines, borderLines ); + + if ( fillCount && borderCount ) + { + const auto idx = fillCount; + + lines[idx].p1 = lines[idx - 1].p2; + lines[idx].p2 = lines[idx + 1].p1; + } + } +} + +void QskArcRenderer::renderBorderGeometry( const QRectF& rect, + const QskArcMetrics& metrics, bool radial, qreal borderWidth, QSGGeometry& geometry ) +{ + geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + + const Renderer renderer( rect, metrics, radial, QskGradient(), 0 ); + + const auto lines = qskAllocateLines( geometry, renderer.borderCount() ); + if ( lines ) + { + QskVertex::Line* fill = nullptr; + renderer.renderArc( metrics.thickness(), borderWidth, fill, lines ); + } +} + +void QskArcRenderer::renderFillGeometry( const QRectF& rect, + const QskArcMetrics& metrics, bool radial, qreal borderWidth, QSGGeometry& geometry ) +{ + geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + + const Renderer renderer( rect, metrics, radial, QskRgb::Black, 0 ); + + const auto lines = qskAllocateLines( geometry, renderer.fillCount() ); + if ( lines ) + { + QskVertex::Line* border = nullptr; + renderer.renderArc( metrics.thickness(), borderWidth, lines, border ); + } +} diff --git a/src/nodes/QskArcRenderer.h b/src/nodes/QskArcRenderer.h new file mode 100644 index 00000000..1b0ce528 --- /dev/null +++ b/src/nodes/QskArcRenderer.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_ARC_RENDERER_H +#define QSK_ARC_RENDERER_H + +#include "QskGlobal.h" + +class QskArcMetrics; +class QskGradient; + +class QSGGeometry; +class QRectF; +class QColor; + +namespace QskArcRenderer +{ + /* + Filling the geometry without any color information: + see QSGGeometry::defaultAttributes_Point2D() + + - clip nodes + - using shaders setting the colors + */ + + QSK_EXPORT void renderBorderGeometry( const QRectF&, + const QskArcMetrics&, bool radial, qreal borderWidth, QSGGeometry& ); + + QSK_EXPORT void renderFillGeometry( const QRectF&, + const QskArcMetrics&, bool radial, qreal borderWidth, QSGGeometry& ); + + /* + Filling the geometry with color information: + see QSGGeometry::defaultAttributes_ColoredPoint2D() + */ + QSK_EXPORT bool isGradientSupported( + const QRectF&, const QskArcMetrics&, const QskGradient& ); + + QSK_EXPORT void renderArc( const QRectF&, const QskArcMetrics&, bool radial, + qreal borderWidth, const QskGradient&, const QColor& borderColor, QSGGeometry& ); + + QSK_EXPORT void renderArc( const QRectF&, const QskArcMetrics&, bool radial, + const QskGradient&, QSGGeometry& ); +} + +#endif diff --git a/src/nodes/QskTextureRenderer.cpp b/src/nodes/QskTextureRenderer.cpp index cade0829..83796f7f 100644 --- a/src/nodes/QskTextureRenderer.cpp +++ b/src/nodes/QskTextureRenderer.cpp @@ -122,7 +122,7 @@ quint32 QskTextureRenderer::createPaintedTextureGL( Binding GL_ARRAY_BUFFER/GL_ELEMENT_ARRAY_BUFFER to 0 seems to be enough. However - as we do not know what is finally painted and what the - OpenGL paint engine is doing with better reinitialize everything. + OpenGL paint engine is doing we better reinitialize everything. Hope this has no side effects as the context will leave the function in a modified state. Otherwise we could try to change the buffers diff --git a/src/nodes/QskTreeNode.h b/src/nodes/QskTreeNode.h index ad50ad83..78d31ffa 100644 --- a/src/nodes/QskTreeNode.h +++ b/src/nodes/QskTreeNode.h @@ -32,7 +32,7 @@ QSK_EXPORT QskTreeNode* qskTreeNodeCast( QSGNode* ); QSK_EXPORT const QskTreeNode* qskTreeNodeCast( const QSGNode* ); /* - Used by all QskQuickItem as root node ( = itemNode ) of its subtree + Used by QskItem as root node ( = itemNode ) of its subtree ( see qskItemNode in QskQuick.h ) */ class QSK_EXPORT QskItemNode final : public QSGTransformNode diff --git a/src/nodes/QskVertex.h b/src/nodes/QskVertex.h index 0d928a84..967d2382 100644 --- a/src/nodes/QskVertex.h +++ b/src/nodes/QskVertex.h @@ -26,7 +26,7 @@ namespace QskVertex Color( QRgb ) noexcept; Color( const QColor& ); - Color interpolatedTo( Color, double ratio ) const noexcept; + Color interpolatedTo( Color, qreal ratio ) const noexcept; constexpr bool operator==( const Color& ) const noexcept; constexpr bool operator!=( const Color& ) const noexcept; @@ -60,7 +60,7 @@ namespace QskVertex if ( a < 255 ) { - const double af = a / 255.0; + const auto af = a / 255.0; r *= af; g *= af; @@ -115,6 +115,11 @@ namespace QskVertex p2.set( x2, y2 ); } + inline void setLine( const QPointF& p1, const QPointF& p2 ) noexcept + { + setLine( p1.x(), p1.y(), p2.x(), p2.y() ); + } + inline void setHLine( float x1, float x2, float y ) noexcept { setLine( x1, y, x2, y ); @@ -125,14 +130,32 @@ namespace QskVertex setLine( x, y1, x, y2 ); } + inline void setLine( const QPointF& p1, const QPointF& p2, Color ) noexcept + { + /* The color parameter makes no sense, but is useful + when being used from templated code + */ + + setLine( p1.x(), p1.y(), p2.x(), p2.y() ); + } + inline void setLine( float x1, float y1, float x2, float y2, Color ) noexcept { /* The color parameter makes no sense, but is useful - when being using from templated code + when being used from templated code */ setLine( x1, y1, x2, y2 ); } + inline float x1() const noexcept { return p1.x; } + inline float y1() const noexcept { return p1.y; } + + inline float x2() const noexcept { return p2.x; } + inline float y2() const noexcept { return p2.y; } + + inline float dx() const noexcept { return p2.x - p1.x; } + inline float dy() const noexcept { return p2.y - p1.y; } + QSGGeometry::Point2D p1; QSGGeometry::Point2D p2; }; @@ -172,6 +195,15 @@ namespace QskVertex setLine( x, y1, color, x, y2, color ); } + inline float x1() const noexcept { return p1.x; } + inline float y1() const noexcept { return p1.y; } + + inline float x2() const noexcept { return p2.x; } + inline float y2() const noexcept { return p2.y; } + + inline float dx() const noexcept { return p2.x - p1.x; } + inline float dy() const noexcept { return p2.y - p1.y; } + QSGGeometry::ColoredPoint2D p1; QSGGeometry::ColoredPoint2D p2; }; @@ -214,15 +246,15 @@ namespace QskVertex m_stepIndex = 0; m_stepCount = stepCount; - const double angleStep = M_PI_2 / stepCount; + const auto angleStep = M_PI_2 / stepCount; m_cosStep = qFastCos( angleStep ); m_sinStep = qFastSin( angleStep ); } inline bool isInverted() const { return m_inverted; } - inline double cos() const { return m_cos; } - inline double sin() const { return m_inverted ? -m_sin : m_sin; } + inline qreal cos() const { return m_cos; } + inline qreal sin() const { return m_inverted ? -m_sin : m_sin; } inline int step() const { return m_stepIndex; } inline int stepCount() const { return m_stepCount; } @@ -253,7 +285,7 @@ namespace QskVertex } else { - const double cos0 = m_cos; + const auto cos0 = m_cos; m_cos = m_cos * m_cosStep + m_sin * m_sinStep; m_sin = m_sin * m_cosStep - cos0 * m_sinStep; @@ -269,9 +301,9 @@ namespace QskVertex inline void operator++() { increment(); } - static int segmentHint( double radius ) + static int segmentHint( qreal radius ) { - const double arcLength = radius * M_PI_2; + const auto arcLength = radius * M_PI_2; return qBound( 3, qCeil( arcLength / 3.0 ), 18 ); // every 3 pixels } @@ -292,12 +324,12 @@ namespace QskVertex } private: - double m_cos; - double m_sin; + qreal m_cos; + qreal m_sin; int m_stepIndex; - double m_cosStep; - double m_sinStep; + qreal m_cosStep; + qreal m_sinStep; int m_stepCount; bool m_inverted; diff --git a/support/CMakeLists.txt b/support/CMakeLists.txt index 7d005444..e2243357 100644 --- a/support/CMakeLists.txt +++ b/support/CMakeLists.txt @@ -11,6 +11,8 @@ set(SOURCES SkinnyShortcut.h SkinnyShortcut.cpp ) +qt_add_resources(SOURCES DejaVuSans.qrc Roboto.qrc SegoeUI.qrc) + set(target qsktestsupport) if (BUILD_QSKDLL) @@ -32,30 +34,8 @@ target_include_directories(${target} PUBLIC ${CMAKE_CURRENT_LIST_DIR}) if(ENABLE_ENSURE_SKINS) target_include_directories(${target} PRIVATE ${QSK_SOURCE_DIR}/designsystems) - target_compile_definitions(${target} PRIVATE ENSURE_SKINS) + target_compile_definitions(${target} PRIVATE ENSURE_SKINS ENSURE_FONTS) target_link_libraries(${target} PRIVATE material3skin fluent2skin fusionskin) endif() -set(HIDE_SYSTEM_FONTS ON) - -if(HIDE_SYSTEM_FONTS) - message( STATUS "Hiding system fonts from the examples" ) - - # The fonts being used in the examples are included and we do not - # need the fonts from the system. To avoid, that setting up the font - # cache slows doesn the stratup performance we hide the system fonts - # be using a dummy font config environment. - # ( this hack is pretty old ( Qt 5.4 ) - maybe it is not needed anymore TODO ) - - set(QSK_FONTDIR ${CMAKE_CURRENT_LIST_DIR}/fonts) - set(QSK_FONTCACHEDIR ${CMAKE_CURRENT_BINARY_DIR}/fontconfig) - set(QSK_FONTCONF_FILE ${QSK_FONTCACHEDIR}/fonts.conf) - - configure_file(fonts.conf.cmake.in ${QSK_FONTCONF_FILE} - @ONLY NEWLINE_STYLE LF) - - target_compile_definitions(${target} - PRIVATE FONTCONFIG_FILE=${QSK_FONTCONF_FILE}) -endif() - set_target_properties(${target} PROPERTIES FOLDER libs) diff --git a/support/DejaVuSans.qrc b/support/DejaVuSans.qrc new file mode 100644 index 00000000..324c6a33 --- /dev/null +++ b/support/DejaVuSans.qrc @@ -0,0 +1,7 @@ + + + + fonts/DejaVuSans/DejaVuSans.ttf + + + diff --git a/support/Roboto.qrc b/support/Roboto.qrc new file mode 100644 index 00000000..290c7469 --- /dev/null +++ b/support/Roboto.qrc @@ -0,0 +1,18 @@ + + + + fonts/Roboto/Roboto-BlackItalic.ttf + fonts/Roboto/Roboto-Black.ttf + fonts/Roboto/Roboto-BoldItalic.ttf + fonts/Roboto/Roboto-Bold.ttf + fonts/Roboto/Roboto-Italic.ttf + fonts/Roboto/Roboto-LightItalic.ttf + fonts/Roboto/Roboto-Light.ttf + fonts/Roboto/Roboto-MediumItalic.ttf + fonts/Roboto/Roboto-Medium.ttf + fonts/Roboto/Roboto-Regular.ttf + fonts/Roboto/Roboto-ThinItalic.ttf + fonts/Roboto/Roboto-Thin.ttf + + + diff --git a/support/SegoeUI.qrc b/support/SegoeUI.qrc new file mode 100644 index 00000000..644baf22 --- /dev/null +++ b/support/SegoeUI.qrc @@ -0,0 +1,18 @@ + + + + fonts/SegoeUI/segoeuib.ttf + fonts/SegoeUI/segoeuii.ttf + fonts/SegoeUI/segoeuil.ttf + fonts/SegoeUI/segoeuisl.ttf + fonts/SegoeUI/segoeui.ttf + fonts/SegoeUI/segoeuiz.ttf + fonts/SegoeUI/seguibli.ttf + fonts/SegoeUI/seguibl.ttf + fonts/SegoeUI/seguili.ttf + fonts/SegoeUI/seguisbi.ttf + fonts/SegoeUI/seguisb.ttf + fonts/SegoeUI/seguisli.ttf + + + diff --git a/support/SkinnyNamespace.cpp b/support/SkinnyNamespace.cpp index 498d4c3d..36377448 100644 --- a/support/SkinnyNamespace.cpp +++ b/support/SkinnyNamespace.cpp @@ -6,20 +6,17 @@ #include "SkinnyNamespace.h" #include +#include #include #include #include +#include #include #include -#define STRINGIFY(x) #x -#define STRING(x) STRINGIFY(x) - #if defined( PLUGIN_PATH ) -#include - #define STRINGIFY(x) #x #define STRING(x) STRINGIFY(x) @@ -76,58 +73,30 @@ static bool pluginPath = initPluginPath(); } Q_COREAPP_STARTUP_FUNCTION( initSkins ) - #endif -#define ENSURE_FONTS - #if defined( ENSURE_FONTS ) - #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - #include - #include - #endif - - static void preloadFonts() - { - #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - QElapsedTimer timer; - timer.start(); - - QFontDatabase(); // deprecated and doing nothing since Qt6 - - const auto elapsed = timer.elapsed(); - - if ( elapsed > 20 ) - { - qWarning() << "Loading fonts needed" << elapsed << "ms" - << "- usually because of creating a font cache."; - } - #endif - } + #include static void initFonts() { - if ( !qobject_cast< QGuiApplication* >( qApp ) ) - return; // no fonts needed + const QString path = QStringLiteral( ":/fonts/" ); - #ifdef FONTCONFIG_FILE - const char env[] = "FONTCONFIG_FILE"; - if ( !qEnvironmentVariableIsSet( env ) ) - qputenv( env, STRING( FONTCONFIG_FILE ) ); - #endif + QDir dir( path ); + const auto fontFiles = dir.entryList(); - preloadFonts(); - - /* - The default initialization in QskSkin sets up its font table - with using the application font for the default font role. - */ - QGuiApplication::setFont( QFont( "DejaVuSans", 12 ) ); + for ( const auto& file : fontFiles ) + { + const auto fontPath = path + file; + if ( QFontDatabase::addApplicationFont( fontPath ) < 0 ) + qWarning() << "couldn't load font file:" << fontPath; + } } -#endif -Q_COREAPP_STARTUP_FUNCTION( initFonts ) + Q_COREAPP_STARTUP_FUNCTION( initFonts ) + +#endif void Skinny::changeSkin() { @@ -156,9 +125,9 @@ void Skinny::changeFonts( int increment ) { auto skin = qskSkinManager->skin(); - const auto fonts = skin->fonts(); + const auto table = skin->fontTable(); - for ( auto it = fonts.constBegin(); it != fonts.constEnd(); ++it ) + for ( auto it = table.constBegin(); it != table.constEnd(); ++it ) { auto role = it.key(); auto font = it.value(); diff --git a/support/SkinnyShortcut.cpp b/support/SkinnyShortcut.cpp index 006a69bd..37d23dcf 100644 --- a/support/SkinnyShortcut.cpp +++ b/support/SkinnyShortcut.cpp @@ -111,7 +111,7 @@ void SkinnyShortcut::showBackground() scengraphDebugMode = sgDebugModes[ id - 2 ]; } - qskSetup->setItemUpdateFlag( QskQuickItem::DebugForceBackground, forceBackground ); + QskSetup::setUpdateFlag( QskItem::DebugForceBackground, forceBackground ); const auto windows = QGuiApplication::topLevelWindows(); for ( auto window : windows ) diff --git a/support/fonts.conf.cmake.in b/support/fonts.conf.cmake.in deleted file mode 100644 index 501a1fa0..00000000 --- a/support/fonts.conf.cmake.in +++ /dev/null @@ -1,6 +0,0 @@ - - - - @QSK_FONTDIR@ - @QSK_FONTCACHEDIR@/cache - diff --git a/support/fonts/DejaVuSans.ttf b/support/fonts/DejaVuSans/DejaVuSans.ttf similarity index 100% rename from support/fonts/DejaVuSans.ttf rename to support/fonts/DejaVuSans/DejaVuSans.ttf diff --git a/support/fonts/LICENSE-DejaVuSans b/support/fonts/DejaVuSans/LICENSE.txt similarity index 100% rename from support/fonts/LICENSE-DejaVuSans rename to support/fonts/DejaVuSans/LICENSE.txt diff --git a/support/fonts/LICENSE-Noto b/support/fonts/LICENSE-Noto deleted file mode 100644 index cee3f306..00000000 --- a/support/fonts/LICENSE-Noto +++ /dev/null @@ -1 +0,0 @@ -For the Noto fonts see: https://www.google.com/get/noto/ diff --git a/support/fonts/NotoSansCJKsc-Regular.otf b/support/fonts/NotoSansCJKsc-Regular.otf deleted file mode 100644 index 741201b0..00000000 Binary files a/support/fonts/NotoSansCJKsc-Regular.otf and /dev/null differ diff --git a/support/fonts/LICENSE-Roboto b/support/fonts/Roboto/LICENSE.txt similarity index 100% rename from support/fonts/LICENSE-Roboto rename to support/fonts/Roboto/LICENSE.txt diff --git a/support/fonts/Roboto-Black.ttf b/support/fonts/Roboto/Roboto-Black.ttf similarity index 100% rename from support/fonts/Roboto-Black.ttf rename to support/fonts/Roboto/Roboto-Black.ttf diff --git a/support/fonts/Roboto-BlackItalic.ttf b/support/fonts/Roboto/Roboto-BlackItalic.ttf similarity index 100% rename from support/fonts/Roboto-BlackItalic.ttf rename to support/fonts/Roboto/Roboto-BlackItalic.ttf diff --git a/support/fonts/Roboto-Bold.ttf b/support/fonts/Roboto/Roboto-Bold.ttf similarity index 100% rename from support/fonts/Roboto-Bold.ttf rename to support/fonts/Roboto/Roboto-Bold.ttf diff --git a/support/fonts/Roboto-BoldItalic.ttf b/support/fonts/Roboto/Roboto-BoldItalic.ttf similarity index 100% rename from support/fonts/Roboto-BoldItalic.ttf rename to support/fonts/Roboto/Roboto-BoldItalic.ttf diff --git a/support/fonts/Roboto-Italic.ttf b/support/fonts/Roboto/Roboto-Italic.ttf similarity index 100% rename from support/fonts/Roboto-Italic.ttf rename to support/fonts/Roboto/Roboto-Italic.ttf diff --git a/support/fonts/Roboto-Light.ttf b/support/fonts/Roboto/Roboto-Light.ttf similarity index 100% rename from support/fonts/Roboto-Light.ttf rename to support/fonts/Roboto/Roboto-Light.ttf diff --git a/support/fonts/Roboto-LightItalic.ttf b/support/fonts/Roboto/Roboto-LightItalic.ttf similarity index 100% rename from support/fonts/Roboto-LightItalic.ttf rename to support/fonts/Roboto/Roboto-LightItalic.ttf diff --git a/support/fonts/Roboto-Medium.ttf b/support/fonts/Roboto/Roboto-Medium.ttf similarity index 100% rename from support/fonts/Roboto-Medium.ttf rename to support/fonts/Roboto/Roboto-Medium.ttf diff --git a/support/fonts/Roboto-MediumItalic.ttf b/support/fonts/Roboto/Roboto-MediumItalic.ttf similarity index 100% rename from support/fonts/Roboto-MediumItalic.ttf rename to support/fonts/Roboto/Roboto-MediumItalic.ttf diff --git a/support/fonts/Roboto-Regular.ttf b/support/fonts/Roboto/Roboto-Regular.ttf similarity index 100% rename from support/fonts/Roboto-Regular.ttf rename to support/fonts/Roboto/Roboto-Regular.ttf diff --git a/support/fonts/Roboto-Thin.ttf b/support/fonts/Roboto/Roboto-Thin.ttf similarity index 100% rename from support/fonts/Roboto-Thin.ttf rename to support/fonts/Roboto/Roboto-Thin.ttf diff --git a/support/fonts/Roboto-ThinItalic.ttf b/support/fonts/Roboto/Roboto-ThinItalic.ttf similarity index 100% rename from support/fonts/Roboto-ThinItalic.ttf rename to support/fonts/Roboto/Roboto-ThinItalic.ttf diff --git a/support/fonts/SegoeUI/LICENSE.txt b/support/fonts/SegoeUI/LICENSE.txt new file mode 100644 index 00000000..2ca273a3 --- /dev/null +++ b/support/fonts/SegoeUI/LICENSE.txt @@ -0,0 +1 @@ +For the Segue-UI fonts on Linux see: https://github.com/mrbvrz/segoe-ui-linux diff --git a/support/fonts/SegoeUI/segoeui.ttf b/support/fonts/SegoeUI/segoeui.ttf new file mode 100755 index 00000000..7db735b2 Binary files /dev/null and b/support/fonts/SegoeUI/segoeui.ttf differ diff --git a/support/fonts/SegoeUI/segoeuib.ttf b/support/fonts/SegoeUI/segoeuib.ttf new file mode 100755 index 00000000..82bf9853 Binary files /dev/null and b/support/fonts/SegoeUI/segoeuib.ttf differ diff --git a/support/fonts/SegoeUI/segoeuii.ttf b/support/fonts/SegoeUI/segoeuii.ttf new file mode 100755 index 00000000..418562f1 Binary files /dev/null and b/support/fonts/SegoeUI/segoeuii.ttf differ diff --git a/support/fonts/SegoeUI/segoeuil.ttf b/support/fonts/SegoeUI/segoeuil.ttf new file mode 100755 index 00000000..2bb02990 Binary files /dev/null and b/support/fonts/SegoeUI/segoeuil.ttf differ diff --git a/support/fonts/SegoeUI/segoeuisl.ttf b/support/fonts/SegoeUI/segoeuisl.ttf new file mode 100755 index 00000000..82af19cf Binary files /dev/null and b/support/fonts/SegoeUI/segoeuisl.ttf differ diff --git a/support/fonts/SegoeUI/segoeuiz.ttf b/support/fonts/SegoeUI/segoeuiz.ttf new file mode 100755 index 00000000..17b88720 Binary files /dev/null and b/support/fonts/SegoeUI/segoeuiz.ttf differ diff --git a/support/fonts/SegoeUI/seguibl.ttf b/support/fonts/SegoeUI/seguibl.ttf new file mode 100755 index 00000000..b2966570 Binary files /dev/null and b/support/fonts/SegoeUI/seguibl.ttf differ diff --git a/support/fonts/SegoeUI/seguibli.ttf b/support/fonts/SegoeUI/seguibli.ttf new file mode 100755 index 00000000..d38c82c8 Binary files /dev/null and b/support/fonts/SegoeUI/seguibli.ttf differ diff --git a/support/fonts/SegoeUI/seguili.ttf b/support/fonts/SegoeUI/seguili.ttf new file mode 100755 index 00000000..66a14495 Binary files /dev/null and b/support/fonts/SegoeUI/seguili.ttf differ diff --git a/support/fonts/SegoeUI/seguisb.ttf b/support/fonts/SegoeUI/seguisb.ttf new file mode 100755 index 00000000..3d90a5fc Binary files /dev/null and b/support/fonts/SegoeUI/seguisb.ttf differ diff --git a/support/fonts/SegoeUI/seguisbi.ttf b/support/fonts/SegoeUI/seguisbi.ttf new file mode 100755 index 00000000..99eee5bf Binary files /dev/null and b/support/fonts/SegoeUI/seguisbi.ttf differ diff --git a/support/fonts/SegoeUI/seguisli.ttf b/support/fonts/SegoeUI/seguisli.ttf new file mode 100755 index 00000000..bc6e5d91 Binary files /dev/null and b/support/fonts/SegoeUI/seguisli.ttf differ diff --git a/tools/svg2qvg/CMakeLists.txt b/tools/svg2qvg/CMakeLists.txt index 05507a7b..90ea5a87 100644 --- a/tools/svg2qvg/CMakeLists.txt +++ b/tools/svg2qvg/CMakeLists.txt @@ -46,3 +46,28 @@ target_link_libraries(${target} PRIVATE Qt::Svg) set_target_properties(${target} PROPERTIES FOLDER tools) install(TARGETS ${target}) + +# packaging +set(PACKAGE_TARGET_FILENAME QSkinnyToolsTargets.cmake) + +set_target_properties(${target} PROPERTIES EXPORT_NAME Svg2Qvg) +set_target_properties(${target} PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) + +install(TARGETS ${target} EXPORT ${PACKAGE_TARGET_FILENAME}) + +export(EXPORT ${PACKAGE_TARGET_FILENAME} + NAMESPACE ${PACKAGE_NAMESPACE}) + +install(EXPORT ${PACKAGE_TARGET_FILENAME} + FILE + ${PACKAGE_TARGET_FILENAME} + NAMESPACE + ${PACKAGE_NAMESPACE} + DESTINATION + ${PACKAGE_LOCATION}) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + install(FILES $ + DESTINATION + ${CMAKE_INSTALL_BINDIR} OPTIONAL) +endif() \ No newline at end of file diff --git a/tools/svg2qvg/main.cpp b/tools/svg2qvg/main.cpp index d5ffcf82..3988076a 100644 --- a/tools/svg2qvg/main.cpp +++ b/tools/svg2qvg/main.cpp @@ -25,6 +25,22 @@ static void usage( const char* appName ) qWarning() << "usage: " << appName << "svgfile qvgfile"; } +static QRectF viewBox( QSvgRenderer& renderer ) +{ + /* + QSvgRenderer::viewBoxF() returns a bounding box when no viewBox + has been defined. So we clear the viewBox and compare the result with + the initial value - assuming, that there was a viewBox when they differ. + */ + const auto viewBox = renderer.viewBoxF(); + + renderer.setViewBox( QRectF() ); + const bool hasViewBox = ( viewBox != renderer.viewBoxF() ); + renderer.setViewBox( viewBox ); + + return hasViewBox ? viewBox : QRectF( 0.0, 0.0, -1.0, -1.0 ); +} + int main( int argc, char* argv[] ) { if ( argc != 3 ) @@ -56,6 +72,7 @@ int main( int argc, char* argv[] ) return -2; QskGraphic graphic; + graphic.setViewBox( ::viewBox( renderer ) ); QPainter painter( &graphic ); renderer.render( &painter );