Merge dfa3ab17bb into 7acd0f9c63
|
|
@ -40,8 +40,7 @@ direction - please contact support@qskinny.org.
|
|||
|
||||

|
||||
|
||||
|
||||
# Contributing to QSkinny
|
||||
# Contributing
|
||||
|
||||
QSkinny is licensed under the BSD 3 Clause License ( https://opensource.org/license/bsd-3-clause ). However we want to be able to offer more licenses on request.
|
||||
|
||||
|
|
|
|||
25
doc/Doxyfile
|
|
@ -68,7 +68,7 @@ PROJECT_LOGO =
|
|||
# entered, it will be relative to the location where doxygen was started. If
|
||||
# left blank the current directory will be used.
|
||||
|
||||
OUTPUT_DIRECTORY = api
|
||||
OUTPUT_DIRECTORY =
|
||||
|
||||
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
|
||||
# sub-directories (in 2 levels) under the output directory of each output format
|
||||
|
|
@ -278,7 +278,9 @@ ALIASES = "accessors=\par Access functions:^^" \
|
|||
"states=\par States:^^" \
|
||||
"skinlet=\par Default Skinlet:^^" \
|
||||
"aspect=\par Aspect^^" \
|
||||
"saqt=\sa ^^"
|
||||
"embedWasm=<div> <div id=\"qtspinner\"> <div id=\"qtstatus\"></div> </div> <div id=\"qt-wasm-screen\"> </div> </div>"
|
||||
|
||||
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
|
|
@ -938,9 +940,10 @@ WARN_LOGFILE = Doxygen.log
|
|||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = . \
|
||||
classes \
|
||||
../src
|
||||
INPUT = classes \
|
||||
../src \
|
||||
tutorials \
|
||||
../README.md
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
|
@ -1113,7 +1116,7 @@ FILTER_SOURCE_PATTERNS =
|
|||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
USE_MDFILE_AS_MAINPAGE = ../README.md
|
||||
|
||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||
# characters from position 72 are to be considered as comment. A common
|
||||
|
|
@ -1313,7 +1316,7 @@ HTML_FILE_EXTENSION = .html
|
|||
# of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_HEADER =
|
||||
HTML_HEADER = header.html
|
||||
|
||||
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
|
||||
# generated HTML page. If the tag is left blank doxygen will generate a standard
|
||||
|
|
@ -1323,7 +1326,7 @@ HTML_HEADER =
|
|||
# that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FOOTER =
|
||||
HTML_FOOTER = footer.html
|
||||
|
||||
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
|
||||
# sheet that is used by each HTML page. It can be used to fine-tune the look of
|
||||
|
|
@ -1353,7 +1356,7 @@ HTML_STYLESHEET =
|
|||
# documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET = ./customdoxygen.css
|
||||
HTML_EXTRA_STYLESHEET = ./doxygen-awesome.css
|
||||
|
||||
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the HTML output directory. Note
|
||||
|
|
@ -1376,7 +1379,7 @@ HTML_EXTRA_FILES =
|
|||
# The default value is: AUTO_LIGHT.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COLORSTYLE = AUTO_LIGHT
|
||||
HTML_COLORSTYLE = LIGHT
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
|
|
@ -1696,7 +1699,7 @@ GENERATE_TREEVIEW = YES
|
|||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FULL_SIDEBAR = YES
|
||||
FULL_SIDEBAR = NO
|
||||
|
||||
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
|
||||
# doxygen will group on one line in the generated HTML documentation.
|
||||
|
|
|
|||
|
|
@ -1,61 +1,66 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doxygenlayout version="1.0">
|
||||
<!-- Generated by doxygen 1.8.20 -->
|
||||
<!-- Generated by doxygen 1.9.8 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title="Home"/>
|
||||
<tab type="pages" visible="no" title="" intro=""/>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="topics" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="yes" title="" intro="">
|
||||
<tab type="modulelist" visible="yes" title="" intro=""/>
|
||||
<tab type="modulemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="no" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="concepts" visible="yes" title="">
|
||||
</tab>
|
||||
<tab type="interfaces" visible="yes" title="">
|
||||
<tab type="interfacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="interfacehierarchy" visible="no" title="" intro=""/>
|
||||
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="yes" title="">
|
||||
<tab type="modules" visible="yes" title="Groups" intro=""/>
|
||||
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="no" title="" intro=""/>
|
||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||
<tab type="classmembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="structs" visible="yes" title="">
|
||||
<tab type="structlist" visible="yes" title="" intro=""/>
|
||||
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
</tab>
|
||||
<tab type="exceptions" visible="no" title="">
|
||||
<tab type="exceptionlist" visible="no" title="" intro=""/>
|
||||
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="exceptionhierarchy" visible="no" title="" intro=""/>
|
||||
<tab type="exceptions" visible="yes" title="">
|
||||
<tab type="exceptionlist" visible="yes" title="" intro=""/>
|
||||
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="files" visible="no" title="">
|
||||
<tab type="filelist" visible="no" title="" intro=""/>
|
||||
<tab type="globals" visible="no" title="" intro=""/>
|
||||
<tab type="files" visible="yes" title="">
|
||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||
<tab type="globals" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="examples" visible="no" title="" intro=""/>
|
||||
<tab type="examples" visible="yes" title="" intro=""/>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="no"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<detaileddescription title=""/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
|
||||
<collaborationgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
<services title=""/>
|
||||
<related title="" subtitle=""/>
|
||||
<interfaces title=""/>
|
||||
<properties title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicslots title=""/>
|
||||
<signals title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<protectedtypes title=""/>
|
||||
<protectedslots title=""/>
|
||||
<protectedmethods title=""/>
|
||||
|
|
@ -67,6 +72,7 @@
|
|||
<packagestaticmethods title=""/>
|
||||
<packageattributes title=""/>
|
||||
<packagestaticattributes title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
<privatetypes title=""/>
|
||||
<privateslots title=""/>
|
||||
|
|
@ -75,6 +81,7 @@
|
|||
<privateattributes title=""/>
|
||||
<privatestaticattributes title=""/>
|
||||
<friends title=""/>
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<memberdef>
|
||||
|
|
@ -103,6 +110,7 @@
|
|||
<constantgroups visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
|
|
@ -126,12 +134,21 @@
|
|||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a concept page -->
|
||||
<concept>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<definition visible="yes" title=""/>
|
||||
<detaileddescription title=""/>
|
||||
<authorsection visible="yes"/>
|
||||
</concept>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="$INCLUDE_GRAPH"/>
|
||||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
|
||||
<includegraph visible="yes"/>
|
||||
<includedbygraph visible="yes"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
|
|
@ -139,6 +156,7 @@
|
|||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
|
|
@ -166,12 +184,14 @@
|
|||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="$GROUP_GRAPHS"/>
|
||||
<groupgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
|
|
@ -213,6 +233,25 @@
|
|||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a C++20 module page -->
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<enums title=""/>
|
||||
<typedefs title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
</module>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskCheckBox QskCheckBox.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskCheckBoxSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskComboBox QskComboBox.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskComboBoxSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskDrawer QskDrawer.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskDrawerSkinlet
|
||||
*/
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
/*!
|
||||
\class QskLinearBox QskLinearBox.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\brief Layout stringing items in rows and columns
|
||||
|
||||
QskLinearBox organizes layout items in vertical or horizontal order
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskPageIndicator QskPageIndicator.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskPageIndicatorSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskProgressBar QskProgressBar.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskProgressBarSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/*!
|
||||
\class QskProgressIndicator QskProgressIndicator.h
|
||||
\ingroup Framework Controls
|
||||
|
||||
\brief Base class for progress indicators
|
||||
|
||||
QskProgressIndicator is the base class for circular (QskProgressRing) and linear (QskProgressBar)
|
||||
progress indicators, and should not be instantiated directly.
|
||||
|
||||
There are two modes for progress indicators:
|
||||
- **Determinate**, which means the user has to set a progress value to advance the indicator
|
||||
- **Indeterminate**, which means the indicator will loop forever without the need for updating its value
|
||||
|
||||
By default progress indicators are determinate.
|
||||
|
||||
\subcontrols QskProgressIndicator::Groove, QskProgressIndicator::Fill
|
||||
|
||||
\skinlet QskProgressIndicatorSkinlet
|
||||
|
||||
\sa QskProgressBar, QskProgressRing
|
||||
*/
|
||||
|
||||
/*!
|
||||
\var QskProgressIndicator::Groove
|
||||
|
||||
Indicating the value range that the indicator can have; is drawn below the QskProgressIndicator::Fill.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\var QskProgressIndicator::Fill
|
||||
|
||||
Showing the current value of the indicator; is drawn above the QskProgressIndicator::Groove.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\property qreal QskProgressIndicator::extent
|
||||
|
||||
The extent of the indicator.
|
||||
|
||||
\accessors extent(), setExtent(), extentChanged(), resetExtent()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\property bool QskProgressIndicator::indeterminate
|
||||
|
||||
Whether the indicator is indeterminate or not.
|
||||
|
||||
\accessors isIndeterminate(), setIndeterminate(), indeterminateChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\property qreal QskProgressIndicator::origin
|
||||
|
||||
The origin of the indicator.
|
||||
|
||||
\accessors qreal origin(), setOrigin(), originChanged(), resetOrigin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\property qreal QskProgressIndicator::value
|
||||
|
||||
The value of the indicator.
|
||||
|
||||
\accessors value(), setValue(), valueChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\property qreal QskProgressIndicator::valueAsRatio
|
||||
|
||||
The value of the indicator as ratio.
|
||||
|
||||
\accessors valueAsRatio(), setValueAsRatio(), valueChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::QskProgressIndicator( QQuickItem* )
|
||||
|
||||
Creates a new progress indicator with the given \a parent.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::QskProgressIndicator( qreal, qreal, QQuickItem* )
|
||||
|
||||
Creates a new progress indicator with the progress interval [\a min, \a max] and the given \a parent.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::QskProgressIndicator( const QskIntervalF& interval, QQuickItem* parent )
|
||||
|
||||
Creates a new progress indicator with the given progress \a interval and the given \a parent.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::~QskProgressIndicator()
|
||||
|
||||
Destructor.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::isIndeterminate() const
|
||||
|
||||
Returns whether the progress indicator is indeterminate, i.e. will loop forever.
|
||||
|
||||
\sa setIndeterminate(), indeterminateChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::setIndeterminate( bool )
|
||||
|
||||
Sets whether the progress indicator is indeterminate.
|
||||
|
||||
\sa isIndeterminate(), indeterminateChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::setFillGradient( const QskGradient& )
|
||||
|
||||
Sets the fill gradient by setting the gradient hint of the QskProgressIndicator::Fill subcontrol.
|
||||
|
||||
\sa fillGradient(), resetFillGradient()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::resetFillGradient()
|
||||
|
||||
Resets the fill gradient.
|
||||
|
||||
\sa fillGradient(), setFillGradient()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::fillGradient() const
|
||||
|
||||
Returns the fill gradient.
|
||||
|
||||
\sa setFillGradient(), resetFillGradient()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::setExtent( qreal )
|
||||
|
||||
Sets the size of the extent, i.e. the QskProgressIndicator::Groove subcontrol.
|
||||
|
||||
\sa extent(), extentChanged(), resetExtent()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::resetExtent()
|
||||
|
||||
Resets the extent.
|
||||
|
||||
\sa extent(), setExtent(), extentChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::extent() const
|
||||
|
||||
Returns the extent, i.e. the size of the QskProgressIndicator::Groove subcontrol.
|
||||
|
||||
\sa setExtent(), extentChanged(), resetExtent()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::resetOrigin()
|
||||
|
||||
Resets the origin.
|
||||
|
||||
\sa hasOrigin(), origin( void ), setOrigin(), originChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::origin() const
|
||||
|
||||
Returns the origin, i.e. the value where the progress will start.
|
||||
If no origin has been set via setOrigin(), the minimum value is returned.
|
||||
|
||||
\sa hasOrigin(), setOrigin(), originChanged(), resetOrigin(), QskBoundedControl::minimum()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::hasOrigin() const
|
||||
|
||||
Returns true if an origin has been set via setOrigin().
|
||||
|
||||
\sa origin(), setOrigin(), originChanged(), resetOrigin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::value() const
|
||||
|
||||
Returns the current value.
|
||||
|
||||
\sa valueAsRatio(), setValue(), valueChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::valueAsRatio() const
|
||||
|
||||
Returns the current value as ratio, i.e. as percentage in the range of [0.0, 1.0].
|
||||
|
||||
The ratio is calculated like this: ratio = (value() - minimum()) / (maximum() - minimum()).
|
||||
|
||||
\sa value(), setValueAsRatio(), valueChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::setValue( qreal )
|
||||
|
||||
Sets the value to \a value. If \a value is outside of the boundaries(), it will be bound to this
|
||||
range.
|
||||
|
||||
\sa setValueAsRatio(), value(), valueChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::setValueAsRatio( qreal )
|
||||
|
||||
Sets the value as ratio of [0.0, 1.0].
|
||||
|
||||
If \a ratio is outside of this range, it will be bound to the interval [0.0, 1.0], i.e. if it is
|
||||
smaller than 0, it will be set to 0, and if it is greater than 1, it will be set to 1.
|
||||
|
||||
\sa valueAsRatio(), value(), setValue(), valueChanged()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::setOrigin( qreal )
|
||||
|
||||
Sets the origin.
|
||||
|
||||
\sa origin(), hasOrigin(), originChanged(), resetOrigin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::extentChanged( qreal )
|
||||
|
||||
Will be emitted when the extent changes.
|
||||
|
||||
\sa extent(), setExtent(), resetExtent()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::indeterminateChanged( bool )
|
||||
|
||||
Will be emitted when the indicator changes from indeterminate to determinate or vice versa.
|
||||
|
||||
\sa isIndeterminate(), setIndeterminate()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::valueChanged( qreal )
|
||||
|
||||
Will be emitted when the value changes.
|
||||
|
||||
\sa value(), valueAsRatio(), setValue(), setValueAsRatio()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::originChanged( qreal )
|
||||
|
||||
Will be emitted when the origin changes.
|
||||
|
||||
\sa origin(), hasOrigin(), setOrigin(), resetOrigin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::componentComplete() override
|
||||
|
||||
This will adjust the value appropriately when the component is complete.
|
||||
|
||||
Derived classes overriding this function should call it in their implementation.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QskProgressIndicator::itemChange( ItemChange, const ItemChangeData& )
|
||||
|
||||
Checks whether the visibility has changed and animates the indicator in case it is indeterminate.
|
||||
I.e. when the indicator becomes visible it will start the animator, and when it becomes invisible
|
||||
it will stop it.
|
||||
|
||||
Derived classes overriding this function should call it in their implementation.
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskProgressRing QskProgressRing.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskProgressRingSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskPushButton QskPushButton.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskPushButtonSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskRadioBox QskRadioBox.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskRadioBoxSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskSegmentedBar QskSegmentedBar.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskSegmentedBarSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/*!
|
||||
\class QskSimpleListBox QskSimpleListBox.h
|
||||
|
||||
\embedWasm
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskSlider QskSlider.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskSliderSkinlet
|
||||
*/
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
/*!
|
||||
\class QskSpinBox QskSpinBox.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\brief A control to edit, increment and decrement number values
|
||||
|
||||
QskSpinBox allows the user to choose a value by:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
/*!
|
||||
\class QskSwipeView QskSwipeView.h
|
||||
|
||||
\embedWasm
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskSwitchButton QskSwitchButton.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskSwitchButtonSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/*!
|
||||
\class QskTabBar QskTabBar.h
|
||||
|
||||
\embedWasm
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskTabButton QskTabButton.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskTabButtonSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskTabView QskTabView.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskTabViewSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskTextInput QskTextInput.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskTextInputSkinlet
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
\class QskTextLabel QskTextLabel.h
|
||||
|
||||
\embedWasm
|
||||
|
||||
\skinlet QskTextLabelSkinlet
|
||||
*/
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
/* Skia overrides for doxygen CSS. */
|
||||
|
||||
html {
|
||||
--blue: rgb(0,114,178);
|
||||
--green: rgb(0,158,115);
|
||||
--red: rgb(213,94,0);
|
||||
--orange: rgb(230,159,0);
|
||||
--purple: rgb(204,121,167);
|
||||
--brown: rgb(177,89,40);
|
||||
--gray: rgb(79,79,79);
|
||||
--light-blue: rgb(128,185,217);
|
||||
--light-green: rgb(128,207,185);
|
||||
--light-red: rgb(234,175,128);
|
||||
--light-orange: rgb(243,207,128);
|
||||
--light-purple: rgb(230,188,211);
|
||||
--light-brown: rgb(216,172,148);
|
||||
--light-gray: rgb(168,168,168);
|
||||
|
||||
--dark-blue: rgb(0,65,101);
|
||||
--dark-red: rgb(156,44,8);
|
||||
|
||||
--white: rgb(254,254,254);
|
||||
--dark-white: rgb(240,240,240);
|
||||
--black: rgb(10,10,10);
|
||||
}
|
||||
|
||||
#titlearea {
|
||||
/* background matches Skia logo. */
|
||||
background: rgb(248,248,248);
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
#main-nav .sm {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
h2.groupheader {
|
||||
border-bottom: var(--gray);
|
||||
color: var(--dark-blue);
|
||||
}
|
||||
|
||||
div.qindex, div.navtab{
|
||||
background-color: var(--light-gray);
|
||||
border: 1px solid var(--light-blue);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
.contents a:visited {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
a.qindexHL {
|
||||
background-color: var(--light-gray);
|
||||
color: var(--white);
|
||||
border: 1px double var(--gray);
|
||||
}
|
||||
|
||||
.contents a.qindexHL:visited {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
a.code, a.code:visited, a.line, a.line:visited {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
pre.fragment {
|
||||
border: 1px solid var(--orange);
|
||||
background-color: var(--dark-white);
|
||||
}
|
||||
|
||||
div.fragment {
|
||||
background-color: var(--dark-white);
|
||||
border: 1px solid var(--orange);
|
||||
}
|
||||
|
||||
span.lineno {
|
||||
border-right: 2px solid var(--green);
|
||||
background-color: var(-light-gray);
|
||||
}
|
||||
span.lineno a {
|
||||
background-color: var(--light-gray);
|
||||
}
|
||||
|
||||
span.lineno a:hover {
|
||||
background-color: var(--light-gray);
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
div.ah, span.ah {
|
||||
background-color: var(--black);
|
||||
color: var(--white);
|
||||
border: solid thin var(--gray);
|
||||
box-shadow: 2px 2px 3px var(light-gray);
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
td.indexkey {
|
||||
background-color: var(--light-gray);
|
||||
border: 1px solid var(--orange);
|
||||
}
|
||||
|
||||
td.indexvalue {
|
||||
background-color: var(--light-gray);
|
||||
border: 1px solid var(--orange);
|
||||
}
|
||||
|
||||
tr.memlist {
|
||||
background-color: var(--light-gray);
|
||||
}
|
||||
|
||||
span.keyword {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
span.keywordtype {
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
span.keywordflow {
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
span.comment {
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
span.charliteral {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
span.vhdldigit {
|
||||
color: var(--purple);
|
||||
}
|
||||
|
||||
span.vhdlchar {
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
background-color: var(--light-gray);
|
||||
border-left: 2px solid var(--gray);
|
||||
}
|
||||
|
||||
.memtitle {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.memdoc, dl.reflist dd {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.paramname {
|
||||
color: var(--dark-red);
|
||||
}
|
||||
|
||||
.tabsearch {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.navpath ul {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.navpath li {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.navpath li.navelem a:hover {
|
||||
color: var(--blue)
|
||||
}
|
||||
|
||||
.navpath li.footer {
|
||||
background-image:none;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!-- HTML footer for doxygen 1.9.8-->
|
||||
<!-- start footer part -->
|
||||
<!--BEGIN GENERATE_TREEVIEW-->
|
||||
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
$navpath
|
||||
<li class="footer">$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion </li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--END GENERATE_TREEVIEW-->
|
||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||
<hr class="footer"/><address class="footer"><small>
|
||||
$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion
|
||||
</small></address>
|
||||
<!--END !GENERATE_TREEVIEW-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -30,12 +30,53 @@ doxygen
|
|||
```
|
||||
This will generate the documentation into the `api` folder.
|
||||
|
||||
### Generating specific header and footer files for doxygen
|
||||
|
||||
*This needs to be done only when the doxygen version changes.*
|
||||
|
||||
```
|
||||
cd ~/dev/qskinny/doc
|
||||
doxygen -w html api/header.html api/footer.html
|
||||
```
|
||||
|
||||
Now open `api/header.html` and copy the code block starting with
|
||||
the comment `<!-- QSkinny WASM code -->` into the new header file.
|
||||
|
||||
*Also*, change the `<body>` tag to load the WASM code at
|
||||
startup:
|
||||
|
||||
```
|
||||
<body onLoad="docSampleInit()">
|
||||
```
|
||||
|
||||
### Generating a doxygen layout page
|
||||
|
||||
*This needs to be done only when the doxygen version changes.*
|
||||
|
||||
```
|
||||
doxygen -l
|
||||
```
|
||||
|
||||
Make sure that the tag labeled `<detaileddescription>` comes before
|
||||
the tag `<inheritancegraph>`, because the former contains the WASM
|
||||
code, which should be displayed near the top of the page.
|
||||
|
||||
|
||||
### Using a different stylesheet for doxygen
|
||||
```
|
||||
git clone git clone git@github.com:jothepro/doxygen-awesome-css.git
|
||||
cd doxygen-awesome-css
|
||||
git checkout v2.3.4
|
||||
cp doxygen-awesome.css ~/dev/qskinny/doc/
|
||||
```
|
||||
|
||||
### Testing and building the website locally
|
||||
|
||||
First copy the generated files from above to the website repo:
|
||||
|
||||
```
|
||||
cp -r api ~/dev/qskinny-website/docs/
|
||||
cp -r html ~/dev/qskinny-website/docs/
|
||||
cp -r images ~/dev/qskinny-website/docs/
|
||||
```
|
||||
|
||||
Then test the website locally:
|
||||
|
|
@ -62,7 +103,7 @@ new version of the homepage:
|
|||
cp -r _site/* ~/dev/qskinny.github.io/
|
||||
cd ~/dev/qskinny.github.io/
|
||||
git commit -a -m "new version" # you might want to add new files
|
||||
gith push
|
||||
git push
|
||||
```
|
||||
|
||||
That's it, the new website is now published at https://qskinny.github.io/ .
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
<!-- HTML header for doxygen 1.9.8-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<script type="text/javascript">var page_layout=1;</script>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
<!-- QSkinny WASM code -->
|
||||
<script type="text/javascript">
|
||||
async function docSampleInit()
|
||||
{
|
||||
const spinner = document.querySelector('#qtspinner');
|
||||
const screen = document.querySelector('#qt-wasm-screen');
|
||||
const status = document.querySelector('#qtstatus');
|
||||
|
||||
const showUi = (ui) => {
|
||||
[spinner, screen].forEach(element => element.style.display = 'none');
|
||||
if (screen === ui)
|
||||
{
|
||||
screen.style.position = 'default';
|
||||
screen.style.width = '500px';
|
||||
screen.style.height = '300px';
|
||||
}
|
||||
ui.style.display = 'block';
|
||||
}
|
||||
|
||||
try {
|
||||
if(spinner)
|
||||
{
|
||||
showUi(spinner);
|
||||
status.innerHTML = 'Loading...';
|
||||
|
||||
const instance = await qtLoad({
|
||||
qt: {
|
||||
onLoaded: () => showUi(screen),
|
||||
onExit: exitData =>
|
||||
{
|
||||
status.innerHTML = 'Application exit';
|
||||
status.innerHTML +=
|
||||
exitData.code !== undefined ? ` with code ` : '';
|
||||
status.innerHTML +=
|
||||
exitData.text !== undefined ? ` ()` : '';
|
||||
showUi(spinner);
|
||||
},
|
||||
entryFunction: window.createQtAppInstance,
|
||||
containerElements: [screen],
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error(e.stack);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="apiDocumentationSamples.js"></script>
|
||||
<script type="text/javascript" src="qtloader.js"></script>
|
||||
<!-- end QSkinny WASM code -->
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
$darkmode
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body onLoad="docSampleInit()">
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr id="projectrow">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign">
|
||||
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN !FULL_SIDEBAR-->
|
||||
<td>$searchbox</td>
|
||||
<!--END !FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<tr><td colspan="2">$searchbox</td></tr>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
/**
|
||||
* Loads the instance of a WASM module.
|
||||
*
|
||||
* @param config May contain any key normally accepted by emscripten and the 'qt' extra key, with
|
||||
* the following sub-keys:
|
||||
* - environment: { [name:string] : string }
|
||||
* environment variables set on the instance
|
||||
* - onExit: (exitStatus: { text: string, code?: number, crashed: bool }) => void
|
||||
* called when the application has exited for any reason. There are two cases:
|
||||
* aborted: crashed is true, text contains an error message.
|
||||
* exited: crashed is false, code contians the exit code.
|
||||
*
|
||||
* Note that by default Emscripten does not exit when main() returns. This behavior
|
||||
* is controlled by the EXIT_RUNTIME linker flag; set "-s EXIT_RUNTIME=1" to make
|
||||
* Emscripten tear down the runtime and exit when main() returns.
|
||||
*
|
||||
* - containerElements: HTMLDivElement[]
|
||||
* Array of host elements for Qt screens. Each of these elements is mapped to a QScreen on
|
||||
* launch.
|
||||
* - fontDpi: number
|
||||
* Specifies font DPI for the instance
|
||||
* - onLoaded: () => void
|
||||
* Called when the module has loaded.
|
||||
* - entryFunction: (emscriptenConfig: object) => Promise<EmscriptenModule>
|
||||
* Qt always uses emscripten's MODULARIZE option. This is the MODULARIZE entry function.
|
||||
* - module: Promise<WebAssembly.Module>
|
||||
* The module to create the instance from (optional). Specifying the module allows optimizing
|
||||
* use cases where several instances are created from a single WebAssembly source.
|
||||
* - qtdir: string
|
||||
* Path to Qt installation. This path will be used for loading Qt shared libraries and plugins.
|
||||
* The path is set to 'qt' by default, and is relative to the path of the web page's html file.
|
||||
* This property is not in use when static linking is used, since this build mode includes all
|
||||
* libraries and plugins in the wasm file.
|
||||
* - preload: [string]: Array of file paths to json-encoded files which specifying which files to preload.
|
||||
* The preloaded files will be downloaded at application startup and copied to the in-memory file
|
||||
* system provided by Emscripten.
|
||||
*
|
||||
* Each json file must contain an array of source, destination objects:
|
||||
* [
|
||||
* {
|
||||
* "source": "path/to/source",
|
||||
* "destination": "/path/to/destination"
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* The source path is relative to the html file path. The destination path must be
|
||||
* an absolute path.
|
||||
*
|
||||
* $QTDIR may be used as a placeholder for the "qtdir" configuration property (see @qtdir), for instance:
|
||||
* "source": "$QTDIR/plugins/imageformats/libqjpeg.so"
|
||||
*
|
||||
* @return Promise<{
|
||||
* instance: EmscriptenModule,
|
||||
* exitStatus?: { text: string, code?: number, crashed: bool }
|
||||
* }>
|
||||
* The promise is resolved when the module has been instantiated and its main function has been
|
||||
* called. The returned exitStatus is defined if the application crashed or exited immediately
|
||||
* after its entry function has been called. Otherwise, config.onExit will get called at a
|
||||
* later time when (and if) the application exits.
|
||||
*
|
||||
* @see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/emscripten for
|
||||
* EmscriptenModule
|
||||
*/
|
||||
async function qtLoad(config)
|
||||
{
|
||||
const throwIfEnvUsedButNotExported = (instance, config) =>
|
||||
{
|
||||
const environment = config.environment;
|
||||
if (!environment || Object.keys(environment).length === 0)
|
||||
return;
|
||||
const isEnvExported = typeof instance.ENV === 'object';
|
||||
if (!isEnvExported)
|
||||
throw new Error('ENV must be exported if environment variables are passed');
|
||||
};
|
||||
|
||||
const throwIfFsUsedButNotExported = (instance, config) =>
|
||||
{
|
||||
const environment = config.environment;
|
||||
if (!environment || Object.keys(environment).length === 0)
|
||||
return;
|
||||
const isFsExported = typeof instance.FS === 'object';
|
||||
if (!isFsExported)
|
||||
throw new Error('FS must be exported if preload is used');
|
||||
};
|
||||
|
||||
if (typeof config !== 'object')
|
||||
throw new Error('config is required, expected an object');
|
||||
if (typeof config.qt !== 'object')
|
||||
throw new Error('config.qt is required, expected an object');
|
||||
if (typeof config.qt.entryFunction !== 'function')
|
||||
config.qt.entryFunction = window.createQtAppInstance;
|
||||
|
||||
config.qt.qtdir ??= 'qt';
|
||||
config.qt.preload ??= [];
|
||||
|
||||
config.qtContainerElements = config.qt.containerElements;
|
||||
delete config.qt.containerElements;
|
||||
config.qtFontDpi = config.qt.fontDpi;
|
||||
delete config.qt.fontDpi;
|
||||
|
||||
// Used for rejecting a failed load's promise where emscripten itself does not allow it,
|
||||
// like in instantiateWasm below. This allows us to throw in case of a load error instead of
|
||||
// hanging on a promise to entry function, which emscripten unfortunately does.
|
||||
let circuitBreakerReject;
|
||||
const circuitBreaker = new Promise((_, reject) => { circuitBreakerReject = reject; });
|
||||
|
||||
// If module async getter is present, use it so that module reuse is possible.
|
||||
if (config.qt.module) {
|
||||
config.instantiateWasm = async (imports, successCallback) =>
|
||||
{
|
||||
try {
|
||||
const module = await config.qt.module;
|
||||
successCallback(
|
||||
await WebAssembly.instantiate(module, imports), module);
|
||||
} catch (e) {
|
||||
circuitBreakerReject(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const qtPreRun = (instance) => {
|
||||
// Copy qt.environment to instance.ENV
|
||||
throwIfEnvUsedButNotExported(instance, config);
|
||||
for (const [name, value] of Object.entries(config.qt.environment ?? {}))
|
||||
instance.ENV[name] = value;
|
||||
|
||||
// Copy self.preloadData to MEMFS
|
||||
const makeDirs = (FS, filePath) => {
|
||||
const parts = filePath.split("/");
|
||||
let path = "/";
|
||||
for (let i = 0; i < parts.length - 1; ++i) {
|
||||
const part = parts[i];
|
||||
if (part == "")
|
||||
continue;
|
||||
path += part + "/";
|
||||
try {
|
||||
FS.mkdir(path);
|
||||
} catch (error) {
|
||||
const EEXIST = 20;
|
||||
if (error.errno != EEXIST)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
throwIfFsUsedButNotExported(instance, config);
|
||||
for ({destination, data} of self.preloadData) {
|
||||
makeDirs(instance.FS, destination);
|
||||
instance.FS.writeFile(destination, new Uint8Array(data));
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.preRun)
|
||||
config.preRun = [];
|
||||
config.preRun.push(qtPreRun);
|
||||
|
||||
config.onRuntimeInitialized = () => config.qt.onLoaded?.();
|
||||
|
||||
const originalLocateFile = config.locateFile;
|
||||
config.locateFile = filename =>
|
||||
{
|
||||
const originalLocatedFilename = originalLocateFile ? originalLocateFile(filename) : filename;
|
||||
if (originalLocatedFilename.startsWith('libQt6'))
|
||||
return `${config.qt.qtdir}/lib/${originalLocatedFilename}`;
|
||||
return originalLocatedFilename;
|
||||
}
|
||||
|
||||
const originalOnExit = config.onExit;
|
||||
config.onExit = code => {
|
||||
originalOnExit?.();
|
||||
config.qt.onExit?.({
|
||||
code,
|
||||
crashed: false
|
||||
});
|
||||
}
|
||||
|
||||
const originalOnAbort = config.onAbort;
|
||||
config.onAbort = text =>
|
||||
{
|
||||
originalOnAbort?.();
|
||||
|
||||
aborted = true;
|
||||
config.qt.onExit?.({
|
||||
text,
|
||||
crashed: true
|
||||
});
|
||||
};
|
||||
|
||||
const fetchPreloadFiles = async () => {
|
||||
const fetchJson = async path => (await fetch(path)).json();
|
||||
const fetchArrayBuffer = async path => (await fetch(path)).arrayBuffer();
|
||||
const loadFiles = async (paths) => {
|
||||
const source = paths['source'].replace('$QTDIR', config.qt.qtdir);
|
||||
return {
|
||||
destination: paths['destination'],
|
||||
data: await fetchArrayBuffer(source)
|
||||
};
|
||||
}
|
||||
const fileList = (await Promise.all(config.qt.preload.map(fetchJson))).flat();
|
||||
self.preloadData = (await Promise.all(fileList.map(loadFiles))).flat();
|
||||
}
|
||||
|
||||
await fetchPreloadFiles();
|
||||
|
||||
// Call app/emscripten module entry function. It may either come from the emscripten
|
||||
// runtime script or be customized as needed.
|
||||
let instance;
|
||||
try {
|
||||
instance = await Promise.race(
|
||||
[circuitBreaker, config.qt.entryFunction(config)]);
|
||||
} catch (e) {
|
||||
config.qt.onExit?.({
|
||||
text: e.message,
|
||||
crashed: true
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Compatibility API. This API is deprecated,
|
||||
// and will be removed in a future version of Qt.
|
||||
function QtLoader(qtConfig) {
|
||||
|
||||
const warning = 'Warning: The QtLoader API is deprecated and will be removed in ' +
|
||||
'a future version of Qt. Please port to the new qtLoad() API.';
|
||||
console.warn(warning);
|
||||
|
||||
let emscriptenConfig = qtConfig.moduleConfig || {}
|
||||
qtConfig.moduleConfig = undefined;
|
||||
const showLoader = qtConfig.showLoader;
|
||||
qtConfig.showLoader = undefined;
|
||||
const showError = qtConfig.showError;
|
||||
qtConfig.showError = undefined;
|
||||
const showExit = qtConfig.showExit;
|
||||
qtConfig.showExit = undefined;
|
||||
const showCanvas = qtConfig.showCanvas;
|
||||
qtConfig.showCanvas = undefined;
|
||||
if (qtConfig.canvasElements) {
|
||||
qtConfig.containerElements = qtConfig.canvasElements
|
||||
qtConfig.canvasElements = undefined;
|
||||
} else {
|
||||
qtConfig.containerElements = qtConfig.containerElements;
|
||||
qtConfig.containerElements = undefined;
|
||||
}
|
||||
emscriptenConfig.qt = qtConfig;
|
||||
|
||||
let qtloader = {
|
||||
exitCode: undefined,
|
||||
exitText: "",
|
||||
loadEmscriptenModule: _name => {
|
||||
try {
|
||||
qtLoad(emscriptenConfig);
|
||||
} catch (e) {
|
||||
showError?.(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qtConfig.onLoaded = () => {
|
||||
showCanvas?.();
|
||||
}
|
||||
|
||||
qtConfig.onExit = exit => {
|
||||
qtloader.exitCode = exit.code
|
||||
qtloader.exitText = exit.text;
|
||||
showExit?.();
|
||||
}
|
||||
|
||||
showLoader?.("Loading");
|
||||
|
||||
return qtloader;
|
||||
};
|
||||
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
|
@ -1,6 +1,4 @@
|
|||
/*!
|
||||
\mainpage Introduction
|
||||
|
||||
The (Q)Skinny library contains a set of lightweight Qt Quick Controls.
|
||||
It is implemented to be 100% usable in C++, but can be used from C++ and/or QML
|
||||
application code. Being "skinny", the framework is intended to be both lightweight
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
---
|
||||
title: 1. What is QSkinny?
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 1. What is QSkinny?
|
||||
:notitle:
|
||||
## What is QSkinny?
|
||||
|
||||
QSkinny is a UI framework based on the Qt graphic stack and written in
|
||||
{cpp}. It allows users to write their UIs in {cpp} and/or QML.
|
||||
|
||||
.The Fendt Tractor GUI
|
||||
image::https://www.fendt.com/de/images/5d19bb4e7b260601c8134e14_1673943667_web_de-DE.jpg[Fendt Tractor GUI]
|
||||
**The Fendt Tractor GUI**
|
||||
|
||||

|
||||
|
||||
It is currently being used in the Fendt Tractor GUI project, see the
|
||||
picture above. For the Fendt Tractor GUI there is no QML used at all;
|
||||
the whole codebase is written in {cpp}. An overview of how QSkinny fits
|
||||
into the Qt architecture is depicted below:
|
||||
|
||||
.QSkinny sits on top of QtQuick, while QML is optional
|
||||
image::/doc/tutorials/images/architecture-simple.png[QSkinny architecture]
|
||||
**QSkinny sits on top of QtQuick, while QML is optional**
|
||||
|
||||

|
||||
|
|
@ -1,10 +1,6 @@
|
|||
---
|
||||
title: 2. Why QSkinny?
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 2. Why QSkinny?
|
||||
:notitle:
|
||||
## Why QSkinny?
|
||||
|
||||
The typical questions about QSkinny are: Why was QSkinny created? And why would
|
||||
somebody use QSkinny and not QML?
|
||||
|
|
@ -12,63 +8,62 @@ somebody use QSkinny and not QML?
|
|||
Which technology to use always depends on the specific use case. However,
|
||||
QSkinny does have some advantages:
|
||||
|
||||
== 1. It's {cpp}
|
||||
### 1. It's C++
|
||||
|
||||
QSkinny is written in {cpp}, so there is no new syntax or programming paradigm
|
||||
QSkinny is written in C++, so there is no new syntax or programming paradigm
|
||||
to learn as is the case with QML. Of course QSkinny has concepts that
|
||||
new programmers need to become familiar with, but they should be understandable
|
||||
for people who know {cpp}. Especially programmers experienced with
|
||||
for people who know C++. Especially programmers experienced with
|
||||
QtWidgets should feel comfortable with QSkinny right away.
|
||||
|
||||
=== 1.1 Integration with other build systems / IDEs
|
||||
#### 1.1 Integration with other build systems / IDEs
|
||||
|
||||
While QtCreator is the natural choice of *IDE* for Qt programmers,
|
||||
While QtCreator is the natural choice of **IDE** for Qt programmers,
|
||||
some people prefer other IDEs, e.g. Visual
|
||||
Studio (Code), Eclipse, CLion etc. Such IDEs usually don't have language support
|
||||
Studio (Code), Eclipse, CLion etc. Such IDEs usually don’t have language support
|
||||
for QML like type completion and other features. So when using QML you are
|
||||
either bound to using QtCreator, or use another IDE and live with the fact that
|
||||
the IDE will not understand QML.
|
||||
|
||||
When it comes to *build systems*, some QML tools might be hard to integrate:
|
||||
When it comes to **build systems**, some QML tools might be hard to integrate:
|
||||
For instance in Visual Studio projects it is difficult to invoke the QML
|
||||
compiler through the build system.
|
||||
|
||||
With QSkinny being written completely in {cpp}, it can be used with any IDE and
|
||||
With QSkinny being written completely in C++, it can be used with any IDE and
|
||||
should integrate nicely with other build systems. QSkinny is using Qt-specific
|
||||
concepts like signals and slots and invokable methods though.
|
||||
|
||||
=== 1.2 Use {cpp} tooling for your whole codebase
|
||||
#### 1.2 Use C++ tooling for your whole codebase
|
||||
|
||||
{cpp} has extensive tooling that assists with writing code, for instance:
|
||||
C++ has extensive tooling that assists with writing code, for instance:
|
||||
|
||||
- gdb and other debuggers
|
||||
- valgrind
|
||||
- address sanitizer and other sanitizers
|
||||
- static code analysis tools
|
||||
- code coverage tools (e.g. gcov)
|
||||
- auto test frameworks
|
||||
- (a lot more, e.g. clang tools)
|
||||
* gdb and other debuggers
|
||||
* valgrind
|
||||
* address sanitizer and other sanitizers
|
||||
* static code analysis tools
|
||||
* code coverage tools (e.g. gcov)
|
||||
* auto test frameworks
|
||||
* (a lot more, e.g. clang tools)
|
||||
|
||||
E.g. QtCreator will let you know about potential problems in your code while
|
||||
you type, e.g. "unused variable", "calling a virtual method from the constructor
|
||||
of a class" etc., and it might even suggest an automatic fix for it.
|
||||
|
||||
QML does have some tooling, but its feature set is nowhere near the support of
|
||||
{cpp}.
|
||||
C++.
|
||||
|
||||
When writing your whole codebase in {cpp} with QSkinny, the tooling can be used
|
||||
When writing your whole codebase in C++ with QSkinny, the tooling can be used
|
||||
for the whole codebase, so also UI code can be debugged, auto tested for a
|
||||
CI system, and so on.
|
||||
|
||||
In addition, {cpp} has concepts that QML as a declarative language doesn't,
|
||||
In addition, C++ has concepts that QML as a declarative language doesn’t,
|
||||
like inheritance and overloading. This makes it easier to implement concepts
|
||||
like event handling, see <<Styling>> below.
|
||||
like event handling, see [Styling](#Styling) below.
|
||||
|
||||
|
||||
== 2. Easy data binding
|
||||
### 2. Easy data binding
|
||||
|
||||
When displaying data from a backend in a QML UI, that data needs to be in a
|
||||
certain format: It needs to be made readable by Qt's Meta Object system via
|
||||
certain format: It needs to be made readable by Qt’s Meta Object system via
|
||||
`Q_PROPERTY`, `Q_INVOKABLE`, `Q_SIGNAL` and others.
|
||||
|
||||
Also, for each model that is used in QML there typically needs to be one
|
||||
|
|
@ -76,19 +71,18 @@ subclass of `QAbstractListModel`, which serves as an adapter class. The process
|
|||
of subclassing and implementing virtual methods can be cumbersome, and lead to
|
||||
lots of boilerplate code.
|
||||
|
||||
QSkinny doesn't need any adaptation layer per se, the data just needs to be
|
||||
connected to the frontend with standard {cpp} functionality. Of course classes
|
||||
QSkinny doesn’t need any adaptation layer per se, the data just needs to be
|
||||
connected to the frontend with standard C++ functionality. Of course classes
|
||||
like the aforementioned `QAbstractListModel` can be used when it makes sense,
|
||||
but this is up to the user.
|
||||
|
||||
### 3. Layouts
|
||||
|
||||
== 3. Layouts
|
||||
|
||||
Whe it comes to *layouts*, QSkinny has a complete concept of laying out the UI,
|
||||
Whe it comes to **layouts**, QSkinny has a complete concept of laying out the UI,
|
||||
or in other words: The user can determine in a fine-grained way what happens
|
||||
when there is too little or too much space available.
|
||||
Concepts like size hints, size policies, stretch factors and others are concepts
|
||||
that were already available in QtWidgets and Qt's Graphics View Framework, and
|
||||
that were already available in QtWidgets and Qt’s Graphics View Framework, and
|
||||
are now supported in QSkinny.
|
||||
|
||||
Why are layouts important? QML was created under the premise that in contrast to
|
||||
|
|
@ -98,33 +92,31 @@ thus size changes will rarely happen.
|
|||
This is true for many cases, however layout code gets important when one of the
|
||||
following events happen:
|
||||
|
||||
- The UI needs to run on two or more screen sizes
|
||||
- Language or style changes need to be supported
|
||||
- The window is resized, e.g. when the Android virtual keyboard pops up
|
||||
* The UI needs to run on two or more screen sizes
|
||||
* Language or style changes need to be supported
|
||||
* The window is resized, e.g. when the Android virtual keyboard pops up
|
||||
|
||||
QSkinny allows the user to take the above use cases into account, but doesn't
|
||||
QSkinny allows the user to take the above use cases into account, but doesn’t
|
||||
force the developer to write overly complex code: A UI written with QSkinny can
|
||||
be coded with fixed sizes for UI elements, as it is typically done in QML.
|
||||
|
||||
|
||||
== [[Styling]] 4. Styling / Adding custom controls
|
||||
### 4. Styling / Adding custom controls
|
||||
|
||||
Qt Quick Controls 2 support different styles, and it even comes with several
|
||||
built-in styles like a Google Material style and a Microsoft Universal style.
|
||||
|
||||
One drawback with Qt Quick Controls 2 is that application developers can only
|
||||
add custom types in QML, not in {cpp}. This makes it cumbersome for concepts
|
||||
add custom types in QML, not in C++. This makes it cumbersome for concepts
|
||||
like event handling, as is noted in the Qt documentation:
|
||||
https://doc.qt.io/qt-5/qtquickcontrols2-differences.html[Differences with Qt Quick Controls 1,role=external,window=_blank].
|
||||
[Differences with Qt Quick Controls 1](https://doc.qt.io/qt-5/qtquickcontrols2-differences.html).
|
||||
|
||||
So an application developer who wants to add own types, as is common for medium
|
||||
to large-scale projects, will have to implement these custom types in QML.
|
||||
Since being able to use {cpp} for application logic of components seems to have been
|
||||
one reason to create Qt Quick Controls 2 (another reason being performance
|
||||
issues with Qt Quick Controls 1, see
|
||||
https://www.qt.io/blog/2015/03/31/qt-quick-controls-for-embedded[Qt Quick Controls for Embedded,role=external,window=_blank]), allowing the user to write controls in {cpp} gives the user more flexibility.
|
||||
Since being able to use C++ for application logic of components seems to have been
|
||||
one reason to create Qt Quick Controls 2 (another reason apparently being performance
|
||||
issues with Qt Quick Controls 1), allowing the user to write controls in C++ gives the user more flexibility.
|
||||
|
||||
QSkinny allows for implementing custom types in {cpp}; also both built-in
|
||||
QSkinny allows for implementing custom types in C++; also both built-in
|
||||
components like push buttons, sliders etc. as well as custom types can be easily
|
||||
styled from {cpp}. The latter can be achieved by simply adding style
|
||||
styled from C++. The latter can be achieved by simply adding style
|
||||
descriptions in user code.
|
||||
|
|
@ -1,14 +1,8 @@
|
|||
---
|
||||
title: 3. Writing your first application
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 3. Writing your first application
|
||||
:notitle:
|
||||
## Writing your first application
|
||||
|
||||
== Writing your first application
|
||||
|
||||
=== Building the QSkinny repository
|
||||
### Building the QSkinny repository
|
||||
|
||||
In this chapter we will write a simple QSkinny application on Linux from scratch in C++ with Qt6.
|
||||
As a prerequisite, a supported Qt6 version should be available.
|
||||
|
|
@ -20,39 +14,35 @@ Optional packages for the virtual keyboard are `libhunspell-dev libimepinyin-dev
|
|||
|
||||
Then we can build and install QSkinny to `/opt/qskinny` with the following commands:
|
||||
|
||||
[source,shell]
|
||||
....
|
||||
```shell
|
||||
$ git clone https://github.com/uwerat/qskinny.git # clone
|
||||
$ cd qskinny
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ cmake --build .
|
||||
$ sudo cmake --install . --prefix "/opt/qskinny"
|
||||
....
|
||||
|
||||
```
|
||||
|
||||
Considering that you want to use a specific Qt version that is installed below "/path/to/qt"
|
||||
you have 2 options:
|
||||
|
||||
[source,shell]
|
||||
....
|
||||
```shell
|
||||
$ cmake .. -DCMAKE_PREFIX_PATH=/path/to/qt
|
||||
....
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
[source,shell]
|
||||
....
|
||||
```shell
|
||||
$ /path/to/qt/bin/qt-cmake ..
|
||||
....
|
||||
```
|
||||
|
||||
=== Compiling our first app
|
||||
### Compiling our first app
|
||||
|
||||
As a next step, we need to write our app. Let's start with a simple `main.cpp` file in a directory `myapp`:
|
||||
As a next step, we need to write our app. Let’s start with a simple `main.cpp` file in a directory `myapp`:
|
||||
|
||||
.main.cpp
|
||||
[source]
|
||||
....
|
||||
**main.cpp**
|
||||
|
||||
```
|
||||
#include <QskWindow.h>
|
||||
#include <QGuiApplication>
|
||||
|
||||
|
|
@ -65,14 +55,14 @@ int main( int argc, char* argv[] )
|
|||
|
||||
return app.exec();
|
||||
}
|
||||
....
|
||||
```
|
||||
|
||||
For now this will just create an empty window (the `QskWindow`) without any controls.
|
||||
Next, we need to create a `CMakeLists.txt` file in our `myapp` directory.
|
||||
|
||||
.CMakeLists.txt
|
||||
[source,cmake]
|
||||
....
|
||||
**CMakeLists.txt**
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
|
||||
project(myapp
|
||||
|
|
@ -95,36 +85,34 @@ add_executable(myapp
|
|||
target_link_libraries(myapp PRIVATE
|
||||
Qt6::Quick
|
||||
Qsk::QSkinny)
|
||||
....
|
||||
```
|
||||
|
||||
Now we can compile our app:
|
||||
|
||||
[source,shell]
|
||||
....
|
||||
```shell
|
||||
$ cd myapp
|
||||
$ mkdir build && cd build
|
||||
$ cmake ../ && make
|
||||
....
|
||||
```
|
||||
|
||||
When running myapp it needs to find the skin plugins. Setting QT_PLUGIN_PATH is one
|
||||
option ( see https://doc.qt.io/qt/deployment-plugins.html ):
|
||||
|
||||
[source,shell]
|
||||
....
|
||||
```shell
|
||||
$ QT_PLUGIN_PATH=/opt/qskinny/plugins ./myapp
|
||||
....
|
||||
```
|
||||
|
||||
This should show just an empty window.
|
||||
|
||||
=== Adding UI controls
|
||||
### Adding UI controls
|
||||
|
||||
Now that we have our app running, we can add some UI controls to it by extending the `main.cpp` file we created earlier.
|
||||
We will add some additional include directives, and then create a horizontal layout containing two push buttons.
|
||||
The layout with the two buttons will be shown in the window. Below is the complete updated source file:
|
||||
|
||||
.main.cpp
|
||||
[source, cpp]
|
||||
....
|
||||
**main.cpp**
|
||||
|
||||
```cpp
|
||||
#include <QskWindow.h>
|
||||
#include <QskLinearBox.h>
|
||||
#include <QskPushButton.h>
|
||||
|
|
@ -153,12 +141,12 @@ int main( int argc, char* argv[] )
|
|||
|
||||
return app.exec();
|
||||
}
|
||||
....
|
||||
```
|
||||
|
||||
Now the app is displaying the two buttons:
|
||||
|
||||
image::/doc/tutorials/images/writing-first-application.png[An app showing two buttons]
|
||||

|
||||
|
||||
That's it; you just created a QSkinny application from scratch.
|
||||
That’s it; you just created a QSkinny application from scratch.
|
||||
|
||||
For information on how the controls and layouts above behave, see the next chapters.
|
||||
|
|
@ -1,24 +1,18 @@
|
|||
---
|
||||
title: 4. Layouts
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 4. Layouts
|
||||
:notitle:
|
||||
|
||||
== Layouts
|
||||
## Layouts
|
||||
|
||||
Layouts manage the position of UI elements on the screen, and how the
|
||||
elements react to size changes (e.g. window resize).
|
||||
|
||||
=== Size hints
|
||||
### Size hints
|
||||
|
||||
Size hints let the layouting code know how big UI elements are, and to
|
||||
which size they may shrink or grow.
|
||||
|
||||
Size hints can be explicit or implicit. Explicit sizes are set by the
|
||||
user via an API call through `setExplicitSizeHint()` ("This element is
|
||||
of that size''), while implicit sizes are deduced from the elements
|
||||
of that size"), while implicit sizes are deduced from the elements
|
||||
themselves. Explicit size hints always take precedence over implicit
|
||||
ones.
|
||||
|
||||
|
|
@ -26,8 +20,9 @@ For instance, the implicit size of a button is calculated from the
|
|||
text width (which itself depends on the font) and possibly padding and
|
||||
margins:
|
||||
|
||||
.implicit horizontal size hint of a button
|
||||
image::/doc/tutorials/images/size-hints-calculation.png[implicit horizontal size hint of a button]
|
||||
**implicit horizontal size hint of a button**
|
||||
|
||||

|
||||
|
||||
The implicit width of a composited UI element containing a
|
||||
graphic on the left and a text on the right would be the sum of the elements’
|
||||
|
|
@ -40,14 +35,14 @@ its children. For instace a horizontal layout containing three buttons
|
|||
next to each other will calculate its implicit width by summing up the
|
||||
widths of the buttons (spacing and margins again come on top).
|
||||
|
||||
There are three types of size hints: *Minimum*, *Preferred* and
|
||||
*Maximum*.
|
||||
There are three types of size hints: **Minimum**, **Preferred** and
|
||||
**Maximum**.
|
||||
|
||||
* The *minimum size hint* of a UI element is used by layouting code to
|
||||
* The **minimum size hint** of a UI element is used by layouting code to
|
||||
determine how small an element can be.
|
||||
* The *preferred size hint* is the natural size of an element, and will
|
||||
* The **preferred size hint** is the natural size of an element, and will
|
||||
be used in an ideal case, meaning there is enough space available.
|
||||
* The *maximum size hint* is used by layouting code to determine how big
|
||||
* The **maximum size hint** is used by layouting code to determine how big
|
||||
an element can be.
|
||||
|
||||
Minimum and maximum size hints of atomic controls like `QskPushButton`
|
||||
|
|
@ -59,7 +54,7 @@ Minimum and maximum sizes, i.e. the methods `minimumSize()` and
|
|||
So in total, a control can have up to 6 size hints: the three types
|
||||
described above, and each one can have an implicit and an explicit hint.
|
||||
|
||||
==== Example
|
||||
#### Example
|
||||
|
||||
Below is an image with an implicit size hint with a width of 91 pixels
|
||||
and a height of 39 pixels (91x39). The hint is determined by the size of
|
||||
|
|
@ -67,25 +62,26 @@ the text (71x19 pixels) plus margins (10 pixels each for top, right,
|
|||
bottom, left). We don’t need to set a size hint explicitly, the control
|
||||
will be rendered correctly with the implicit size hint:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* label1 = new QskTextLabel( "control 1" );
|
||||
label1->setMargins( 10 );
|
||||
label1->setBackgroundColor( Qt::magenta );
|
||||
....
|
||||
```
|
||||
|
||||
.control without explicit size hint
|
||||
image::/doc/tutorials/images/size-hints-1.png[Image without explicit size hint]
|
||||
**control without explicit size hint**
|
||||
|
||||

|
||||
|
||||
If we set an explicit size hint of 150x60 pixels ourselves for the
|
||||
preferred size, the control will be rendered differently:
|
||||
|
||||
....
|
||||
```
|
||||
label1->setExplicitSizeHint( Qt::PreferredSize, { 150, 60 } );
|
||||
....
|
||||
```
|
||||
|
||||
.control with explicit size hint
|
||||
image::/doc/tutorials/images/size-hints-2.png[Image with explicit size hint]
|
||||
**control with explicit size hint**
|
||||
|
||||

|
||||
|
||||
When dealing with standard controls or layouts, the size hints don’t
|
||||
need to be specified explicitly, as it can be deduced from its standard
|
||||
|
|
@ -94,7 +90,7 @@ values, as seen in the example above.
|
|||
The actual size of a UI element also depends on its size policy, see the
|
||||
next topic.
|
||||
|
||||
=== Size policies
|
||||
### Size policies
|
||||
|
||||
Size policies define the way UI elements can change their size depending
|
||||
on the available space. Imagine a UI with a top bar and a main content
|
||||
|
|
@ -109,66 +105,37 @@ The size policies of QSkinny correspond to the
|
|||
*https://doc.qt.io/qt-5/qsizepolicy.html#Policy-enum[size policies from
|
||||
QtWidgets]*:
|
||||
|
||||
[width="100%",cols="50%,50%",options="header",]
|
||||
|=======================================================================
|
||||
|`QskSizePolicy::Policy` |description
|
||||
|`Fixed` |The control has a fixed size and can neither grow nor shrink.
|
||||
|
||||
|`Minimum` |The control cannot shrink beyond its minimum size, but it
|
||||
can grow if needed.
|
||||
|
||||
|`Maximum` |The control cannot grow beyond its maximum size, but it can
|
||||
shrink if needed.
|
||||
|
||||
|`Preferred` |The control can grow and shrink, but it should be of the
|
||||
size given by `sizeHint()`.
|
||||
|
||||
|`MinimumExpanding` |The control cannot shrink beyond its minimum size,
|
||||
but it can grow and should get as much space as possible.
|
||||
|
||||
|`Expanding` |The control can shrink and grow, and it should get as much
|
||||
space as possible.
|
||||
|
||||
|`Ignored` |The `sizeHint()` is ignored, and the control will get as
|
||||
much space as possible.
|
||||
|
||||
|`Constrained` |The size of the control depends on a constraint,
|
||||
i.e. the width is depending on the height or vice versa. For this policy
|
||||
and the other `Constrained*` ones below, `QskControl::widthForHeight()`
|
||||
or `QskControl::heightForWidth()` will be queried.
|
||||
|
||||
|`ConstrainedMinimum` |The size of the control depends on a constraint,
|
||||
but it can grow if needed.
|
||||
|
||||
|`ConstrainedMaximum` |The size of the control depends on a constraint,
|
||||
but it can shrink if needed.
|
||||
|
||||
|`ConstrainedPreferred` |The size of the control depends on a
|
||||
constraint, but it can grow and srhink if needed.
|
||||
|
||||
|`ConstrainedMinimumExpanding` |The size of the control depends on a
|
||||
constraint, but it can grow and should get as much space as possible.
|
||||
|
||||
|`ConstrainedExpanding` |The size of the control depends on a
|
||||
constraint, and it should get as much space as possible.
|
||||
|=======================================================================
|
||||
|`QskSizePolicy::Policy` | description |
|
||||
| ---------------------- | ----------- |
|
||||
|`Fixed` |The control has a fixed size and can neither grow nor shrink. |
|
||||
|`Minimum` |The control cannot shrink beyond its minimum size, but it can grow if needed. |
|
||||
|`Maximum` |The control cannot grow beyond its maximum size, but it can shrink if needed. |
|
||||
|`Preferred` |The control can grow and shrink, but it should be of the size given by `sizeHint()`. |
|
||||
|`MinimumExpanding` |The control cannot shrink beyond its minimum size, but it can grow and should get as much space as possible. |
|
||||
|`Expanding` |The control can shrink and grow, and it should get as much space as possible. |
|
||||
|`Ignored` |The `sizeHint()` is ignored, and the control will get as much space as possible. |
|
||||
|`Constrained` |The size of the control depends on a constraint, i.e. the width is depending on the height or vice versa. For this policy and the other `Constrained*` ones below, `QskControl::widthForHeight()` or `QskControl::heightForWidth()` will be queried. |
|
||||
|`ConstrainedMinimum` |The size of the control depends on a constraint, but it can grow if needed. |
|
||||
|`ConstrainedMaximum` |The size of the control depends on a constraint, but it can shrink if needed. |
|
||||
|`ConstrainedPreferred` |The size of the control depends on a constraint, but it can grow and srhink if needed. |
|
||||
|`ConstrainedMinimumExpanding` |The size of the control depends on a constraint, but it can grow and should get as much space as possible. |
|
||||
|`ConstrainedExpanding` |The size of the control depends on a constraint, and it should get as much space as possible. |
|
||||
|
||||
All the `Constrained*` policies correspond to Qt’s
|
||||
https://doc.qt.io/qt-5/qsizepolicy.html#hasHeightForWidth[QSizePolicy::hasHeightForWidth()]
|
||||
[QSizePolicy::hasHeightForWidth()](https://doc.qt.io/qt-5/qsizepolicy.html#hasHeightForWidth)
|
||||
or
|
||||
https://doc.qt.io/qt-5/qsizepolicy.html#hasWidthForHeight[QSizePolicy::hasWidthForHeight()]
|
||||
[QSizePolicy::hasWidthForHeight()](https://doc.qt.io/qt-5/qsizepolicy.html#hasWidthForHeight)
|
||||
flag. E.g. if a control has a horizontal size policy of `Constrained`
|
||||
and a vertical size policy of `Fixed`, it will call `widthForHeight()`
|
||||
to determine the width that corresponds to the height.
|
||||
|
||||
==== Example
|
||||
#### Example
|
||||
|
||||
Below is an example of two buttons with different size policies. In this
|
||||
case only the horizontal size policies are considered; the vertical size
|
||||
policies behave correspondingly.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto horizontalBox = new QskLinearBox( Qt::Horizontal );
|
||||
|
||||
auto* label1 = new QskTextLabel( "size policy: fixed" );
|
||||
|
|
@ -179,53 +146,56 @@ auto* label2 = new QskTextLabel( "size policy: minimum" );
|
|||
label2->setSizePolicy( Qt::Horizontal, QskSizePolicy::Minimum );
|
||||
horizontalBox->addItem( label2 );
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
By default the width of the buttons is determined by its text plus its
|
||||
margins:
|
||||
|
||||
.Size policies with preferred size
|
||||
image::/doc/tutorials/images/size-policies-horizontal-minimum-1.png[Fixed vs. Minimum size policy]
|
||||
**Size policies with preferred size**
|
||||
|
||||

|
||||
|
||||
After growing the window horizontally, the button with the Fixed
|
||||
horizontal size policy keeps its width, while the button with the
|
||||
Minimum policy will grow:
|
||||
|
||||
.Size policies when increasing window width
|
||||
image::/doc/tutorials/images/size-policies-horizontal-minimum-2.png[Fixed vs. Minimum size policy]
|
||||
**Size policies when increasing window width**
|
||||
|
||||

|
||||
|
||||
When shrinking the window below its original size, both buttons stay
|
||||
with their width: The one on the left because of its `Fixed` size policy,
|
||||
and the one on the right because it won’t shrink below its original size
|
||||
due to the `Minimum` size policy.
|
||||
|
||||
.Size policies when shrinking window width
|
||||
image::/doc/tutorials/images/size-policies-horizontal-minimum-3.png[Fixed vs. Minimum size policy]
|
||||
**Size policies when shrinking window width**
|
||||
|
||||

|
||||
|
||||
If we change the policy of the right button to `Preferred`, it will shrink
|
||||
below its original size (even though the text is too wide now):
|
||||
|
||||
....
|
||||
```
|
||||
label2->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
|
||||
label2->setText( "size policy: preferred" );
|
||||
....
|
||||
```
|
||||
|
||||
.Size policies when changing to preferred size policy
|
||||
image::/doc/tutorials/images/size-policies-horizontal-minimum-4.png[Fixed vs. Minimum size policy]
|
||||
**Size policies when changing to preferred size policy**
|
||||
|
||||
=== Types of layouts
|
||||

|
||||
|
||||
### Types of layouts
|
||||
|
||||
There are different types of layouts that can group UI elements
|
||||
together. Internally, layouts use the `layoutRect()` method to determine
|
||||
the available space to place its children.
|
||||
|
||||
==== Linear layouts (QskLinearBox)
|
||||
#### Linear layouts (QskLinearBox)
|
||||
|
||||
A linear layout can group elements either horizontally or vertically, as
|
||||
in the images below.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto horizontalBox = new QskLinearBox( Qt::Horizontal );
|
||||
|
||||
auto* label1 = new QskTextLabel( "control 1" );
|
||||
|
|
@ -237,13 +207,13 @@ horizontalBox->addItem( label2 );
|
|||
auto* label3 = new QskTextLabel( "control 3" );
|
||||
horizontalBox->addItem( label3 );
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
.Horizontal layout
|
||||
image::/doc/tutorials/images/layout-horizontal.png[Horizontal layout]
|
||||
**Horizontal layout**
|
||||
|
||||
[source]
|
||||
....
|
||||

|
||||
|
||||
```
|
||||
auto verticalBox = new QskLinearBox( Qt::Vertical );
|
||||
|
||||
auto* label1 = new QskTextLabel( "control 1" );
|
||||
|
|
@ -255,18 +225,18 @@ verticalBox->addItem( label2 );
|
|||
auto* label3 = new QskTextLabel( "control 3" );
|
||||
verticalBox->addItem( label3 );
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
.Vertical layout
|
||||
image::/doc/tutorials/images/layout-vertical.png[Vertical layout]
|
||||
**Vertical layout**
|
||||
|
||||
==== Grid layouts (QskGridBox)
|
||||

|
||||
|
||||
#### Grid layouts (QskGridBox)
|
||||
|
||||
Grid layouts are like linear layouts, but 2 dimensional, and support
|
||||
laying out UI controls in a grid, including spanning columns and rows.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* gridBox = new QskGridBox;
|
||||
|
||||
auto* label1 = new QskTextLabel( "control 1" );
|
||||
|
|
@ -289,19 +259,19 @@ gridBox->addItem( label6, 2, 0 );
|
|||
|
||||
auto* label7 = new QskTextLabel( "control 7" );
|
||||
gridBox->addItem( label7, 2, 1, 1, 2 );
|
||||
....
|
||||
```
|
||||
|
||||
.Grid layout
|
||||
image::/doc/tutorials/images/layout-grid.png[Grid layout]
|
||||
**Grid layout**
|
||||
|
||||
==== Stack layouts (QskStackBox)
|
||||

|
||||
|
||||
#### Stack layouts (QskStackBox)
|
||||
|
||||
Stack layouts allow for items to be arranged on top of each other.
|
||||
Usually there is one current (visible) item, while the rest of the items
|
||||
are hidden below the current one:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* stackBox = new QskStackBox;
|
||||
|
||||
auto* label1 = new QskTextLabel( "control 1" );
|
||||
|
|
@ -318,25 +288,27 @@ stackBox->addItem( label3 );
|
|||
|
||||
stackBox->setCurrentIndex( 2 );
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
.Stack layout (symbolized)
|
||||
image::/doc/tutorials/images/layout-stack.png[Stack layout]
|
||||
**Stack layout (symbolized)**
|
||||
|
||||

|
||||
|
||||
In this example, "control 3" is stacked on top of the blue and the
|
||||
cyan control. Controls in a stacked layout can be of different sizes.
|
||||
|
||||
NOTE: The image above is just for illustrating purposes. In practice
|
||||
**NOTE**:
|
||||
The image above is just for illustrating purposes. In practice
|
||||
the topmost control ("control 3" here) is completely covering the ones
|
||||
below it.
|
||||
|
||||
==== QskControl::autoLayoutChildren()
|
||||
#### QskControl::autoLayoutChildren()
|
||||
|
||||
When the `QskControl::autoLayoutChildren()` flag is set, the control will
|
||||
recalculate the geometry of its children whenever the item is updating
|
||||
its layout.
|
||||
|
||||
=== Stretch factors
|
||||
### Stretch factors
|
||||
|
||||
Stretch factors allow layouts to keep a size ratio for their elements.
|
||||
Let’s say a horizontal layout contains two elements, and when filling up
|
||||
|
|
@ -347,8 +319,7 @@ factor of 1 and the second element a factor of 2.
|
|||
Stretch factors are set on the layout rather than on the controls
|
||||
itself:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto horizontalBox = new QskLinearBox( Qt::Horizontal );
|
||||
|
||||
auto* label1 = new QskTextLabel( "stretch factor 1" );
|
||||
|
|
@ -360,45 +331,48 @@ horizontalBox->addItem( label2 );
|
|||
horizontalBox->setStretchFactor( label2, 2 );
|
||||
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
When the layout has all the space it needs (but not more), both elements
|
||||
are rendered with their preferred size:
|
||||
|
||||
.Stretch factors with preferred size
|
||||
image::/doc/tutorials/images/stretch-factors-1.png[Stretch factors preferred size]
|
||||
**Stretch factors with preferred size**
|
||||
|
||||

|
||||
|
||||
When the layout gets more width, the stretch factors come into play:
|
||||
|
||||
.A stretch factor of 1:2
|
||||
image::/doc/tutorials/images/stretch-factors-2.png[Stretch factors increasing width]
|
||||
**A stretch factor of 1:2**
|
||||
|
||||

|
||||
|
||||
No matter how wide the layout is, the aspect ratio of 1:2 will always be
|
||||
kept, meaning that the label on the left will get 33% of the space, and
|
||||
the label on the right 67%:
|
||||
|
||||
.A stretch factor of 1:2 with different widths
|
||||
image::/doc/tutorials/images/stretch-factors-3.png[Stretch factors even more width]
|
||||
**A stretch factor of 1:2 with different widths**
|
||||
|
||||

|
||||
|
||||
Stretch factors in QSkinny are the same as in the Qt Graphics View
|
||||
Framework, see
|
||||
https://doc.qt.io/qt-5/qgraphicslinearlayout.html#stretch-factor-in-qgraphicslinearlayout[Stretch
|
||||
Factor in QGraphicsLinearLayout].
|
||||
|
||||
=== Nesting layouts
|
||||
### Nesting layouts
|
||||
|
||||
In a real-world application it is typical to nest several layouts in
|
||||
each other. The example below depicts a UI with a top bar and menu items
|
||||
on the left:
|
||||
|
||||
.A UI with nested layouts
|
||||
image::/doc/tutorials/images/nesting-layouts.png[Nested layouts]
|
||||
**A UI with nested layouts**
|
||||
|
||||

|
||||
|
||||
The code to produce the above UI could look like this (setting colors
|
||||
etc. omitted for brevity):
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* outerBox = new QskLinearBox( Qt::Vertical );
|
||||
|
||||
auto* topBar = new QskLinearBox( Qt::Horizontal, outerBox );
|
||||
|
|
@ -417,7 +391,7 @@ auto* menuLabel3 = new QskTextLabel( "menu 3", menuBox );
|
|||
|
||||
auto* mainText = new QskTextLabel( "here main area", mainBox );
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
Here we have an outer vertical layout which divides the content into a
|
||||
top bar and a main box. The top bar itself consists of a horizontal
|
||||
|
|
@ -427,10 +401,10 @@ with the menu buttons is again a vertical layout.
|
|||
|
||||
The following diagram makes the layouts visible:
|
||||
|
||||
.The layout structure of the UI
|
||||
image::/doc/tutorials/images/nesting-layouts-architecture.png[Nested layouts architecture]
|
||||
**The layout structure of the UI**
|
||||
|
||||
=== Anchoring in QSkinny
|
||||

|
||||
|
||||
### Anchoring in QSkinny
|
||||
|
||||
TODO
|
||||
|
||||
|
|
@ -1,33 +1,27 @@
|
|||
---
|
||||
title: 5. Skins
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 5. Skins
|
||||
:notitle:
|
||||
|
||||
== Skins, Skin hints and Skinlets
|
||||
## Skins, Skin hints and Skinlets
|
||||
|
||||
Skins, Skin hints and Skinlets allow the user to define how specific
|
||||
controls looke like. Controls are drawn on the screen by the
|
||||
skinlet, and therefore it will read information from both the control
|
||||
itself as well as read the skin hints from the skin:
|
||||
|
||||
.Skinlets query the control and the skin
|
||||
image::/doc/tutorials/images/skins-1.png[Styling controls]
|
||||
**Skinlets query the control and the skin**
|
||||
|
||||

|
||||
|
||||
For instance, a button skinlet will read the margins from the skin and
|
||||
the text to render from the button.
|
||||
|
||||
=== Skins
|
||||
### Skins
|
||||
|
||||
Skins are a way to define a look and feel for a whole set of UI
|
||||
controls, e.g. a night time vs. day time skin, skins for different
|
||||
brands or an Android Material skin. They contain all kinds of properties
|
||||
(i.e. skin hints) like colors, margins, fonts and more.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class MySkin : public QskSkin
|
||||
{
|
||||
|
||||
|
|
@ -37,19 +31,21 @@ public:
|
|||
// here define the skin with skin hints
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
The example below shows different implementations for a push button: One
|
||||
has a traditional desktop skin, the other is a flat button with a skin
|
||||
often found in mobile devices.
|
||||
|
||||
.desktop style button
|
||||
image::/doc/tutorials/images/skinlets-button-1.png[desktop style button]
|
||||
**desktop style button**
|
||||
|
||||
.flat button
|
||||
image::/doc/tutorials/images/skinlets-button-2.png[flat button]
|
||||

|
||||
|
||||
=== Skin hints
|
||||
**flat button**
|
||||
|
||||

|
||||
|
||||
### Skin hints
|
||||
|
||||
Each instance of a button will have unique properties like its text or
|
||||
icon file name, but all buttons will have common properties like the
|
||||
|
|
@ -69,8 +65,7 @@ Extending the `MySkin` example from above, here is an example of some
|
|||
skin hints for a push button, setting the padding to 10 pixels, the
|
||||
background color to magenta and the text color to black:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class MySkin : public QskSkin
|
||||
{
|
||||
|
||||
|
|
@ -82,10 +77,11 @@ public:
|
|||
setColor( QskPushButton::Text, Qt::black );
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
.A button styled with skin hints
|
||||
image::/doc/tutorials/images/skin-hints.png[Button with skin hints]
|
||||
**A button styled with skin hints**
|
||||
|
||||

|
||||
|
||||
When writing a new skin, a developer needs to know which hints to set
|
||||
for which control. This usually depends on the control itself; however,
|
||||
|
|
@ -93,31 +89,30 @@ since usually controls are broken down into the three primitives box,
|
|||
text and graphic, the methods for rendering each of them will take the
|
||||
following skin hints into account:
|
||||
|
||||
[cols=",",options="header",]
|
||||
|=======================================================================
|
||||
|Primitive |Skin hint from QskAspect
|
||||
|Text |`Alignment` +
|
||||
`Color` +
|
||||
`TextColor` +
|
||||
`StyleColor` +
|
||||
`LinkColor` +
|
||||
`Style` +
|
||||
|Text |`Alignment`\
|
||||
`Color`\
|
||||
`TextColor`\
|
||||
`StyleColor`\
|
||||
`LinkColor`\
|
||||
`Style`\
|
||||
`FontRole`
|
||||
|
||||
|Graphic |`Alignment` +
|
||||
|Graphic |`Alignment`\
|
||||
`GraphicRole`
|
||||
|
||||
|Box | `Margin` +
|
||||
`Metric` \| `Border` +
|
||||
`Color` \| `Border` +
|
||||
`Color` +
|
||||
|Box | `Margin`\
|
||||
`Metric` \| `Border`\
|
||||
`Color` \| `Border`\
|
||||
`Color`\
|
||||
`Metric` \| `Shape`
|
||||
|=======================================================================
|
||||
|
||||
Some special cases exist where elements other than the primitives above
|
||||
are used.
|
||||
|
||||
==== States and animations
|
||||
#### States and animations
|
||||
|
||||
Skin hints can also depend on the state a control is in: Buttons for
|
||||
instance can be in a `Pressed` or `Hovered` state. For such cases, skin
|
||||
|
|
@ -135,8 +130,7 @@ button, there will be a smooth animation from magenta to cyan
|
|||
interpolating between the colors. Without the `setAnimation()` call, the
|
||||
button would just switch to magenta when hovered right away.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class MySkin : public QskSkin
|
||||
{
|
||||
|
||||
|
|
@ -151,39 +145,41 @@ public:
|
|||
setAnimation( QskPushButton::Panel | QskAspect::Color, 1000 );
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
.button in normal state
|
||||
image::/doc/tutorials/images/skin-hints-states-1.png[button in normal state]
|
||||
**button in normal state**
|
||||
|
||||
.button in hovered state
|
||||
image::/doc/tutorials/images/skin-hints-states-2.png[button in hovered state]
|
||||

|
||||
|
||||
==== Local skin hints
|
||||
**button in hovered state**
|
||||
|
||||

|
||||
|
||||
#### Local skin hints
|
||||
|
||||
It is possible to set local skin hints on specific controls to override
|
||||
skin-wide settings:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* label1 = new QskTextLabel( "control 1" );
|
||||
label1->setMargins( 20 );
|
||||
label1->setBackgroundColor( Qt::blue );
|
||||
....
|
||||
```
|
||||
|
||||
In general it is recommended to set the skin hints in the skin rather
|
||||
than on the control locally, in order to separate the style from the
|
||||
implementation, and to allow switching between skins. How to write
|
||||
controls that are themable is explained in the section about
|
||||
link:Writing-own-controls.html[writing own controls].
|
||||
[writing own controls](Writing-own-controls.html).
|
||||
|
||||
Taking animations and local skin hints into account, the architecture
|
||||
diagram now looks like this:
|
||||
|
||||
.Skinlets can also read from local skinlets and animators
|
||||
image::/doc/tutorials/images/skins-2.png[Animators and local skin hints]
|
||||
**Skinlets can also read from local skinlets and animators**
|
||||
|
||||
=== Skinlets
|
||||

|
||||
|
||||
### Skinlets
|
||||
|
||||
A skinlet is in charge of drawing a control on the screen, similar to a
|
||||
Delegate in QML. It will read all the hints it needs from either the
|
||||
|
|
@ -204,19 +200,20 @@ QSkinny already contains implementations of many common controls like
|
|||
text labels, buttons and so on. However, some custom controls might
|
||||
need to be written from scratch, including the skinlet; for an
|
||||
explanation on how to do this, see the example of
|
||||
link:Writing-own-controls.html[writing own controls].
|
||||
[writing own controls](Writing-own-controls.html).
|
||||
|
||||
For a closer look at how the skinlet draws the controls in the scene
|
||||
graph, see link:scene-graph.html[scene graph representations of controls].
|
||||
graph, see [scene graph representations of controls](scene-graph.html).
|
||||
|
||||
Of course each app has different controls and therefore there are also
|
||||
different skinlets, so a more complete version of the architecture
|
||||
diagram looks like this:
|
||||
|
||||
.There is one skinlet for each atomic control
|
||||
image::/doc/tutorials/images/skins-3.png[Animators and local skin hints]
|
||||
**There is one skinlet for each atomic control**
|
||||
|
||||
=== Skin factories and switching between skins
|
||||

|
||||
|
||||
### Skin factories and switching between skins
|
||||
|
||||
Skins are usually not created by the user directly, but by a skin
|
||||
factory. Such a factory keeps track of the skins registered in the
|
||||
|
|
@ -226,8 +223,7 @@ during application lifetime.
|
|||
When having two skins called `MySkin` and `OtherSkin` in an app, the
|
||||
corresponding skin factory might look like this:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class MySkinFactory : public QskSkinFactory
|
||||
{
|
||||
|
||||
|
|
@ -250,13 +246,12 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
That skin factory has to be registered during app start; it is also a
|
||||
good idea to set a default skin right away:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
auto* skinFactory = new MySkinFactory;
|
||||
|
|
@ -272,14 +267,13 @@ int main( int argc, char* argv[] )
|
|||
|
||||
return app.exec();
|
||||
}
|
||||
....
|
||||
```
|
||||
|
||||
Now we can define the `OtherSkin` and define different skin hints for
|
||||
e.g. push buttons. Here we define the background color and padding to be
|
||||
different; also we configure buttons to have a blue border:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class OtherSkin : public QskSkin
|
||||
{
|
||||
|
||||
|
|
@ -292,14 +286,15 @@ public:
|
|||
setBoxBorderMetrics( QskPushButton::Panel, 1 );
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
Switching between skins will change the look of `QskPushButton`
|
||||
instances:
|
||||
|
||||
.button in `MySkin` (as above)
|
||||
image::/doc/tutorials/images/skin-hints-states-1.png[button in normal state]
|
||||
**button in `MySkin` (as above)**
|
||||
|
||||
.button in `OtherSkin`
|
||||
image::/doc/tutorials/images/skin-factory.png[Styling controls]
|
||||

|
||||
|
||||
**button in `OtherSkin`**
|
||||
|
||||

|
||||
|
|
@ -1,12 +1,6 @@
|
|||
---
|
||||
title: 6. (Scalable) graphics
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 6. (Scalable) graphics
|
||||
:notitle:
|
||||
|
||||
== (Scalable) graphics
|
||||
## (Scalable) graphics
|
||||
|
||||
QSkinny offers support for scalable graphics, i.e. rendering SVGs that
|
||||
adapt to a specific size. This means that when a graphic is embedded in
|
||||
|
|
@ -15,8 +9,7 @@ shrinking, while still maintaining a correct aspect ratio.
|
|||
|
||||
Imagine the following code, which produces the image depicted below:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto horizontalBox = new QskLinearBox( Qt::Horizontal );
|
||||
horizontalBox->setPreferredSize( { 200, 75 } );
|
||||
|
||||
|
|
@ -30,19 +23,22 @@ QskGraphic graphic2 = QskGraphic::fromImage( image2 );
|
|||
auto* label2 = new QskGraphicLabel( graphic2, horizontalBox );
|
||||
label2->setSizePolicy( QskSizePolicy::ConstrainedPreferred, QskSizePolicy::Expanding );
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
.graphics with preferred size
|
||||
image::/doc/tutorials/images/scalable-graphics-1.png[Scalable graphics default]
|
||||
**graphics with preferred size**
|
||||
|
||||

|
||||
|
||||
When resizing the window, the graphics will scale according to the size
|
||||
available in the layout:
|
||||
|
||||
.graphics bounded by width
|
||||
image::/doc/tutorials/images/scalable-graphics-2.png[Scalable graphics bounded by width]
|
||||
**graphics bounded by width**
|
||||
|
||||
.graphics bounded by height
|
||||
image::/doc/tutorials/images/scalable-graphics-3.png[Scalable graphics bounded by height]
|
||||

|
||||
|
||||
**graphics bounded by height**
|
||||
|
||||

|
||||
|
||||
Since we set the horizontal size policy of the graphics to
|
||||
`ConstrainedPreferred`, the scaling is done through QskGraphic’s
|
||||
|
|
@ -53,10 +49,8 @@ one to e.g. `Expanding`, the layout would have queried the
|
|||
|
||||
Of course non-scalable graphics like PNGs and JPGs are also supported:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
QImage image( "background.jpg" );
|
||||
QskGraphic graphic = QskGraphic::fromImage( image );
|
||||
...
|
||||
....
|
||||
|
||||
```
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
---
|
||||
title: 7. Parents and parent items
|
||||
layout: docs
|
||||
---
|
||||
|
||||
:doctitle: 7. Parents and parent items
|
||||
:notitle:
|
||||
|
||||
== Parents and parent items
|
||||
|
||||
Creating an app with QSkinny consists of creating controls, putting them
|
||||
into layouts and nesting layouts and controls inside each other. The
|
||||
nesting already creates some sort of a hierarchy in the app, see the
|
||||
"Nesting layouts" section in the link:Layouts.html[layouts page]. In
|
||||
more general terms, all controls are part of several hierarchies:
|
||||
|
||||
* The *object tree*. This is a tree of `QObject` instances which manages
|
||||
lifetime: Objects created with a parent will get deleted whenever their
|
||||
parent is deleted. For more information, see the Qt documentation on
|
||||
https://doc.qt.io/qt-5/objecttrees.html[Object Trees & Ownership].
|
||||
* The *item tree*. This is a tree of items displayed on the screen,
|
||||
i.e. `QQuickItem` instances. Qt will traverse the item tree when
|
||||
rendering items on the screen. The positioning of an item depends on its
|
||||
parent item, e.g. layouts will position their child items according to
|
||||
certain policies. In addition, visual items will inherit properties from
|
||||
its parent item like visibility or opacity. The item tree is often
|
||||
similar to the object tree, but not necessarily: Instances of
|
||||
`QQuickItem` can have a parent item set, but have another parent, or no
|
||||
parent at all. See also the Qt documentation on
|
||||
https://doc.qt.io/qt-5/qtquick-visualcanvas-visualparent.html[Concepts -
|
||||
Visual Parent in Qt Quick].
|
||||
* The *scene graph*. The scene graph contains a representation of
|
||||
graphic primitives like rectangles, textures (i.e. images) and text, to
|
||||
allow efficient rendering on the screen with OpenGL or other backends.
|
||||
This is described in more details in link:scene-graph.html[scene graph
|
||||
representations of controls].
|
||||
|
||||
=== Example
|
||||
|
||||
Let’s look at the "Nesting layouts" example from the
|
||||
link:Layouts.html[layouts documentation]. The UI looks like this:
|
||||
|
||||
.UI with nested layouts
|
||||
image::/doc/tutorials/images/nesting-layouts.png[Nested layouts]
|
||||
|
||||
The code for this UI is below:
|
||||
|
||||
[source]
|
||||
....
|
||||
auto* outerBox = new QskLinearBox( Qt::Vertical );
|
||||
|
||||
auto* topBar = new QskLinearBox( Qt::Horizontal, outerBox );
|
||||
|
||||
auto* topLabel1 = new QskTextLabel( "top bar label 1", topBar );
|
||||
auto* topLabel2 = new QskTextLabel( "top bar label 2", topBar );
|
||||
auto* topLabel3 = new QskTextLabel( "top bar label 3", topBar );
|
||||
|
||||
auto* mainBox = new QskLinearBox( Qt::Horizontal, outerBox );
|
||||
|
||||
auto* menuBox = new QskLinearBox( Qt::Vertical, mainBox );
|
||||
|
||||
auto* menuLabel1 = new QskTextLabel( "menu 1", menuBox );
|
||||
auto* menuLabel2 = new QskTextLabel( "menu 2", menuBox );
|
||||
auto* menuLabel3 = new QskTextLabel( "menu 3", menuBox );
|
||||
|
||||
auto* mainText = new QskTextLabel( "here main area", mainBox );
|
||||
|
||||
QskWindow window;
|
||||
window.addItem( outerBox );
|
||||
window.show();
|
||||
....
|
||||
|
||||
==== Object tree
|
||||
|
||||
In the example above, when we created a new element, we always passed
|
||||
the `QObject` parent as an argument to the constructor, which is good
|
||||
practice. We do that for instance in this line:
|
||||
|
||||
[source]
|
||||
....
|
||||
auto* topLabel1 = new QskTextLabel( "top bar label 1", topBar );
|
||||
....
|
||||
|
||||
This makes sure `topBar` is a parent of `topLabel1`. It means that when
|
||||
`topBar` is deleted, it will automatically delete `topLabel1`, because
|
||||
the latter is a child of the `topBar`.
|
||||
|
||||
Below is an image of the object tree, i.e. the `QObject` parent-child
|
||||
relationship. The `QskWindow` is hereby the parent of the
|
||||
`QQuickRootItem`, which itself is the parent of the `outer box`, and so
|
||||
on. For information on how to obtain this tree, see
|
||||
https://doc.qt.io/qt-5/qobject.html#dumpObjectTree[QObject::dumpObjectTree()].
|
||||
|
||||
.QObject tree (and item tree) of the nested layouts UI
|
||||
image::/doc/tutorials/images/object-hierarchy.png[QObject hierarchy]
|
||||
|
||||
==== Item tree
|
||||
|
||||
The Item tree for the example above is identical to the object tree. As
|
||||
described, we always pass the parent object in the constructor:
|
||||
|
||||
[source]
|
||||
....
|
||||
auto* topLabel1 = new QskTextLabel( "top bar label 1", topBar );
|
||||
....
|
||||
|
||||
The line above will (in addition to the setting the parent) also ensure
|
||||
that `topBar` will be a *parent item* of `topLabel1`; this is done by
|
||||
the `QQuickItem` constructor.
|
||||
|
||||
Even if we had not passed the parent in the constructor, we could still
|
||||
add the label to the `topBar` via an explicit call:
|
||||
|
||||
[source]
|
||||
....
|
||||
auto* topLabel1 = new QskTextLabel( "top bar label 1" );
|
||||
topBar->addItem( topLabel1 );
|
||||
....
|
||||
|
||||
The call to `addItem()` above sets the parent item of `topLabel1` to
|
||||
`topBar` and thus the latter will display it as one of its children. In
|
||||
this case it would also set the parent, because the `topLabel1` does not
|
||||
have one yet. In other words, setting a parent item will also set the
|
||||
parent *if* the parent is null.
|
||||
|
||||
So since the `topBar` is a parent item of `topLabel1`, it means that
|
||||
`topLabel1` will inherit settings like visibility and opacity from
|
||||
`topBar`. For instance, if we set the the visibility of the `topBar` to
|
||||
false, all its child items will be invisible as well (which in this case
|
||||
would be all top bar labels). If we set the opacity to 0.2, all its
|
||||
child items will be almost transparent:
|
||||
|
||||
[source]
|
||||
....
|
||||
topBar->setOpacity( 0.2 );
|
||||
....
|
||||
|
||||
.Changing opacity of an item will affect all its child items
|
||||
image::/doc/tutorials/images/nesting-layouts-item-tree-1.png[Changing the item tree]
|
||||
|
||||
==== Difference in object trees and item trees
|
||||
|
||||
As an example for when the object tree and item tree differ, let’s
|
||||
decide to add a bottom bar to our UI and move our `topLabel1` from the
|
||||
top bar to the bottom bar. This is easy:
|
||||
|
||||
[source]
|
||||
....
|
||||
auto* bottomBar = new QskLinearBox( Qt::Horizontal, outerBox );
|
||||
topLabel1->setParentItem( bottomBar );
|
||||
....
|
||||
|
||||
.Moving a label from the top bar to the bottom bar
|
||||
image::/doc/tutorials/images/nesting-layouts-item-tree-2.png[Moving a label to the bottom bar]
|
||||
|
||||
Now we decide to get rid of our top bar altogether:
|
||||
|
||||
[source]
|
||||
....
|
||||
topBar->deleteLater();
|
||||
....
|
||||
|
||||
This will also delete our label from the bottom bar:
|
||||
|
||||
.Deleting the top bar will delete all its children
|
||||
image::/doc/tutorials/images/nesting-layouts-item-tree-3.png[Deleting the top bar]
|
||||
|
||||
The reason why the label from the bottom bar was also deleted is that
|
||||
with the call to `setParentItem()` above we set a new parent item; the
|
||||
parent, however, was still `topBar` (the call to `setParentItem()` did
|
||||
not change the parent, because it was not null). So when the `topBar`
|
||||
was deleted, it deleted all of its children, including the moved label
|
||||
`topLabel1`.
|
||||
|
||||
After we moved the label to the bottom bar, the object tree was
|
||||
different from the item tree, hence we got a surprising result when
|
||||
deleting the top bar. It is a good idea to try to keep the trees the
|
||||
same, and be aware of the existence of both of them.
|
||||
|
||||
If we reparent our label to the bottom bar before deleting the top bar,
|
||||
we get the desired effect:
|
||||
|
||||
[source]
|
||||
....
|
||||
topLabel1->setParent( bottomBar );
|
||||
topLabel1->setParentItem( bottomBar );
|
||||
topBar->deleteLater();
|
||||
....
|
||||
|
||||
.Reparenting the label will keep it alive when deleting the top bar
|
||||
image::/doc/tutorials/images/nesting-layouts-item-tree-4.png[Reparenting the item]
|
||||
|
|
@ -1,12 +1,6 @@
|
|||
---
|
||||
title: 8. Using QSkinny and QML
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 8. Using QSkinny and QML
|
||||
:notitle:
|
||||
|
||||
== QSkinny - Using QSkinny and QML
|
||||
## QSkinny - Using QSkinny and QML
|
||||
|
||||
Combining QSkinny and QML is possible: Since both QML elements and
|
||||
QSkinny controls derive from `QQuickItem`, they can be combined and
|
||||
|
|
@ -18,31 +12,29 @@ When using a QSkinny control, all the methods exposed as either properties,
|
|||
slots or invokables can be used in QML. For example, the QSkinny control
|
||||
`QskLinearBox` defines the following properties:
|
||||
|
||||
.CMakeLists.txt
|
||||
[source,cmake]
|
||||
....
|
||||
**CMakeLists.txt**
|
||||
|
||||
```cmake
|
||||
target_link_libraries(myapp PRIVATE
|
||||
...
|
||||
Qsk::QmlExport)
|
||||
...
|
||||
....
|
||||
```
|
||||
|
||||
[source,cpp]
|
||||
....
|
||||
```cpp
|
||||
class QSK_EXPORT QskLinearBox : public QskIndexedLayoutBox
|
||||
{
|
||||
Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL )
|
||||
Q_PROPERTY( qreal spacing READ spacing WRITE setSpacing RESET resetSpacing NOTIFY spacingChanged FINAL )
|
||||
...
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
The `QskLinearBox` class is registered to QML as `Qsk.LinearBox` via
|
||||
Qt’s `qmlRegisterType`, so the exposed properties `orientation` and
|
||||
`spacing` can be used like this:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
Qsk.LinearBox
|
||||
{
|
||||
orientation: Qt.Horizontal
|
||||
|
|
@ -51,12 +43,13 @@ Qsk.LinearBox
|
|||
// here define elements inside the box
|
||||
...
|
||||
}
|
||||
....
|
||||
```
|
||||
|
||||
The full Buttons example is depicted below.
|
||||
|
||||
.The buttons example shows how to mix QSkinny and QML
|
||||
image::/doc/tutorials/images/buttons-example.png[Buttons example]
|
||||
**The buttons example shows how to mix QSkinny and QML**
|
||||
|
||||

|
||||
|
||||
For more information on using C++ classes from QML, see the article about exposing attributes of {cpp} types to QML in the
|
||||
https://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html[Qt documentation].
|
||||
[Qt documentation](https://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html).
|
||||
|
|
@ -1,26 +1,19 @@
|
|||
---
|
||||
title: 9. Writing own controls
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 9. Writing own controls
|
||||
:notitle:
|
||||
|
||||
== Writing own controls
|
||||
## Writing own controls
|
||||
|
||||
Writing own controls is either done by subclassing or compositing an
|
||||
existing displayable control like `QskTextLabel`, or by writing a
|
||||
completely new class including a skinlet, which is typically derived
|
||||
directly from `QskControl`.
|
||||
|
||||
=== Subclassing existing controls
|
||||
### Subclassing existing controls
|
||||
|
||||
Let’s say an app is displaying a text label with a specific style at
|
||||
several different places, then it makes sense to subclass `QskTextLabel`
|
||||
and set the needed properties like font size etc. in the derived class:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class TextLabel : public QskTextLabel
|
||||
{
|
||||
|
||||
|
|
@ -33,15 +26,16 @@ public:
|
|||
setBackgroundColor( Qt::cyan );
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
.A subclassed control with local skin hints
|
||||
image::/doc/tutorials/images/subclassing-existing-controls.png[Subclassing existing controls]
|
||||
**A subclassed control with local skin hints**
|
||||
|
||||

|
||||
|
||||
Then there is no need to set the margins and background color for every
|
||||
instance of the custom text label.
|
||||
|
||||
=== Making custom classes skinnable
|
||||
### Making custom classes skinnable
|
||||
|
||||
To make custom classes like the `TextLabel` class above skinnable, we
|
||||
need to define our own subcontrols and style them in our skin, in
|
||||
|
|
@ -51,8 +45,7 @@ generic `QskTextLabel`, we need to define our own subcontrols and
|
|||
substitute the generic subcontrols for them in an overriden method
|
||||
`effectiveSubcontrol()`:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class TextLabel : public QskTextLabel
|
||||
{
|
||||
QSK_SUBCONTROLS( Panel )
|
||||
|
|
@ -70,7 +63,7 @@ class TextLabel : public QskTextLabel
|
|||
}
|
||||
...
|
||||
}
|
||||
....
|
||||
```
|
||||
|
||||
When the skinlet is drawing a `TextLabel` instance, it queries it for
|
||||
its subcontrols through `effectiveSubcontrol()` in order to style them
|
||||
|
|
@ -79,8 +72,7 @@ properly. Now that we substitute the `QskTextLabel::Panel` for our
|
|||
need to set the local skin hints in the constructor of `TextLabel`
|
||||
anymore.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class MySkin : public QskSkin
|
||||
{
|
||||
|
||||
|
|
@ -91,10 +83,11 @@ public:
|
|||
setMargins( TextLabel::Panel | QskAspect::Padding, 15 );
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
.A subclassed control with skin hints defined in the skin
|
||||
image::/doc/tutorials/images/subclassing-existing-controls.png[Subclassing existing controls]
|
||||
**A subclassed control with skin hints defined in the skin**
|
||||
|
||||

|
||||
|
||||
The styling described above has the same effect as in the simpler
|
||||
example, but now the `TextLabel` control can be given a different style
|
||||
|
|
@ -104,14 +97,13 @@ In our class we only set a custom skin hint for the panel, but as
|
|||
`QskTextLabel` also has a `Text` subcontrol, we could of course also
|
||||
define our own one for the text.
|
||||
|
||||
=== Compositing controls
|
||||
### Compositing controls
|
||||
|
||||
Controls can also be composited; e.g. when writing a class with a text
|
||||
label on the left and a graphic on the right side, it could look like
|
||||
this:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class TextAndGraphic : public QskLinearBox
|
||||
{
|
||||
|
||||
|
|
@ -139,34 +131,33 @@ private:
|
|||
QskTextLabel* m_textLabel;
|
||||
QskGraphicLabel* m_graphicLabel;
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
This allows for easy instantiation of the class with a text and a file
|
||||
name for the graphic:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* textAndGraphic = new TextAndGraphic( "Text", "cloud" );
|
||||
....
|
||||
```
|
||||
|
||||
.A composited control
|
||||
image::/doc/tutorials/images/compositing-controls.png[Compositing controls]
|
||||
**A composited control**
|
||||
|
||||
=== Writing controls with a skinlet
|
||||

|
||||
|
||||
### Writing controls with a skinlet
|
||||
|
||||
QSkinny already comes with controls like text labels, list views,
|
||||
buttons etc. When there is a completely new control to be written that
|
||||
cannot be subclassed or composited, the skinlet for the class needs to
|
||||
be implemented as well.
|
||||
|
||||
==== Writing the class
|
||||
#### Writing the class
|
||||
|
||||
For demo purposes we create a class called `CustomShape` which shall
|
||||
display an outer circle and an inner circle, with minimal API. There are
|
||||
only 2 subcontrols that will be painted in the skinlet later:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class CustomShape : public QskControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -178,9 +169,9 @@ public:
|
|||
{
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
==== Writing the skinlet
|
||||
#### Writing the skinlet
|
||||
|
||||
Writing the skinlet is the hard part of the work. We need the following
|
||||
things in our skinlet:
|
||||
|
|
@ -191,10 +182,10 @@ from the control, so since in our case we have a subcontrol `Panel` and
|
|||
`InnerShapeRole`. The node roles are often set in the constructor of the
|
||||
class.
|
||||
|
||||
IMPORTANT: The constructor of the skinlet needs to be invokable!
|
||||
**❗ IMPORTANT**\
|
||||
The constructor of the skinlet needs to be invokable!
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class CustomShapeSkinlet : public QskSkinlet
|
||||
{
|
||||
Q_GADGET
|
||||
|
|
@ -209,7 +200,7 @@ public:
|
|||
{
|
||||
setNodeRoles( { PanelRole, InnerShapeRole } );
|
||||
}
|
||||
....
|
||||
```
|
||||
|
||||
* The enclosing rectangle for each subcontrol. This can be just the
|
||||
`contentsRect`, but we can define it more accurately if we want by
|
||||
|
|
@ -217,8 +208,7 @@ applying some metrics. If the code below is hard to understand, the
|
|||
important thing to take away from it is that different subcontrols can
|
||||
have different enclosing rectangles.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
QRectF subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const override
|
||||
{
|
||||
const auto* customShape = static_cast< const CustomShape* >( skinnable );
|
||||
|
|
@ -234,7 +224,7 @@ have different enclosing rectangles.
|
|||
}
|
||||
|
||||
return QskSkinlet::subControlRect( skinnable, contentsRect, subControl );
|
||||
....
|
||||
```
|
||||
|
||||
* The code to actually draw the nodes. In our case of an outer circle
|
||||
and an inner circle, the code for each subcontrol / node role is quite
|
||||
|
|
@ -243,8 +233,7 @@ similar. The method `updateSubNode()`, which is reimplemented from
|
|||
might not be straight forward to understand, the gist of it is that for
|
||||
each node role we draw a circle by creating a `BoxNode`.
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
protected:
|
||||
QSGNode* updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override
|
||||
{
|
||||
|
|
@ -279,16 +268,15 @@ protected:
|
|||
return QskSkinlet::updateSubNode( skinnable, nodeRole, node );
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
==== Connecting class and skinlet
|
||||
#### Connecting class and skinlet
|
||||
|
||||
In our skin, we need to declare that the skinlet above will be
|
||||
responsible of drawing our control via `declareSkinlet`. Also, we can
|
||||
style our control with skin hints:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
class MySkin : public QskSkin
|
||||
{
|
||||
|
||||
|
|
@ -302,7 +290,7 @@ public:
|
|||
setGradient( CustomShape::InnerShape, Qt::magenta );
|
||||
}
|
||||
};
|
||||
....
|
||||
```
|
||||
|
||||
SkinFactories etc. are again omitted here. Finally we can draw our
|
||||
control; the effort might seem excessive, but we wrote the control with
|
||||
|
|
@ -310,5 +298,6 @@ all capabilities of styling; in addition, the control will react to size
|
|||
changes properly. A simpler version with hardcoded values for margins,
|
||||
colors etc. can be written with less code.
|
||||
|
||||
.A class with an own skinlet
|
||||
image::/doc/tutorials/images/control-with-skinlet.png[Control with skinlet]
|
||||
**A class with an own skinlet**
|
||||
|
||||

|
||||
|
|
@ -1,12 +1,6 @@
|
|||
---
|
||||
title: 9. Scene graph representations of controls
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 9. Scene graph representations of controls
|
||||
:notitle:
|
||||
|
||||
== QSkinny - Scene graph representations of controls
|
||||
## Scene graph representations of controls
|
||||
|
||||
Each control that is displayed on the screen consists of one or more
|
||||
scene graph nodes. Those nodes can be either basic shapes like
|
||||
|
|
@ -15,19 +9,19 @@ with transform nodes), opacity or clipping.
|
|||
|
||||
The source code below shows a minimal example displaying a button:
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* button = new QskPushButton( "button" );
|
||||
|
||||
QskWindow window;
|
||||
window.addItem( button );
|
||||
window.show();
|
||||
....
|
||||
```
|
||||
|
||||
For this example, the scene graph will contain the following nodes:
|
||||
|
||||
.Scene graph representation of a button
|
||||
image::/doc/tutorials/images/skins-sg-1.png[Scene graph nodes for a button]
|
||||
**Scene graph representation of a button**
|
||||
|
||||

|
||||
|
||||
The top two nodes (root and Quick root item) are created for every
|
||||
QtQuick application. The button itself consists of 5 nodes in our case:
|
||||
|
|
@ -38,7 +32,7 @@ another geometry node for displaying the text (`text node`).
|
|||
|
||||
For an explanation of the different scene graph node types, see the Qt
|
||||
documentation of
|
||||
https://doc.qt.io/qt-5/qsgnode.html#NodeType-enum[QSGNode::NodeType].
|
||||
[QSGNode::NodeType](https://doc.qt.io/qt-5/qsgnode.html#NodeType-enum).
|
||||
|
||||
The example above is the simplest form of a button, in practice there
|
||||
might be more nodes per control, for instance an opacity node or a clip
|
||||
|
|
@ -47,20 +41,20 @@ node.
|
|||
Now we add more elements to the UI by putting the button inside a layout
|
||||
(`QskBox`):
|
||||
|
||||
[source]
|
||||
....
|
||||
```
|
||||
auto* box = new QskBox;
|
||||
auto* button = new QskPushButton( "button", box );
|
||||
|
||||
QskWindow window;
|
||||
window.addItem( box );
|
||||
window.show();
|
||||
....
|
||||
```
|
||||
|
||||
Then the scene graph has the following structure:
|
||||
|
||||
.Scene graph representation of a button inside a box
|
||||
image::/doc/tutorials/images/skins-sg-2.png[Scene graph nodes for a button in a box]
|
||||
**Scene graph representation of a button inside a box**
|
||||
|
||||

|
||||
|
||||
Here we can see that since the box is a parent of the button, the `box
|
||||
node` is also a parent of the `button node` in the scene graph. Also, the
|
||||
|
|
@ -1,51 +1,42 @@
|
|||
---
|
||||
title: 10. Building QSkinny for WebAssembly (Wasm)
|
||||
layout: docs
|
||||
---
|
||||
# Tutorials {#tutorials}
|
||||
|
||||
:doctitle: 10. Building QSkinny for WebAssembly (Wasm)
|
||||
:notitle:
|
||||
## Building QSkinny for WebAssembly (Wasm)
|
||||
|
||||
== 10. Building QSkinny for WebAssembly (Wasm)
|
||||
|
||||
|
||||
=== Build Qt for 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"
|
||||
./configure -platform wasm-emscripten -qt-host-path ~/Qt/6.6.0/gcc_64/ -prefix $PWD/qtbase -submodules qtbase,qtdeclarative,qtshadertools,qtsvg
|
||||
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
|
||||
### 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
|
||||
....
|
||||
make
|
||||
```
|
||||
|
||||
|
||||
=== Run QSkinny for Wasm
|
||||
### 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::/doc/tutorials/images/iotdashboard-wasm.png[The IOT dashboard example in a browser]
|
||||
**The IOT dashboard example in a browser**
|
||||
|
||||

|
||||
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: Tutorials
|
||||
excerpt: In this section you'll find the QSkinny tutorials.
|
||||
layout: docs
|
||||
---
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ add_subdirectory(qvgviewer)
|
|||
add_subdirectory(thumbnails)
|
||||
add_subdirectory(tabview)
|
||||
add_subdirectory(iotdashboard)
|
||||
add_subdirectory(apiDocumentationSamples)
|
||||
|
||||
if( BUILD_QML_EXPORT )
|
||||
add_subdirectory(boxes)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
############################################################################
|
||||
# QSkinny - Copyright (C) The authors
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
############################################################################
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
qsk_add_example(apiDocumentationSamples ${SOURCES})
|
||||
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include <SkinnyNamespace.h>
|
||||
|
||||
#include <QskCheckBox.h>
|
||||
#include <QskComboBox.h>
|
||||
#include <QskDrawer.h>
|
||||
#include <QskIcon.h>
|
||||
#include <QskSkinManager.h>
|
||||
#include <QskLabelData.h>
|
||||
#include <QskLinearBox.h>
|
||||
#include <QskMainView.h>
|
||||
#include <QskPageIndicator.h>
|
||||
#include <QskSpinBox.h>
|
||||
#include <QskProgressBar.h>
|
||||
#include <QskProgressRing.h>
|
||||
#include <QskPushButton.h>
|
||||
#include <QskRadioBox.h>
|
||||
#include <QskScrollArea.h>
|
||||
#include <QskSegmentedBar.h>
|
||||
#include <QskSeparator.h>
|
||||
#include <QskSimpleListBox.h>
|
||||
#include <QskSlider.h>
|
||||
#include <QskSwipeView.h>
|
||||
#include <QskSwitchButton.h>
|
||||
#include <QskTabBar.h>
|
||||
#include <QskTabButton.h>
|
||||
#include <QskTabView.h>
|
||||
#include <QskTextField.h>
|
||||
#include <QskTextLabel.h>
|
||||
#include <QskWindow.h>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#ifdef Q_OS_WASM
|
||||
#include <emscripten/val.h>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
class SkinsBar : public QskSegmentedBar
|
||||
{
|
||||
public:
|
||||
SkinsBar() : QskSegmentedBar()
|
||||
{
|
||||
const auto skins = qskSkinManager->skinNames();
|
||||
|
||||
for( int i = 0; i < skins.count(); ++i )
|
||||
{
|
||||
addOption( {}, skins.at( i ) );
|
||||
|
||||
if( skins.at( i ) == qskSkinManager->skinName() )
|
||||
{
|
||||
setCurrentIndex( i );
|
||||
}
|
||||
}
|
||||
|
||||
connect( this, &QskSegmentedBar::currentIndexChanged, this, [ this ]( int i )
|
||||
{
|
||||
if( i >= 0 )
|
||||
{
|
||||
qskSkinManager->setSkin( optionAt( i ).text() );
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
class BrightnessBar : public QskSegmentedBar
|
||||
{
|
||||
public:
|
||||
BrightnessBar() : QskSegmentedBar()
|
||||
{
|
||||
QStringList options = { "bright", "dark" };
|
||||
|
||||
for( auto& option : options )
|
||||
{
|
||||
addOption( {}, option );
|
||||
}
|
||||
|
||||
connect( this, &QskSegmentedBar::currentIndexChanged, this, [ this, options ]( int i )
|
||||
{
|
||||
if( i>= 0 )
|
||||
{
|
||||
auto s = qskSkinManager->skin();
|
||||
const auto scheme = optionAt( currentIndex() ).text() == options.at( 0 )
|
||||
? QskSkin::LightScheme : QskSkin::DarkScheme;
|
||||
s->setColorScheme( scheme );
|
||||
}
|
||||
} );
|
||||
|
||||
connect( qskSkinManager, &QskSkinManager::skinChanged, this, [ this ]()
|
||||
{
|
||||
const auto scheme = ( selectedIndex() == 0 ) ? QskSkin::LightScheme : QskSkin::DarkScheme;
|
||||
auto s = qskSkinManager->skin();
|
||||
s->setColorScheme( scheme );
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
QskControl* createControl( const QByteArray& name )
|
||||
{
|
||||
if( name == "CheckBox" )
|
||||
{
|
||||
return new QskCheckBox( "check box" );
|
||||
}
|
||||
if( name == "ComboBox" )
|
||||
{
|
||||
return new QskComboBox;
|
||||
}
|
||||
if( name == "Drawer" )
|
||||
{
|
||||
auto box = new QskLinearBox;
|
||||
box->setPanel( true );
|
||||
box->setPreferredSize( 200, 200 );
|
||||
auto button = new QskPushButton( "open drawer", box );
|
||||
|
||||
auto d = new QskDrawer( box );
|
||||
d->setEdge( Qt::RightEdge );
|
||||
auto drawerBox = new QskLinearBox( Qt::Vertical, d );
|
||||
drawerBox->setMargins( 20 );
|
||||
|
||||
new QskTextLabel( "drawer element 1 ", drawerBox );
|
||||
new QskTextLabel( "drawer element 2 ", drawerBox );
|
||||
new QskTextLabel( "drawer element 3 ", drawerBox );
|
||||
|
||||
QObject::connect( button, &QskPushButton::clicked, d, &QskDrawer::toggle );
|
||||
|
||||
return box;
|
||||
}
|
||||
if( name == "LinearBox" )
|
||||
{
|
||||
auto b = new QskLinearBox;
|
||||
b->addItem( new QskTextLabel( "item 1" ) );
|
||||
b->addItem( new QskTextLabel( "item 2" ) );
|
||||
b->addItem( new QskTextLabel( "item 3" ) );
|
||||
return b;
|
||||
}
|
||||
if( name == "PageIndicator" )
|
||||
{
|
||||
auto i = new QskPageIndicator( 5 );
|
||||
i->setCurrentIndex( 0 );
|
||||
return i;
|
||||
}
|
||||
if( name == "ProgressBar" )
|
||||
{
|
||||
auto b = new QskProgressBar( 1, 10 );
|
||||
b->setIndeterminate( true );
|
||||
return b;
|
||||
}
|
||||
if( name == "ProgressRing" )
|
||||
{
|
||||
auto r = new QskProgressRing( 1, 10 );
|
||||
r->setIndeterminate( true );
|
||||
return r;
|
||||
}
|
||||
if( name == "PushButton" )
|
||||
{
|
||||
return new QskPushButton( "button" );
|
||||
}
|
||||
if( name == "SpinBox" )
|
||||
{
|
||||
return new QskSpinBox( 1, 10, 2 );
|
||||
}
|
||||
if( name == "RadioBox" )
|
||||
{
|
||||
auto r = new QskRadioBox( { "one", "two", "three" } );
|
||||
r->setSelectedIndex( 0 );
|
||||
return r;
|
||||
}
|
||||
if( name == "ScrollArea" )
|
||||
{
|
||||
return nullptr; // ###
|
||||
}
|
||||
if( name == "SegmentedBar" )
|
||||
{
|
||||
auto b = new QskSegmentedBar;
|
||||
b->addOption( {}, "one" );
|
||||
b->addOption( {}, "two" );
|
||||
b->addOption( {}, "three" );
|
||||
return b;
|
||||
}
|
||||
if( name == "Separator" )
|
||||
{
|
||||
return nullptr; // ###
|
||||
}
|
||||
if( name == "SimpleListBox" )
|
||||
{
|
||||
auto b = new QskSimpleListBox;
|
||||
b->setEntries( { "one", "two", "three", "four" } );
|
||||
b->setSelectedRow( 0 );
|
||||
b->setFixedSize( 200, 150 );
|
||||
return b;
|
||||
}
|
||||
if( name == "Slider" )
|
||||
{
|
||||
auto s = new QskSlider;
|
||||
s->setPreferredWidth( 200 );
|
||||
return s;
|
||||
}
|
||||
if( name == "SpinBox" )
|
||||
{
|
||||
return new QskSpinBox;
|
||||
}
|
||||
if( name == "SwipeView" )
|
||||
{
|
||||
auto s = new QskSwipeView;
|
||||
s->setPreferredSize( 200, 200 );
|
||||
|
||||
auto t1 = new QskTextLabel( "view 1 - swipe to the side" );
|
||||
t1->setAlignment( Qt::AlignCenter );
|
||||
auto t2 = new QskTextLabel( "view 2 - swipe to the side" );
|
||||
t2->setAlignment( Qt::AlignCenter );
|
||||
auto t3 = new QskTextLabel( "view 3 - swipe to the side" );
|
||||
t3->setAlignment( Qt::AlignCenter );
|
||||
|
||||
s->addItem( t1, Qt::AlignCenter );
|
||||
s->addItem( t2, Qt::AlignCenter );
|
||||
s->addItem( t3, Qt::AlignCenter );
|
||||
|
||||
return s;
|
||||
}
|
||||
if( name == "SwitchButton" )
|
||||
{
|
||||
return new QskSwitchButton;
|
||||
}
|
||||
if( name == "TabBar" )
|
||||
{
|
||||
auto b = new QskTabBar;
|
||||
b->setPreferredSize( 300, 100 );
|
||||
b->addTab( "tab 1 " );
|
||||
b->addTab( "tab 2 " );
|
||||
b->addTab( "tab 3 " );
|
||||
return b;
|
||||
}
|
||||
if( name == "TabButton" )
|
||||
{
|
||||
return new QskTabButton( "tab button" );
|
||||
}
|
||||
if( name == "TabView" )
|
||||
{
|
||||
auto t = new QskTabView;
|
||||
t->setPreferredSize( 300, 200 );
|
||||
t->addTab( "tab 1 ", new QskTextLabel( "tab content 1" ) );
|
||||
t->addTab( "tab 2 ", new QskTextLabel( "tab content 2" ) );
|
||||
t->addTab( "tab 3 ", new QskTextLabel( "tab content 3" ) );
|
||||
return t;
|
||||
}
|
||||
if( name == "TextField" )
|
||||
{
|
||||
auto t = new QskTextField;
|
||||
t->setPreferredWidth( 100 );
|
||||
return t;
|
||||
}
|
||||
if( name == "TextLabel" )
|
||||
{
|
||||
return new QskTextLabel( "text label" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
QGuiApplication app( argc, argv );
|
||||
|
||||
QByteArray className;
|
||||
|
||||
#ifdef Q_OS_WASM // Doxygen file name will be classQskSlider.html etc.
|
||||
emscripten::val location = emscripten::val::global( "location" );
|
||||
const auto href = location["href"].as< std::string >();
|
||||
const QUrl url( QString::fromStdString( href ) );
|
||||
static QRegularExpression re( "classQsk(.*)\\.html" );
|
||||
auto match = re.match( url.fileName() );
|
||||
|
||||
if( match.hasMatch() )
|
||||
{
|
||||
className = match.captured( 1 ).toUtf8();
|
||||
}
|
||||
else
|
||||
{
|
||||
qFatal() << "could not deduce class from" << url.fileName();
|
||||
}
|
||||
#else
|
||||
if( argc < 2 )
|
||||
{
|
||||
qFatal( "usage: %s [CheckBox|PushButton|etc.]", argv[0] );
|
||||
}
|
||||
|
||||
className = argv[1];
|
||||
#endif
|
||||
|
||||
Skinny::init();
|
||||
|
||||
auto mainView = new QskMainView;
|
||||
mainView->setPadding( 20 );
|
||||
mainView->setSpacing( 20 );
|
||||
mainView->setPanel( true );
|
||||
|
||||
auto* c = createControl( className );
|
||||
mainView->setHeader( c );
|
||||
|
||||
mainView->setBody( new SkinsBar );
|
||||
mainView->setFooter( new BrightnessBar );
|
||||
|
||||
QskWindow window;
|
||||
window.addItem( mainView );
|
||||
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#include "main.moc"
|
||||