Merge branch 'master' into features/modelobjectbinder

This commit is contained in:
Uwe Rathmann 2024-09-11 18:13:26 +02:00
commit cd8fd0d2b2
142 changed files with 2322 additions and 1614 deletions

View File

@ -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"

View File

@ -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

View File

@ -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 )

View File

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/fluent2">
<file>icons/qvg/checkmark.qvg</file>
<file>icons/qvg/chevron_down.qvg</file>
<file>icons/qvg/chevron_up.qvg</file>
</qresource>
</RCC>

View File

@ -103,6 +103,13 @@
#include <qguiapplication.h>
#include <qfontinfo.h>
static void qskFluent2InitResources()
{
Q_INIT_RESOURCE( QskFluent2Icons );
}
Q_CONSTRUCTOR_FUNCTION( qskFluent2InitResources )
namespace Fluent2
{
using F = QskFontRole;
@ -412,7 +419,6 @@ void Editor::setupCheckBoxMetrics()
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 );
setPadding( Q::Box, 5_px ); // "icon size"
setFontRole( Q::Text, Fluent2::Body );
}
@ -541,9 +547,9 @@ void Editor::setupComboBoxMetrics()
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setFontRole( Q::Text, Fluent2::Body );
setStrutSize( Q::StatusIndicator, 12_px, 12_px );
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_px, 1_px, 1_px, 2_px } );
@ -1497,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_px } );
setBoxBorderMetrics( Q::Panel, 1_px );
setBoxShape( Q::Panel, 3_px );
setPadding( Q::Panel, { 11_px, 0, 11_px, 0 } );
setAlignment( Q::Text, Qt::AlignLeft );
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setFontRole( Q::Text, Fluent2::Body );
setPadding( Q::TextPanel, { 11_px, 5_px, 0, 0 } );
setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } );
setStrutSize( Q::UpPanel, 32_px, 20_px );
setPadding( Q::UpPanel, { 11_px, 7_px, 11_px, 7_px } );
for ( auto panel : { Q::UpPanel, Q::DownPanel } )
setStrutSize( panel, 32_px, 18_px );
setStrutSize( Q::DownPanel, 34_px, 20_px );
setPadding( Q::DownPanel, { 11_px, 7_px, 13_px, 7_px } );
setSymbol( Q::UpIndicator, symbol( "chevron_up" ) );
setSymbol( Q::DownIndicator, symbol( "chevron_down" ) );
setSymbol( Q::UpIndicator, symbol( "spin-box-arrow-up" ) );
setSymbol( Q::DownIndicator, symbol( "spin-box-arrow-down" ) );
setPadding( Q::UpPanel, { 0, 1_px, 0, 0 } );
setPadding( Q::DownPanel, { 0, 0, 0, 1_px } );
// Focused (Pressed doesn't exist yet):
setBoxBorderMetrics( Q::Panel | Q::Focused, { 1_px, 1_px, 1_px, 2_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(
@ -1581,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 );

View File

@ -1,10 +0,0 @@
<RCC>
<qresource prefix="/fluent2">
<file>icons/qvg/checkmark.qvg</file>
<file>icons/qvg/combo-box-arrow-closed.qvg</file>
<file>icons/qvg/combo-box-arrow-open.qvg</file>
<file>icons/qvg/segmented-button-check.qvg</file>
<file>icons/qvg/spin-box-arrow-down.qvg</file>
<file>icons/qvg/spin-box-arrow-up.qvg</file>
</qresource>
</RCC>

View File

@ -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 )

View File

@ -1,4 +1,3 @@
<svg width="11" height="8" viewBox="0 0 11 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.00195312 3.49805C0.00195312 3.36133 0.0507812 3.24414 0.148438 3.14648C0.246094 3.04883 0.363281 3 0.5 3C0.636719 3 0.753906 3.04883 0.851562 3.14648L3.5 5.79492L9.14844 0.146484C9.24609 0.0488281 9.36328 0 9.5 0C9.57031 0 9.63477 0.0136719 9.69336 0.0410156C9.75586 0.0644531 9.80859 0.0996094 9.85156 0.146484C9.89844 0.189453 9.93555 0.242187 9.96289 0.304688C9.99023 0.363281 10.0039 0.427734 10.0039 0.498047C10.0039 0.634766 9.95312 0.753906 9.85156 0.855469L3.85156 6.85547C3.75391 6.95312 3.63672 7.00195 3.5 7.00195C3.36328 7.00195 3.24609 6.95312 3.14844 6.85547L0.148438 3.85547C0.0507812 3.75781 0.00195312 3.63867 0.00195312 3.49805Z" fill="black"/>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.85355 3.14645C10.0488 3.34171 10.0488 3.65829 9.85355 3.85355L5.35355 8.35355C5.15829 8.54882 4.84171 8.54882 4.64645 8.35355L2.64645 6.35355C2.45118 6.15829 2.45118 5.84171 2.64645 5.64645C2.84171 5.45118 3.15829 5.45118 3.35355 5.64645L5 7.29289L9.14645 3.14645C9.34171 2.95118 9.65829 2.95118 9.85355 3.14645Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 777 B

After

Width:  |  Height:  |  Size: 444 B

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.14645 4.64645C2.34171 4.45118 2.65829 4.45118 2.85355 4.64645L6 7.79289L9.14645 4.64645C9.34171 4.45118 9.65829 4.45118 9.85355 4.64645C10.0488 4.84171 10.0488 5.15829 9.85355 5.35355L6.35355 8.85355C6.15829 9.04882 5.84171 9.04882 5.64645 8.85355L2.14645 5.35355C1.95118 5.15829 1.95118 4.84171 2.14645 4.64645Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.14645 7.35355C2.34171 7.54882 2.65829 7.54882 2.85355 7.35355L6 4.20711L9.14645 7.35355C9.34171 7.54882 9.65829 7.54882 9.85355 7.35355C10.0488 7.15829 10.0488 6.84171 9.85355 6.64645L6.35355 3.14645C6.15829 2.95118 5.84171 2.95118 5.64645 3.14645L2.14645 6.64645C1.95118 6.84171 1.95118 7.15829 2.14645 7.35355Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@ -1,4 +0,0 @@
<svg width="8" height="5" viewBox="0 0 8 5" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.25 0.5L4 4.25L7.75 0.5H0.25Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 157 B

View File

@ -1,4 +0,0 @@
<svg width="8" height="5" viewBox="0 0 8 5" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.25 0.5L4 4.25L7.75 0.5H0.25Z" transform="rotate(180 4 2.5)" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 187 B

View File

@ -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

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +0,0 @@
<svg width="14" height="11" viewBox="0 0 14 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.24914 8.12738L1.12164 4.99988L0.0566406 6.05738L4.24914 10.2499L13.2491 1.24988L12.1916 0.192383L4.24914 8.12738Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 246 B

View File

@ -1,4 +0,0 @@
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.5 1.125C0.5 1.02344 0.537109 0.935547 0.611328 0.861328C0.685547 0.787109 0.773438 0.75 0.875 0.75C0.976562 0.75 1.06445 0.787109 1.13867 0.861328L5 4.7168L8.86133 0.861328C8.93555 0.787109 9.02344 0.75 9.125 0.75C9.22656 0.75 9.31445 0.787109 9.38867 0.861328C9.46289 0.935547 9.5 1.02344 9.5 1.125C9.5 1.22656 9.46289 1.31445 9.38867 1.38867L5.26367 5.51367C5.18945 5.58789 5.10156 5.625 5 5.625C4.89844 5.625 4.81055 5.58789 4.73633 5.51367L0.611328 1.38867C0.537109 1.31445 0.5 1.22656 0.5 1.125Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 631 B

View File

@ -1,4 +0,0 @@
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.5 4.875C0.5 4.77344 0.537109 4.68555 0.611328 4.61133L4.73633 0.486328C4.81055 0.412109 4.89844 0.375 5 0.375C5.10156 0.375 5.18945 0.412109 5.26367 0.486328L9.38867 4.61133C9.46289 4.68555 9.5 4.77344 9.5 4.875C9.5 4.97656 9.46289 5.06445 9.38867 5.13867C9.31445 5.21289 9.22656 5.25 9.125 5.25C9.02344 5.25 8.93555 5.21289 8.86133 5.13867L5 1.2832L1.13867 5.13867C1.06445 5.21289 0.976562 5.25 0.875 5.25C0.773438 5.25 0.685547 5.21289 0.611328 5.13867C0.537109 5.06445 0.5 4.97656 0.5 4.875Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 625 B

View File

@ -145,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 );
}
@ -209,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 } )
{
@ -277,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 );
@ -859,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 );
@ -872,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 );
@ -1338,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();
}

View File

@ -24,7 +24,8 @@ class QSK_FUSION_EXPORT QskFusionSkin : public QskSkin
GraphicNormal,
GraphicDisabled,
GraphicHighlighted,
GraphicError
GraphicError,
GraphicIndicator
};
protected:

View File

@ -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 )

View File

@ -0,0 +1,19 @@
<RCC>
<qresource prefix="/m3">
<!-- needed until we have fixed a rendering bug for small icons -->
<file>icons/qvg/check_small.qvg</file>
<file>icons/qvg/combo-box-arrow-closed.qvg</file>
<file>icons/qvg/combo-box-arrow-open.qvg</file>
<file>icons/qvg/segmented-button-check.qvg</file>
<!-- https://github.com/marella/material-design-icons/tree/main/svg/round -->
<file>icons/qvg/add.qvg</file>
<file>icons/qvg/arrow_drop_down.qvg</file>
<file>icons/qvg/arrow_drop_up.qvg</file>
<file>icons/qvg/check.qvg</file>
<file>icons/qvg/remove.qvg</file>
</qresource>
</RCC>

View File

@ -64,6 +64,13 @@
#include <qguiapplication.h>
#include <qfontinfo.h>
static void qskMaterial3InitResources()
{
Q_INIT_RESOURCE( QskMaterial3Icons );
}
Q_CONSTRUCTOR_FUNCTION( qskMaterial3InitResources )
static const int qskDuration = 150;
namespace
@ -105,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
@ -585,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 );
@ -881,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 } )
@ -898,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 );
@ -939,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()

View File

@ -1,8 +0,0 @@
<RCC>
<qresource prefix="/m3">
<file>icons/qvg/check_small.qvg</file>
<file>icons/qvg/combo-box-arrow-closed.qvg</file>
<file>icons/qvg/combo-box-arrow-open.qvg</file>
<file>icons/qvg/segmented-button-check.qvg</file>
</qresource>
</RCC>

View File

@ -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.

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M18 13h-5v5c0 .55-.45 1-1 1s-1-.45-1-1v-5H6c-.55 0-1-.45-1-1s.45-1 1-1h5V6c0-.55.45-1 1-1s1 .45 1 1v5h5c.55 0 1 .45 1 1s-.45 1-1 1z"/></svg>

After

Width:  |  Height:  |  Size: 232 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="m8.71 11.71 2.59 2.59c.39.39 1.02.39 1.41 0l2.59-2.59c.63-.63.18-1.71-.71-1.71H9.41c-.89 0-1.33 1.08-.7 1.71z"/></svg>

After

Width:  |  Height:  |  Size: 211 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M8.71 12.29 11.3 9.7a.996.996 0 0 1 1.41 0l2.59 2.59c.63.63.18 1.71-.71 1.71H9.41c-.89 0-1.33-1.08-.7-1.71z"/></svg>

After

Width:  |  Height:  |  Size: 208 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9 16.17 5.53 12.7a.996.996 0 1 0-1.41 1.41l4.18 4.18c.39.39 1.02.39 1.41 0L20.29 7.71a.996.996 0 1 0-1.41-1.41L9 16.17z"/></svg>

After

Width:  |  Height:  |  Size: 221 B

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M18 13H6c-.55 0-1-.45-1-1s.45-1 1-1h12c.55 0 1 .45 1 1s-.45 1-1 1z"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@ -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]

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View File

@ -9,6 +9,60 @@
#include <QskLinearBox.h>
#include <QskPushButton.h>
#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 <QskMessageSubWindow.h>
#include <QskSelectionSubWindow.h>
#include <qquickwindow.h>
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
{
class Button : public QskPushButton
@ -48,17 +102,32 @@ namespace
private:
void execMessage()
{
qskDialog->information( "Message", "Request vector, over." );
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 ?" );
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()
{
const QString title( "The Teens" );
const QStringList entries =
{
"Give Me More",
@ -81,7 +150,12 @@ namespace
"Gimme Gimme Gimme Gimme Gimme Your Love"
};
qskDialog->select( "The Teens", 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
}
};
}

View File

@ -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

Binary file not shown.

View File

@ -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 );
}
}
};
}

View File

@ -159,6 +159,7 @@ void ProgressBarPage::populate()
{
auto* ring = new QskProgressRing( determinateRingsHBox );
ring->setSize( size );
ring->setLayoutAlignmentHint( Qt::AlignCenter );
QQuickItem* parentItem;

View File

@ -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

View File

@ -1,197 +0,0 @@
/******************************************************************************
* Copyright (C) 2021 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "CircularProgressBar.h"
#include <QskAnimator.h>
#include <QskFunctions.h>
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"

View File

@ -1,61 +0,0 @@
/******************************************************************************
* Copyright (C) 2021 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#pragma once
#include <QskBoundedControl.h>
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;
};

View File

@ -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"

View File

@ -1,36 +0,0 @@
/******************************************************************************
* Copyright (C) 2021 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#pragma once
#include <QskSkinlet.h>
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;
};

View File

@ -14,17 +14,6 @@
#include "TopBar.h"
#include "UsageBox.h"
#include <QskBoxBorderColors.h>
#include <QskBoxBorderMetrics.h>
#include <QskBoxShapeMetrics.h>
#include <QskGridBox.h>
#include <QskSetup.h>
#include <QskSkin.h>
#include <QskTextLabel.h>
#include <QskQuick.h>
#include <QTimer>
QSK_SUBCONTROL( DashboardPage, Panel )
namespace

View File

@ -1,69 +0,0 @@
/******************************************************************************
* Copyright (C) 2021 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "EnergyMeter.h"
#include "CircularProgressBar.h"
#include <QskTextLabel.h>
#include <QskFontRole.h>
namespace
{
class ValueLabel : public QskTextLabel
{
public:
ValueLabel( QQuickItem* parent )
: QskTextLabel( parent )
{
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
setLayoutAlignmentHint( Qt::AlignCenter );
setFontRole( QskFontRole::Caption );
}
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 );
}

View File

@ -1,18 +0,0 @@
/******************************************************************************
* Copyright (C) 2021 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#pragma once
#include <QskControl.h>
class EnergyMeter : public QskControl
{
public:
EnergyMeter( const QColor&, const QskGradient&,
int progress, QQuickItem* parent = nullptr );
protected:
QSizeF contentsSizeHint( Qt::SizeHint, const QSizeF& ) const override;
};

View File

@ -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 );

View File

@ -6,7 +6,6 @@
#pragma once
#include <QskBoundedValueInput.h>
#include <QskBoxShapeMetrics.h>
#include <QskShadowMetrics.h>
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;
};

View File

@ -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,6 +34,7 @@
#include <QskSkinHintTableEditor.h>
#include <QskStateCombination.h>
#include <QskTextLabel.h>
#include <QskProgressRing.h>
#include <QskGraphicLabel.h>
#include <QskFontRole.h>
@ -56,7 +54,6 @@ Skin::Skin( QObject* parent )
{
setObjectName( "iot" );
declareSkinlet< CircularProgressBar, CircularProgressBarSkinlet >();
declareSkinlet< Diagram, DiagramSkinlet >();
declareSkinlet< LightDisplay, LightDisplaySkinlet >();
declareSkinlet< StorageBar, StorageBarSkinlet >();
@ -87,9 +84,9 @@ void Skin::initHints()
ed.setPadding( MainContentGridBox::Panel, { 19, 0, 27, 24 } );
// menu bar:
{
// menu bar:
using Q = QskPushButton;
using A = QskAspect;
@ -113,25 +110,27 @@ 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, { QskFontRole::Caption, QskFontRole::High } );
@ -283,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
@ -302,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" }
} }
);
}
}
@ -322,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
};
@ -339,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
};

View File

@ -34,5 +34,4 @@ class Skin : public QskSkin
void initHints() override;
Palette palette( ColorScheme ) const;
void initHints( const Palette& );
};

View File

@ -1,65 +0,0 @@
/******************************************************************************
* Copyright (C) 2022 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "StorageMeter.h"
#include "CircularProgressBar.h"
#include <QskFontRole.h>
#include <QskTextLabel.h>
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( QskFontRole::Caption );
}
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 );
}

View File

@ -1,22 +0,0 @@
/******************************************************************************
* Copyright (C) 2022 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#pragma once
#include "CircularProgressBar.h"
#include <QskControl.h>
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;
};

View File

@ -6,7 +6,7 @@
#include "StoragePage.h"
#include "Box.h"
#include "StorageBar.h"
#include "StorageMeter.h"
#include "ValueMeter.h"
#include <QTimer>
#include <QskAnimator.h>
#include <QskBox.h>
@ -20,6 +20,60 @@
#include <QskTextLabel.h>
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,8 +139,6 @@ 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( QskFontRole::Headline );
@ -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(); } );
}

View File

@ -7,44 +7,15 @@
#include <QVector>
#include <QskLinearBox.h>
#include <memory>
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 );
};

View File

@ -4,7 +4,7 @@
*****************************************************************************/
#include "TopBar.h"
#include "EnergyMeter.h"
#include "ValueMeter.h"
#include <QskFontRole.h>
#include <QskTextLabel.h>
@ -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(
@ -62,9 +78,7 @@ 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 );

View File

@ -0,0 +1,42 @@
/******************************************************************************
* Copyright (C) 2022 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "ValueMeter.h"
#include <QskFontRole.h>
#include <QskTextLabel.h>
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 );
}

View File

@ -0,0 +1,25 @@
/******************************************************************************
* Copyright (C) 2022 Edelhirsch Software GmbH
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#pragma once
#include <QskProgressRing.h>
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;
};

View File

@ -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

View File

@ -28,7 +28,6 @@ int main( int argc, char* argv[] )
QGuiApplication app( argc, argv );
QskSetup::setUpdateFlag( QskItem::PreferRasterForTextures, true );
qskSkinManager->setSkin( new Skin() );
Qsk::addGraphicProvider( QString(), new GraphicProvider() );

View File

@ -139,8 +139,6 @@ int main( int argc, char* argv[] )
qskSkinManager->registerFactory(
QStringLiteral( "MySkinFactory" ), new MySkinFactory() );
QskSetup::setUpdateFlag( QskItem::PreferRasterForTextures, true );
Window window;
window.resize( 480, 360 );
window.show();

Binary file not shown.

View File

@ -9,153 +9,10 @@
#include <QskIntervalF.h>
#include <QskArcMetrics.h>
#include <QskArcNode.h>
#include <QskArcRenderNode.h>
#include <qpainterpath.h>
#include <qmath.h>
#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 <QskPaintedNode.h>
#include <qpainter.h>
#include <qpainterpath.h>
#include <qpen.h>
#include <qbrush.h>
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;
}

View File

@ -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 );

View File

@ -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 )

View File

@ -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:

Some files were not shown because too many files have changed in this diff Show More