├── .gitignore ├── LICENSE ├── README.md ├── SnowWhite ├── BusyIndicator.qml ├── Button.qml ├── CheckBox.qml ├── ComboBox.qml ├── Dial.qml ├── Frame.qml ├── Label.qml ├── ProgressBar.qml ├── RadioButton.qml ├── RangeSlider.qml ├── Slider.qml ├── SnowWhite.pri ├── SnowWhite.pro ├── SnowWhite.qml ├── SpinBox.qml ├── SplitView.qml ├── StackView.qml ├── Switch.qml ├── TextArea.qml ├── TextField.qml ├── Tumbler.qml ├── base │ └── DashedRing.qml ├── qmldir ├── script.js └── snowwhite.qrc ├── docs └── README.md ├── example ├── example-1 │ ├── ColorPicker.qml │ ├── example-1.pro │ ├── main.cpp │ ├── main.qml │ ├── qml.qrc │ ├── qtquickcontrols2.conf │ └── resources │ │ └── Carlito-Regular.ttf └── example.pro ├── extra └── preview │ ├── preview-1.webp │ └── preview-2.webp └── snow-white.pro /.gitignore: -------------------------------------------------------------------------------- 1 | *.tmp 2 | 3 | # style template 4 | *Pending/* 5 | 6 | # qt build directory 7 | build* 8 | 9 | # qt IDE user settigns 10 | *.user 11 | *.autosave 12 | 13 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 smr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QML Snow White 2 |

version tag 3 | license 4 | 5 | BCH donate

6 | 7 | 8 | **Snow White** QtQuick2 theme.
9 | *Tired of the same UI for QtQuick2?* Here is my own UI for the most common components, which you can simply utilize.
10 | You may also swap and change between color variations; the implementation is well-formed for color changes, and it is also simple to do.
11 | *If you liked these components, please consider givin a star :star2:.* 12 | ## Preview 13 | 14 |
  15 | 16 |
17 | 18 | 19 |
20 | 21 | ## How to use 22 | > **Warning**
23 | > This component has only been tested on **Qt version 5.15.2** and *Windows OS* at *3840x2160 resolution* with a *scaling factor of 250 percent*; ***USAGE OF THIS COMPONENT CARRIES NO WARRANTY***. 24 | >
  25 | 26 | ### Usage 27 | 28 | Clone the repository first. 29 | 30 | ```bash 31 | git clone https://github.com/SMR76/qml-snow-white.git 32 | ``` 33 | 34 | Then include `SnowWhite.pri` in your project. [see example-1](example/example-1/example-1.pro#L11) 35 | ```make 36 | include('path/to/SnowWhite.pri') 37 | ``` 38 | 39 | Add `qrc:/` to the engine import path. [see example-1](example/example-1/main.cpp#L17) 40 | ```cpp 41 | engine.addImportPath("qrc:/"); 42 | ``` 43 | 44 | And finally import the `SnowWhite` module. [see example-1](example/example-1/main.qml#L6) 45 | ```qml 46 | import SnowWhite 1.0 47 | ``` 48 | 49 | If you are confused, please refer to [example-1](example/example-1/) for a clearer understanding of what you should do. 50 | 51 | ## Components 52 | 53 |
54 | Available 55 | 56 | - [x] Button 57 | - [x] Radio Button 58 | - [x] CheckBox 59 | - [x] Slider 60 | - [x] TextArea 61 | - [x] TextField 62 | - [x] ProgressBar 63 | - [x] RadioButton 64 | - [x] Switch 65 | - [x] RangeSlider 66 | - [x] SpinBox 67 | - [x] Tumbler 68 | - [x] Dial 69 | - [x] BusyIndicator *(updated)* 70 | - [x] SplitView 71 | - [x] StackView 72 | - [x] ComboBox 73 | 74 |
75 | 76 | ## Issues 77 | 78 | Please file an issue on [issues page](https://github.com/SMR76/qml-snow-white/issues) if you have any problems. 79 | 80 | ## Documentation 81 | 82 | [Documentation](docs/README.md) can be found in the `docs` directory. -------------------------------------------------------------------------------- /SnowWhite/BusyIndicator.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Templates 2.15 as T 3 | 4 | T.BusyIndicator { 5 | id: control 6 | 7 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 8 | implicitContentWidth + leftPadding + rightPadding) 9 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 10 | implicitContentHeight + topPadding + bottomPadding) 11 | 12 | visible: running 13 | running: false 14 | padding: 6 15 | 16 | contentItem: ShaderEffect { 17 | id: effect 18 | 19 | property real strokeWidth: 0.06 20 | property real sweepAngle: .5 21 | property color color: control.palette.button 22 | 23 | fragmentShader: " 24 | varying highp vec2 qt_TexCoord0; 25 | uniform highp float qt_Opacity; 26 | uniform highp float sweepAngle; 27 | uniform highp float strokeWidth; 28 | uniform highp float width; 29 | uniform highp vec4 color; 30 | 31 | void main() { 32 | highp vec2 coord = qt_TexCoord0 - vec2(0.5); 33 | highp float ring = smoothstep(0.0, 0.5/width, -abs(length(coord) - 0.5 + strokeWidth) + strokeWidth); 34 | gl_FragColor = color * ring; 35 | gl_FragColor *= smoothstep(0.0, 0.5/width, -atan(coord.x, coord.y) / 6.2831 - 0.48 + sweepAngle); 36 | }" 37 | } 38 | 39 | Timer { 40 | property real seed: 0 41 | running: control.running 42 | repeat: true; interval: 25 // Almost 40Hz 43 | onTriggered: { 44 | effect.rotation += 8 45 | effect.sweepAngle = 0.5 + Math.sin(seed+=0.05) / 3 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SnowWhite/Button.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | import QtQuick.Controls 2.15 8 | import SnowWhite 1.0 9 | 10 | T.Button { 11 | id: control 12 | 13 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 14 | implicitContentWidth + leftPadding + rightPadding) 15 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 16 | implicitContentHeight + topPadding + bottomPadding) 17 | 18 | property alias radius: background.radius 19 | 20 | padding: 6 21 | spacing: 6 22 | 23 | icon.width: 24 24 | icon.height: 24 25 | icon.color: palette.buttonText 26 | 27 | display: AbstractButton.TextOnly 28 | 29 | contentItem: Item { 30 | Grid { 31 | anchors.centerIn: parent 32 | spacing: control.display == AbstractButton.TextOnly || 33 | control.display == AbstractButton.IconOnly ? 0 : control.spacing 34 | 35 | flow: control.display == AbstractButton.TextUnderIcon ? 36 | Grid.TopToBottom : Grid.LeftToRight 37 | layoutDirection: control.mirrored ? Qt.RightToLeft : Qt.LeftToRight 38 | 39 | opacity: control.down || control.checked ? 0.8 : 1.0 40 | 41 | Image { 42 | visible: control.display != AbstractButton.TextOnly 43 | source: control.icon.source 44 | width: control.icon.width 45 | height: control.icon.height 46 | cache: control.icon.cache 47 | } 48 | 49 | Text { 50 | visible: control.display != AbstractButton.IconOnly 51 | text: control.text 52 | font: control.font 53 | color: !control.enabled ? 'gray' : 54 | control.highlighted ? palette.highlightedText : 55 | palette.buttonText 56 | horizontalAlignment: Text.AlignHCenter 57 | } 58 | } 59 | } 60 | 61 | background: Rectangle { 62 | id: background 63 | visible: control.enabled 64 | 65 | implicitWidth: 45 66 | implicitHeight: 45 67 | 68 | radius: width * 0.2 69 | opacity: control.flat ? 0.5 : 1.0 70 | 71 | color: { 72 | const _color = control.highlighted ? palette.highlight : palette.button 73 | control.down ? Qt.lighter(_color, 1.1) : _color 74 | } 75 | 76 | border { 77 | width: control.visualFocus ? 1 :0 78 | color: palette.windowText 79 | } 80 | 81 | Behavior on border.width { NumberAnimation { duration: 75 } } 82 | 83 | Rectangle { 84 | x: (parent.width - width)/2 85 | y: (parent.height - height)/2 86 | radius: parent.radius - 0.05 87 | visible: control.checked 88 | color: 'transparent' 89 | border { 90 | color: palette.buttonText 91 | width: 1 92 | } 93 | 94 | NumberAnimation on width { 95 | running: control.checked 96 | from: control.background.width 97 | to: control.background.width - 5 98 | } 99 | 100 | NumberAnimation on height { 101 | running: control.checked 102 | from: control.background.height 103 | to: control.background.height - 5 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /SnowWhite/CheckBox.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.CheckBox { 9 | id: control 10 | 11 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 12 | implicitContentWidth + leftPadding + rightPadding) 13 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 14 | implicitContentHeight + topPadding + bottomPadding, 15 | implicitIndicatorHeight + topPadding + bottomPadding, 16 | indicator.height + topPadding + bottomPadding) 17 | padding: 6 18 | spacing: 6 19 | 20 | indicator: Rectangle { 21 | implicitWidth: 25 22 | implicitHeight: 25 23 | 24 | x: control.text ? 25 | (control.mirrored ? 26 | control.width - width - control.rightPadding : 27 | control.leftPadding) : 28 | control.leftPadding + (control.availableWidth - width) / 2 29 | 30 | y: control.topPadding + (control.availableHeight - height) / 2 31 | 32 | radius: 5 33 | 34 | color: 'transparent' 35 | 36 | border { 37 | color: control.visualFocus ? control.palette.highlight : control.palette.button 38 | width: 2 39 | } 40 | 41 | Rectangle { 42 | id: ibox 43 | x: (parent.width - width) / 2 44 | y: (parent.height - height) / 2 45 | 46 | color: control.palette.button 47 | 48 | radius: 3 49 | 50 | states:[ 51 | State { 52 | when: control.checkState === Qt.Checked 53 | PropertyChanges { 54 | target: ibox 55 | width: control.indicator.width - 8 56 | height: control.indicator.height - 8 57 | } 58 | }, 59 | State { 60 | when: control.checkState === Qt.Unchecked 61 | PropertyChanges { target: ibox; width: 0; height: 0 } 62 | }, 63 | State { 64 | when: control.checkState === Qt.PartiallyChecked 65 | PropertyChanges { 66 | target: ibox 67 | width: control.indicator.width - 8 68 | height: control.indicator.height/3 69 | } 70 | } 71 | ] 72 | 73 | transitions: [ 74 | Transition { 75 | from: "*" 76 | to: "*" 77 | NumberAnimation { properties: "width, height"; duration: 100 } 78 | } 79 | ] 80 | } 81 | } 82 | 83 | contentItem: Text { 84 | leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0 85 | rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0 86 | verticalAlignment: Text.AlignVCenter 87 | text: control.text 88 | font: control.font 89 | color: control.palette.windowText 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /SnowWhite/ComboBox.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Controls 2.15 7 | import QtQuick.Window 2.15 8 | import QtQuick.Templates 2.15 as T 9 | 10 | T.ComboBox { 11 | id: control 12 | 13 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 14 | implicitContentWidth + leftPadding + rightPadding) 15 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 16 | implicitContentHeight + topPadding + bottomPadding, 17 | implicitIndicatorHeight + topPadding + bottomPadding) 18 | leftPadding: 5 + (!control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing) 19 | rightPadding: 5 + (control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing) 20 | spacing: 5 21 | 22 | delegate: ItemDelegate { 23 | width: ListView.view.width 24 | text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData 25 | palette.text: control.palette.buttonText 26 | palette.highlightedText: control.palette.highlightedText 27 | hoverEnabled: control.hoverEnabled 28 | 29 | background: Rectangle { 30 | radius: 5 31 | color: control.palette.button 32 | opacity: control.currentIndex === index || hovered ? 1 : 0.7 33 | border.width: visualFocus ? 2 : 1 34 | border.color: control.palette.buttonText 35 | 36 | Behavior on opacity { NumberAnimation { duration: 100 } } 37 | 38 | Rectangle { 39 | x: parent.width * 0.9 40 | y: (parent.height - height)/2 41 | width: 5; height: 5; radius: width 42 | color: control.palette.buttonText 43 | visible: control.currentIndex === index 44 | } 45 | } 46 | } 47 | 48 | indicator: Text { 49 | x: control.mirrored ? control.padding : control.availableWidth + control.spacing + 4 50 | y: control.topPadding + (control.availableHeight - height)/2 51 | width: implicitWidth 52 | color: control.palette.buttonText 53 | text: "\u2261" 54 | font.pixelSize: 12 55 | font.bold: true 56 | opacity: enabled ? 1 : 0.3 57 | } 58 | 59 | contentItem: T.TextField { 60 | leftPadding: !control.mirrored ? 12 : 13 61 | rightPadding: control.mirrored ? 12 : 13 62 | topInset: 5; bottomInset: 5 63 | text: control.editable ? control.editText : control.displayText 64 | 65 | enabled: control.editable 66 | autoScroll: control.editable 67 | readOnly: !control.editable 68 | inputMethodHints: control.inputMethodHints 69 | validator: control.validator 70 | selectByMouse: control.selectTextByMouse 71 | 72 | font: control.font 73 | color: control.palette.windowText 74 | selectionColor: control.palette.highlight 75 | selectedTextColor: control.palette.highlightedText 76 | verticalAlignment: Text.AlignVCenter 77 | 78 | background: Rectangle { 79 | visible: control.enabled && control.editable && !control.flat 80 | color: control.palette.window 81 | opacity: parent.activeFocus && control.editable ? 0.9 : 0.6 82 | radius: 2 83 | Behavior on opacity { NumberAnimation { duration: 100 } } 84 | } 85 | } 86 | 87 | background: Rectangle { 88 | implicitWidth: 140 89 | implicitHeight: 40 90 | 91 | visible: !control.flat || control.down 92 | radius: 5 93 | color: control.palette.button 94 | opacity: control.down ? 0.8 : 1.0 95 | 96 | Behavior on opacity { NumberAnimation { duration: 100 } } 97 | } 98 | 99 | popup: T.Popup { 100 | y: control.height 101 | topPadding: 2 102 | width: control.width 103 | height: Math.min(contentItem.implicitHeight, control.Window.height - y - control.y) + 2 104 | 105 | contentItem: ListView { 106 | clip: true 107 | implicitHeight: contentHeight 108 | model: control.delegateModel 109 | spacing: 2 110 | currentIndex: control.highlightedIndex 111 | highlightMoveDuration: 0 112 | T.ScrollIndicator.vertical: ScrollIndicator { } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /SnowWhite/Dial.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | import QtQuick.Controls 2.15 8 | import SnowWhite 1.0 9 | 10 | T.Dial { 11 | id: control 12 | property alias dottedRing: dottedRing 13 | 14 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 15 | implicitContentWidth + leftPadding + rightPadding) 16 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 17 | implicitContentHeight + topPadding + bottomPadding) 18 | 19 | background: Item { 20 | implicitWidth: 184 21 | implicitHeight: 184 22 | 23 | clip: true 24 | 25 | Rectangle { 26 | x: (control.availableWidth - width)/2 27 | y: (control.availableHeight - height)/2 28 | 29 | implicitWidth: parent.width - 20 30 | implicitHeight: parent.width - 20 31 | 32 | radius: width 33 | 34 | border.color: 'transparent' 35 | 36 | border.width: 1 37 | color: control.palette.button 38 | } 39 | 40 | DashedRing { 41 | id: dottedRing 42 | 43 | x: (control.availableWidth - width)/2 44 | y: (control.availableHeight - height)/2 45 | 46 | implicitWidth: parent.width - 5 47 | implicitHeight: parent.width - 5 48 | 49 | borderWidth: 2 50 | dashWidth: 2 51 | dashCount: 25 52 | palette.base: control.palette.button 53 | } 54 | } 55 | 56 | handle: Item { 57 | width: control.width 58 | height: control.height 59 | 60 | rotation: control.angle * 1.07 61 | 62 | Rectangle { 63 | x: (parent.width - width)/2 64 | y: 12 65 | width: 2; height: 20 66 | radius: width 67 | color: control.palette.buttonText 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /SnowWhite/Frame.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.Frame { 9 | id: control 10 | 11 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 12 | contentWidth + leftPadding + rightPadding) 13 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 14 | contentHeight + topPadding + bottomPadding) 15 | 16 | padding: 6 17 | 18 | background: Rectangle { 19 | color: 'transparent' 20 | radius: 3 21 | border.width: 1 22 | border.color: control.palette.button 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SnowWhite/Label.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Templates 2.15 as T 3 | 4 | T.Label { 5 | id: control 6 | 7 | color: control.palette.windowText 8 | linkColor: control.palette.link 9 | } 10 | -------------------------------------------------------------------------------- /SnowWhite/ProgressBar.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.ProgressBar { 9 | id: control 10 | 11 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 12 | implicitContentWidth + leftPadding + rightPadding) 13 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 14 | implicitContentHeight + topPadding + bottomPadding) 15 | 16 | property int orientation: Qt.Horizontal 17 | padding: 2 18 | 19 | QtObject { 20 | id: orient 21 | property bool vertical: control.orientation == Qt.Vertical 22 | property bool horizontal: control.orientation == Qt.Horizontal 23 | } 24 | 25 | contentItem: Item { 26 | Rectangle { 27 | x: 2 28 | y: (parent.height - height)/2 29 | width: control.position * (parent.width - 4) 30 | height: Math.min(width, parent.height - 4) 31 | 32 | radius: Math.max(width, height) 33 | color: control.palette.button 34 | } 35 | } 36 | 37 | background: Rectangle { 38 | implicitWidth: orient.horizontal ? 200 : 18 39 | implicitHeight: orient.vertical ? 200 : 18 40 | 41 | color: 'transparent' 42 | radius: width 43 | 44 | border { 45 | color: control.palette.button 46 | width: 2 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SnowWhite/RadioButton.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | import SnowWhite 1.0 8 | 9 | T.RadioButton { 10 | id: control 11 | 12 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 13 | implicitContentWidth + leftPadding + rightPadding) 14 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 15 | implicitContentHeight + topPadding + bottomPadding, 16 | implicitIndicatorHeight + topPadding + bottomPadding) 17 | 18 | padding: 6 19 | spacing: 6 20 | 21 | indicator: Rectangle { 22 | implicitWidth: 25 23 | implicitHeight: 25 24 | 25 | x: control.text ? 26 | (control.mirrored ? 27 | control.width - width - control.rightPadding : 28 | control.leftPadding) : 29 | control.leftPadding + (control.availableWidth - width) / 2 30 | 31 | y: control.topPadding + (control.availableHeight - height) / 2 32 | 33 | radius: width 34 | 35 | color: 'transparent' 36 | 37 | border { 38 | color: control.visualFocus ? control.palette.highlight : control.palette.button 39 | width: 2 40 | } 41 | 42 | Rectangle { 43 | id: ibox 44 | x: (parent.width - width) / 2 45 | y: (parent.height - height) / 2 46 | 47 | width: control.checked ? parent.width - 8 : 0 48 | height: width 49 | 50 | color: palette.button 51 | 52 | radius: width 53 | 54 | Behavior on width { 55 | NumberAnimation { duration: 100 } 56 | } 57 | } 58 | } 59 | 60 | contentItem: Text { 61 | leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0 62 | rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0 63 | verticalAlignment: Text.AlignVCenter 64 | color: palette.windowText 65 | text: control.text 66 | font: control.font 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /SnowWhite/RangeSlider.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | import QtQuick.Controls 2.15 as QQC 8 | 9 | T.RangeSlider { 10 | id: control 11 | 12 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 13 | first.implicitHandleWidth + leftPadding + rightPadding, 14 | second.implicitHandleWidth + leftPadding + rightPadding) 15 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 16 | first.implicitHandleHeight + topPadding + bottomPadding, 17 | second.implicitHandleHeight + topPadding + bottomPadding) 18 | padding: 6 19 | 20 | component Handler: Rectangle { 21 | property real vpos: 0 22 | x: control.leftPadding + (control.horizontal ? vpos * (control.availableWidth - width) : (control.availableWidth - width) / 2) 23 | y: control.topPadding + (!control.horizontal ? vpos * (control.availableHeight - height) : (control.availableHeight - height) / 2) 24 | 25 | implicitWidth: 20 26 | implicitHeight: 20 27 | 28 | radius: width / 2 29 | border { 30 | color: control.palette.window 31 | width: 2 32 | } 33 | } 34 | 35 | first.handle: Handler { 36 | vpos: control.first.visualPosition 37 | color: control.first.pressed ? Qt.lighter(control.palette.button, 1.1) : control.palette.button 38 | border.color: activeFocus ? control.palette.highlight : control.palette.window 39 | } 40 | 41 | second.handle: Handler { 42 | vpos: control.second.visualPosition 43 | color: control.second.pressed ? Qt.lighter(control.palette.button, 1.1) : control.palette.button 44 | border.color: activeFocus ? control.palette.highlight : control.palette.window 45 | } 46 | 47 | background: Rectangle { 48 | x: (control.width - width) / 2 49 | y: (control.height - height) / 2 50 | 51 | implicitWidth: !control.vertical ? 200 : 2 52 | implicitHeight: control.vertical ? 200 : 2 53 | 54 | width: !control.vertical ? control.availableWidth : implicitWidth 55 | height: control.vertical ? control.availableHeight : implicitHeight 56 | 57 | radius: width 58 | color: control.palette.button 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SnowWhite/Slider.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | 6 | import QtQuick 2.15 7 | import QtQuick.Templates 2.15 as T 8 | 9 | T.Slider { 10 | id: control 11 | 12 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 13 | implicitHandleWidth + leftPadding + rightPadding) 14 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 15 | implicitHandleHeight + topPadding + bottomPadding) 16 | padding: 6 17 | 18 | handle: Rectangle { 19 | x: control.leftPadding + (control.horizontal ? control.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2) 20 | y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.visualPosition * (control.availableHeight - height)) 21 | 22 | implicitWidth: 20 23 | implicitHeight: 20 24 | 25 | color: control.palette.button 26 | radius: width 27 | 28 | border { 29 | color: visualFocus ? control.palette.highlight : control.palette.window 30 | width: 2 31 | } 32 | 33 | Behavior on border.width { NumberAnimation{ duration: 100} } 34 | } 35 | 36 | background: Rectangle { 37 | x: (control.width - width) / 2 38 | y: (control.height - height) / 2 39 | 40 | implicitWidth: control.horizontal ? 200 : 2 41 | implicitHeight: control.horizontal ? 2 : 200 42 | 43 | width: control.horizontal ? control.availableWidth : implicitWidth 44 | height: control.horizontal ? implicitHeight : control.availableHeight 45 | 46 | radius: width 47 | 48 | color: control.palette.button 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SnowWhite/SnowWhite.pri: -------------------------------------------------------------------------------- 1 | QML_IMPORT_PATH += $${PWD}/.. 2 | QML2_IMPORT_PATH += $${PWD}/.. 3 | QML_DESIGNER_IMPORT_PATH += $${PWD}/.. 4 | 5 | RESOURCES += $${PWD}/snowwhite.qrc 6 | -------------------------------------------------------------------------------- /SnowWhite/SnowWhite.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | QT += qml quick quickcontrols2 3 | CONFIG += qt plugin 4 | 5 | include(SnowWhite.pri) 6 | 7 | qmltypes.target = qmltypes 8 | qmltypes.commands = $$[QT_INSTALL_BINS]/qmlplugindump SnowWhite 1.0 $$QMAKE_RESOLVED_TARGET > $$PWD/snowwhite.qmltypes 9 | qmltypes.depends = $$QMAKE_RESOLVED_TARGET 10 | 11 | QMAKE_EXTRA_TARGETS += qmltypes 12 | -------------------------------------------------------------------------------- /SnowWhite/SnowWhite.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | pragma Singleton 6 | import QtQuick 2.15 7 | 8 | QtObject { 9 | /** 10 | * 11 | */ 12 | function blend(color1, color2) { 13 | let color = color1; 14 | color.r = (color1.r + color2.r) / 2; 15 | color.g = (color1.g + color2.g) / 2; 16 | color.b = (color1.b + color2.b) / 2; 17 | color.b = (color1.a + color2.a) / 2; 18 | return color; 19 | } 20 | 21 | function setAlpha(color, alpha) { 22 | color.a = alpha; 23 | return color; 24 | } 25 | 26 | function clamp(x, a, b) { 27 | return Math.min(Math.max(x, a), b); 28 | } 29 | 30 | function remap(value, low1, high1, low2, high2) { 31 | return low2 + (high2 - low2) * (value - low1) / (high1 - low1); 32 | } 33 | 34 | function invertColor(color) { 35 | return Qt.rgba(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, 1.0); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SnowWhite/SpinBox.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | 6 | import QtQuick 2.15 7 | import QtQuick.Templates 2.15 as T 8 | 9 | T.SpinBox { 10 | id: control 11 | 12 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 13 | contentItem.implicitWidth + 2 * padding + 14 | up.implicitIndicatorWidth + 15 | down.implicitIndicatorWidth) 16 | implicitHeight: Math.max(implicitContentHeight + topPadding + bottomPadding, 17 | implicitBackgroundHeight, 18 | up.implicitIndicatorHeight, 19 | down.implicitIndicatorHeight) 20 | 21 | padding: 6 22 | leftPadding: padding + (control.mirrored ? (up.indicator ? up.indicator.width : 0) : (down.indicator ? down.indicator.width : 0)) 23 | rightPadding: padding + (control.mirrored ? (down.indicator ? down.indicator.width : 0) : (up.indicator ? up.indicator.width : 0)) 24 | font.bold: true 25 | 26 | component Indicator: Text { 27 | width: 30; height: parent.height 28 | 29 | font.pointSize: 10 30 | font.bold: true 31 | 32 | color: control.palette.button 33 | horizontalAlignment: Text.AlignHCenter 34 | verticalAlignment: Text.AlignVCenter 35 | } 36 | 37 | validator: IntValidator { 38 | locale: control.locale.name 39 | bottom: Math.min(control.from, control.to) 40 | top: Math.max(control.from, control.to) 41 | } 42 | 43 | contentItem: TextInput { 44 | text: control.displayText 45 | 46 | font: control.font 47 | color: control.palette.button 48 | selectionColor: control.palette.highlight 49 | selectedTextColor: control.palette.highlightedText 50 | horizontalAlignment: Text.AlignHCenter 51 | verticalAlignment: Text.AlignVCenter 52 | 53 | readOnly: !control.editable 54 | validator: control.validator 55 | inputMethodHints: control.inputMethodHints 56 | 57 | Rectangle { 58 | y: parent.height - height 59 | width: parent.width 60 | height: parent.readOnly ? 0 : 1 61 | color: control.palette.button 62 | } 63 | } 64 | 65 | up.indicator: Indicator { 66 | x: control.mirrored ? 0 : parent.width - width 67 | text: "+"; 68 | opacity: !enabled || control.up.pressed ? 0.4 : 1 69 | } 70 | 71 | down.indicator: Indicator { 72 | x: control.mirrored ? parent.width - width : 0 73 | text: "-"; 74 | opacity: !enabled || control.down.pressed ? 0.4 : 1 75 | } 76 | 77 | 78 | background: Rectangle { 79 | id: background 80 | implicitWidth: 80 81 | implicitHeight: 30 82 | 83 | radius: 5 84 | color: 'transparent' 85 | border.width: 2 86 | border.color: control.palette.button 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /SnowWhite/SplitView.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.SplitView { 9 | id: control 10 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 11 | implicitContentWidth + leftPadding + rightPadding) 12 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 13 | implicitContentHeight + topPadding + bottomPadding) 14 | 15 | handle: Rectangle { 16 | implicitWidth: control.orientation === Qt.Horizontal ? 4 : control.width 17 | implicitHeight: control.orientation === Qt.Horizontal ? control.height : 4 18 | color: control.enabled ? control.palette.button : '#aaa' 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SnowWhite/StackView.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.StackView { 9 | id: control 10 | 11 | pushEnter: Transition { 12 | NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 700; easing.type: Easing.OutCubic } 13 | } 14 | 15 | pushExit: Transition { 16 | NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 700; easing.type: Easing.OutCubic } 17 | } 18 | 19 | popEnter : Transition { 20 | NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 700; easing.type: Easing.OutCubic } 21 | } 22 | 23 | popExit : Transition { 24 | NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 700; easing.type: Easing.OutCubic } 25 | } 26 | 27 | replaceEnter : Transition { 28 | NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 700; easing.type: Easing.OutCubic } 29 | } 30 | 31 | replaceExit : Transition { 32 | NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 700; easing.type: Easing.OutCubic } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SnowWhite/Switch.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | 6 | import QtQuick 2.15 7 | import QtQuick.Templates 2.15 as T 8 | 9 | T.Switch { 10 | id: control 11 | 12 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 13 | implicitContentWidth + leftPadding + rightPadding) 14 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 15 | implicitContentHeight + topPadding + bottomPadding, 16 | implicitIndicatorHeight + topPadding + bottomPadding) 17 | 18 | padding: 6 19 | spacing: 6 20 | 21 | indicator: Rectangle { 22 | implicitWidth: 50 23 | implicitHeight: 25 24 | 25 | x: control.text ? 26 | (control.mirrored ? 27 | control.width - width - control.rightPadding : 28 | control.leftPadding) : 29 | control.leftPadding + (control.availableWidth - width) / 2 30 | 31 | y: control.topPadding + (control.availableHeight - height) / 2 32 | 33 | color: 'transparent' 34 | 35 | radius: 5 36 | 37 | border { 38 | color: control.visualFocus ? control.palette.highlight : control.palette.button 39 | width: 2 40 | } 41 | 42 | Rectangle { 43 | id: ibox 44 | x: Math.min(Math.max(y, control.visualPosition * parent.width - height/2), parent.width - height - y) 45 | y: 4 46 | 47 | color: control.palette.button 48 | 49 | width: { 50 | let pos = Math.abs(0.5 - (x-y)/(parent.implicitWidth - height - 2 * y)); 51 | height + 8 - pos * 8 * 2 52 | } 53 | height: control.indicator.height - 8 54 | 55 | radius: 3 56 | 57 | Behavior on x { 58 | enabled: !control.down 59 | NumberAnimation{ duration: 200 } 60 | } 61 | } 62 | } 63 | 64 | contentItem: Text { 65 | leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0 66 | rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0 67 | verticalAlignment: Text.AlignVCenter 68 | text: control.text 69 | font: control.font 70 | color: control.palette.windowText 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SnowWhite/TextArea.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.TextArea { 9 | id: control 10 | 11 | implicitWidth: Math.max(contentWidth + leftPadding + rightPadding, 12 | implicitBackgroundWidth + leftInset + rightInset, 13 | placeholder.implicitWidth + leftPadding + rightPadding) 14 | implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, 15 | implicitBackgroundHeight + topInset + bottomInset, 16 | placeholder.implicitHeight + topPadding + bottomPadding) 17 | 18 | padding: 6 19 | leftPadding: padding + 4 20 | 21 | color: control.palette.windowText 22 | placeholderTextColor: palette.mid 23 | selectionColor: control.palette.highlight 24 | selectedTextColor: control.palette.highlightedText 25 | 26 | Text { 27 | id: placeholder 28 | x: control.leftPadding 29 | y: control.topPadding 30 | width: control.width - (control.leftPadding + control.rightPadding) 31 | height: control.height - (control.topPadding + control.bottomPadding) 32 | 33 | text: control.placeholderText 34 | font: control.font 35 | 36 | color: control.placeholderTextColor 37 | 38 | verticalAlignment: control.verticalAlignment 39 | visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) 40 | elide: Text.ElideRight 41 | renderType: control.renderType 42 | } 43 | 44 | background: Rectangle { 45 | implicitWidth: 200 46 | implicitHeight: 40 47 | 48 | opacity: control.activeFocus ? 1 : 0.5 49 | color: Qt.lighter(palette.window, palette.window.hslLightness * 2) 50 | 51 | border.color: Qt.lighter(palette.button, 0.5 + palette.window.hslLightness) 52 | border.width: 0.5 53 | 54 | Behavior on opacity { NumberAnimation { duration: 100 } } 55 | 56 | Rectangle { 57 | y: parent.height - height 58 | height: control.activeFocus ? 3 : 1 59 | width: parent.width 60 | color: control.enabled ? control.palette.button : '#aaa' 61 | 62 | Behavior on height { NumberAnimation { duration: 100 } } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SnowWhite/TextField.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.TextField { 9 | id: control 10 | 11 | implicitWidth: implicitBackgroundWidth + leftInset + rightInset 12 | || Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding 13 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 14 | contentHeight + topPadding + bottomPadding, 15 | placeholder.implicitHeight + topPadding + bottomPadding) 16 | 17 | padding: 6 18 | leftPadding: padding + 4 19 | 20 | color: control.palette.windowText 21 | selectionColor: control.palette.highlight 22 | selectedTextColor: control.palette.highlightedText 23 | placeholderTextColor: control.palette.text 24 | verticalAlignment: TextInput.AlignVCenter 25 | 26 | Text { 27 | id: placeholder 28 | x: control.leftPadding 29 | y: control.topPadding 30 | width: control.width - (control.leftPadding + control.rightPadding) 31 | height: control.height - (control.topPadding + control.bottomPadding) 32 | 33 | text: control.placeholderText 34 | font: control.font 35 | 36 | color: control.placeholderTextColor 37 | 38 | verticalAlignment: control.verticalAlignment 39 | visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) 40 | elide: Text.ElideRight 41 | renderType: control.renderType 42 | } 43 | 44 | background: Rectangle { 45 | implicitWidth: 200 46 | implicitHeight: 40 47 | 48 | opacity: control.activeFocus ? 1 : 0.5 49 | color: Qt.lighter(palette.window, palette.window.hslLightness * 2) 50 | 51 | border.color: Qt.lighter(palette.button, 0.5 + palette.window.hslLightness) 52 | border.width: 0.5 53 | 54 | Behavior on opacity { NumberAnimation { duration: 100 } } 55 | 56 | Rectangle { 57 | height: parent.height 58 | width: control.activeFocus ? 3 : 1 59 | 60 | color: control.enabled ? control.palette.button : '#00000055' 61 | 62 | Behavior on width { NumberAnimation { duration: 100 } } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SnowWhite/Tumbler.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Templates 2.15 as T 7 | 8 | T.Tumbler { 9 | id: control 10 | 11 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 12 | implicitContentWidth + leftPadding + rightPadding) 13 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 14 | implicitContentHeight + topPadding + bottomPadding) 15 | 16 | font.pixelSize: 12 17 | palette.text: "gray" 18 | 19 | delegate: Text { 20 | text: modelData 21 | color: control.visualFocus ? control.palette.highlightedText : control.palette.buttonText 22 | font: control.font 23 | opacity: 0.4 + Math.max(0, 1 - Math.abs(Tumbler.displacement)) * 0.6 24 | horizontalAlignment: Text.AlignHCenter 25 | verticalAlignment: Text.AlignVCenter 26 | } 27 | 28 | contentItem: PathView { 29 | id: pathview 30 | preferredHighlightBegin: 0.5 31 | preferredHighlightEnd: 0.5 32 | highlightRangeMode: PathView.StrictlyEnforceRange 33 | implicitWidth: 60 34 | implicitHeight: 80 35 | clip: true 36 | model: control.model 37 | delegate: control.delegate 38 | pathItemCount: control.visibleItemCount 39 | path: Path { 40 | startX: control.availableWidth / 2 41 | startY: 0 42 | PathLine { 43 | x: control.availableWidth / 2 44 | y: control.availableHeight 45 | } 46 | } 47 | } 48 | 49 | background: Rectangle { 50 | implicitWidth: 60 51 | implicitHeight: 80 52 | radius: 5 53 | color: control.palette.button 54 | 55 | Rectangle { 56 | y: (parent.height - height)/2 57 | x: (parent.width - width)/2 58 | width: parent.width * 0.7 59 | height: control.currentItem.height 60 | radius: 3 61 | opacity: Math.abs(pathview.offset % 1 - 0.5) * 2 62 | border.width: 1 63 | border.color: control.palette.window 64 | color: 'transparent' 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /SnowWhite/base/DashedRing.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | 5 | import QtQuick 2.15 6 | import QtQuick.Controls 2.15 7 | 8 | Control { 9 | id: control 10 | 11 | property real dashCount: 2 12 | property real dashWidth: 2 13 | property real borderWidth: 2 14 | 15 | ShaderEffect { 16 | id: effect 17 | width: parent.width; 18 | height: width; 19 | readonly property real count: 360 / control.dashCount 20 | readonly property real dashWidth: control.dashWidth / 2 21 | readonly property real borderWidth: control.borderWidth / width / 2 22 | readonly property real smoothstp: 0.5 / width 23 | readonly property color color: control.palette.base; 24 | 25 | fragmentShader: " 26 | varying highp vec2 qt_TexCoord0; 27 | uniform highp float qt_Opacity; 28 | uniform highp float count; 29 | uniform highp float dashWidth; 30 | uniform highp float borderWidth; 31 | uniform highp float smoothstp; 32 | uniform highp vec4 color; 33 | 34 | void main() { 35 | highp vec2 normal = qt_TexCoord0 - vec2(0.5); 36 | gl_FragColor = color; 37 | highp float ticks = smoothstep(0.0, 20 * smoothstp / count, -abs(fract(atan(normal.x, normal.y) * 57.2958 / count) - 0.5) + dashWidth / count); 38 | highp float ring = smoothstep(0.0, smoothstp, -abs(length(normal) - 0.5 + borderWidth) + borderWidth); 39 | gl_FragColor = gl_FragColor * ring * ticks; 40 | }" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SnowWhite/qmldir: -------------------------------------------------------------------------------- 1 | module SnowWhite 2 | typeinfo snowwhite.qmltypes 3 | 4 | Script 1.0 script.js 5 | 6 | singleton SnowWhite 1.0 SnowWhite.qml 7 | 8 | DashedRing 1.0 base/DashedRing.qml 9 | 10 | Button 1.0 Button.qml 11 | BusyIndicator 1.0 BusyIndicator.qml 12 | CheckBox 1.0 CheckBox.qml 13 | Label 1.0 Label.qml 14 | ProgressBar 1.0 ProgressBar.qml 15 | RadioButton 1.0 RadioButton.qml 16 | RangeSlider 1.0 RangeSlider.qml 17 | Slider 1.0 Slider.qml 18 | SpinBox 1.0 SpinBox.qml 19 | Switch 1.0 Switch.qml 20 | TextArea 1.0 TextArea.qml 21 | TextField 1.0 TextField.qml 22 | Tumbler 1.0 Tumbler.qml 23 | HorizontalSeprator 1.0 HorizontalSeprator.qml 24 | SplitView 1.0 SplitView.qml 25 | Frame 1.0 Frame.qml 26 | Dial 1.0 Dial.qml 27 | ComboBox 1.0 ComboBox.qml 28 | -------------------------------------------------------------------------------- /SnowWhite/script.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 smr. 2 | // SPDX-License-Identifier: LGPL-3.0-only 3 | // http://s-m-r.ir 4 | -------------------------------------------------------------------------------- /SnowWhite/snowwhite.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | base/DashedRing.qml 4 | Button.qml 5 | CheckBox.qml 6 | Label.qml 7 | SnowWhite.qml 8 | ProgressBar.qml 9 | RadioButton.qml 10 | RangeSlider.qml 11 | Slider.qml 12 | SpinBox.qml 13 | Switch.qml 14 | TextArea.qml 15 | TextField.qml 16 | Tumbler.qml 17 | BusyIndicator.qml 18 | SplitView.qml 19 | Frame.qml 20 | Dial.qml 21 | ComboBox.qml 22 | StackView.qml 23 | qmldir 24 | script.js 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | This file only offers documentation for the additional properties of components given by the SnowWhite theme.
4 | On [doc.qt.io](https://doc.qt.io/qt-6/qtquick-controls2-qmlmodule.html), you can find the documentation for the remaining components. 5 | 6 | ## Table Of Content 7 | - [Quick Start](#quick-start) 8 | - [Change Color Theme](#change-color-theme) 9 | - [Components](#components) 10 | + **base** 11 | * [DashedRing](#dashed-ring) `[base]` 12 | + [Button](#dial) 13 | + [Dial](#dial) 14 | + [ProgressBar](#progressbar) 15 | + [SnowWhite](#snowwhite) `[singleton]` 16 | 17 | ## Quick Start 18 | 19 | See the Usage section of the main [README](../README.md#usage) page. 20 | 21 | ## Change Color Theme 22 | 23 | To alter the color scheme of a component, update the palette properties on the parent component so that all children are affected. 24 | 25 | **button**: 26 | + `palette.button` 27 | + `palette.buttonText` 28 | 29 | **window & base**: 30 | + `palette.base` 31 | + `palette.text` 32 | + `palette.window` 33 | + `palette.windowText` 34 | 35 | **highlights**: 36 | + `palette.highlight` 37 | + `palette.highlightedText` 38 | 39 | ## Components 40 | 41 | ### Dashed Ring 42 | 43 | This is a basic component which generates a dashed ring. 44 | 45 | #### Component properties: 46 | ##### `[property]` `[real]` dashCount: 2 47 | The number of dashes on the ring is specified by this property. 48 | ##### `[property]` `[real]` borderWidth: 2 49 | This property specifies the width of the border, which can range from $0$ to $\dfrac{width}{2}$. 50 | ##### `[property]` `[real]` dashWidth: 2 51 | This property specifies the width of dashes. 52 | ##### `[property]` `[color]` palette.base 53 | This property derives from the `Control` component, which specifies the `color` of the dashes. 54 | 55 | ```qml 56 | DashedRing { 57 | width: 100 58 | height: 100 59 | 60 | borderWidth: 2 61 | dashWidth: 2 62 | dashCount: 25 63 | palette.base: 'blue' 64 | } 65 | ``` 66 | ---- 67 | ### Button 68 | #### Component properties: 69 | ##### `[property]` `[real]` radius 70 | This property determines the radius of background. 71 | 72 | ---- 73 | ### Dial 74 | #### Component properties: 75 | ##### `[property]` `[DashedRing]` dottedRing 76 | This property refers to the [DashedRing](#dashed-ring) component used in the Dial component. 77 | 78 | ---- 79 | ### ProgressBar 80 | #### Component properties: 81 | ##### `[property]` `[int]` orientation: Qt.Horizontal 82 | This property determines the orientation of the ProgressBar component.
83 | Either `Qt.Horizontal` or `Qt.Vertical` can be the value. 84 | 85 | ---- 86 | ### SnowWhite 87 | #### Component functions: 88 | ##### function blend(`color` color1, `color` color2): `color` 89 | This function returns an average color from two input color RGBA values. 90 | ```javascript 91 | SnowWhite.blend(Qt.rgba(0, 0, 0, 1), Qt.rgba(1, 1, 1, 0)); 92 | // Qt.rgba(.5, .5, .5, .5) 93 | ``` 94 | 95 | ##### function setAlpha(`color` color, `real` alpha): `color` 96 | This function set new alpha values for given color. 97 | ```javascript 98 | SnowWhite.setAlpha(Qt.rgba(1, 1, 1, 1), 0.3); // Qt.rgba(1, 1, 1, 0.3) 99 | ``` 100 | 101 | ##### function clamp(`real` x, `real` a, `real` b): `real` 102 | This functions constrain `x` to lie between `a` and `b`. 103 | ```javascript 104 | SnowWhite.clamp(1, 3, 5); // 3 105 | SnowWhite.clamp(1, 8, 5); // 5 106 | SnowWhite.clamp(1,-4, 5); // 1 107 | ``` 108 | 109 | ##### function remap(`real` value, `real` low1, `real` high1, `real` low2, `real` high2): `real` 110 | This function re-maps a number from one range to another. 111 | ```javascript 112 | SnowWhite.remap(2, 1, 3, 0, 10); // 5 113 | ``` 114 | 115 | ##### function invertColor(`color` color): `color` 116 | This function invert RGB values of input color. 117 | ```javascript 118 | SnowWhite.invertColor(Qt.rgba(0.2, 1, 0.5, 1)); // Qt.rgba(0.8, 0, 0.5, 1) 119 | ``` 120 | -------------------------------------------------------------------------------- /example/example-1/ColorPicker.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | 4 | Item { 5 | id: control 6 | property color selectedColor: '#f5f5f5' 7 | implicitWidth: 150 8 | 9 | function setColor (value, saturation) { 10 | value = value < 0 ? 0 : value > 1 ? 1 : value; 11 | saturation = saturation < 0 ? 0 : saturation > 1 ? 1 : saturation; 12 | control.selectedColor.hsvHue = slider.value; 13 | control.selectedColor.hsvValue = value; 14 | control.selectedColor.hsvSaturation = saturation; 15 | } 16 | 17 | Column { 18 | width: parent.width 19 | Rectangle { 20 | id: colorRect 21 | width: parent.width 22 | height: width 23 | 24 | gradient: Gradient { 25 | orientation: Gradient.Horizontal 26 | GradientStop { position: 0.0; color: "white" } 27 | GradientStop { position: 1.0; color: Qt.hsva(selectedColor.hsvHue,1,1,1) } 28 | } 29 | 30 | Rectangle { 31 | anchors.fill: parent 32 | gradient: Gradient { 33 | GradientStop { position: 0.0; color: "transparent" } 34 | GradientStop { position: 1.0; color: "black" } 35 | } 36 | } 37 | 38 | Rectangle { 39 | anchors.fill: parent 40 | gradient: Gradient { 41 | GradientStop { position: 0.0; color: "transparent" } 42 | GradientStop { position: 1.0; color: "black" } 43 | } 44 | } 45 | 46 | MouseArea { 47 | anchors.fill: colorRect 48 | 49 | onPositionChanged: control.setColor(1 - mouseY/height, mouseX/width); 50 | onPressed: control.setColor(1 - mouseY/height, mouseX/width); 51 | } 52 | } 53 | 54 | Slider { 55 | id: slider 56 | width: 150; 57 | handle.width: 15; 58 | handle.height: 15; 59 | from: 0; to: 1; 60 | stepSize: 0.01; 61 | 62 | background: Rectangle { 63 | height: 5 64 | width: slider.width - (2 * x) 65 | x: slider.padding + slider.handle.width/2 66 | y: slider.height/2 - height/2 67 | color: control.selectedColor 68 | radius: height/2 69 | 70 | 71 | gradient: Gradient { 72 | orientation: Gradient.Horizontal 73 | GradientStop { position: 0.0; color: Qt.hsva(0.0,1,1,1) } 74 | GradientStop { position: 0.1; color: Qt.hsva(0.1,1,1,1) } 75 | GradientStop { position: 0.2; color: Qt.hsva(0.2,1,1,1) } 76 | GradientStop { position: 0.3; color: Qt.hsva(0.3,1,1,1) } 77 | GradientStop { position: 0.4; color: Qt.hsva(0.4,1,1,1) } 78 | GradientStop { position: 0.5; color: Qt.hsva(0.5,1,1,1) } 79 | GradientStop { position: 0.6; color: Qt.hsva(0.6,1,1,1) } 80 | GradientStop { position: 0.7; color: Qt.hsva(0.7,1,1,1) } 81 | GradientStop { position: 0.8; color: Qt.hsva(0.8,1,1,1) } 82 | GradientStop { position: 0.9; color: Qt.hsva(0.9,1,1,1) } 83 | GradientStop { position: 1.0; color: Qt.hsva(1.0,1,1,1) } 84 | } 85 | } 86 | 87 | onValueChanged: { 88 | control.selectedColor.hsvHue = value 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /example/example-1/example-1.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += qml quick quickcontrols2 3 | CONFIG += c++11 4 | # You can make your code fail to compile if it uses deprecated APIs. 5 | # In order to do so, uncomment the following line. 6 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 7 | 8 | SOURCES += main.cpp 9 | RESOURCES += qml.qrc 10 | 11 | include(../../SnowWhite/SnowWhite.pri) 12 | -------------------------------------------------------------------------------- /example/example-1/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 7 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 8 | #endif 9 | QGuiApplication app(argc, argv); 10 | 11 | app.setOrganizationName("smr"); 12 | app.setOrganizationDomain("smr"); 13 | app.setApplicationName("example-1"); 14 | 15 | QQmlApplicationEngine engine; 16 | // Path to module. 17 | engine.addImportPath("qrc:/"); 18 | 19 | const QUrl url(QStringLiteral("qrc:/main.qml")); 20 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, 21 | &app, [url](QObject *obj, const QUrl &objUrl) { 22 | if (!obj && url == objUrl) 23 | QCoreApplication::exit(-1); 24 | }, Qt::QueuedConnection); 25 | engine.load(url); 26 | 27 | return app.exec(); 28 | } 29 | -------------------------------------------------------------------------------- /example/example-1/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Window 2.15 3 | import QtQuick.Controls 2.15 4 | import Qt.labs.settings 1.1 5 | 6 | import SnowWhite 1.0 7 | 8 | ApplicationWindow { 9 | id: window 10 | 11 | width: toggleButton.checked ? scrollView.contentWidth : 230 12 | height: 420 13 | visible: true 14 | 15 | palette { 16 | base: "#48abff" 17 | button: "#48abff" 18 | highlight: "#5c75f4" 19 | 20 | window: '#333' 21 | text: "#eee" 22 | windowText: "#eee" 23 | 24 | buttonText: "#fff" 25 | highlightedText: "#fff" 26 | } 27 | 28 | component ButtonColor: Button { 29 | width: 20; height: width 30 | onClicked: { 31 | window.palette.base = palette.button 32 | window.palette.button = palette.button 33 | window.palette.highlight = Qt.darker(palette.button, 1.5) 34 | } 35 | } 36 | 37 | component BackColor: Button { 38 | width: 20; height: width 39 | onClicked: { 40 | window.palette.window = palette.button 41 | window.palette.windowText = palette.buttonText 42 | } 43 | } 44 | 45 | FontLoader { 46 | id: carlito 47 | source: "resources/Carlito-Regular.ttf" 48 | } 49 | 50 | Settings { 51 | id: settings 52 | fileName: 'config.conf' 53 | property alias toggleButton: toggleButton.checked 54 | property real xScroll: scrollView.xScroll 55 | } 56 | 57 | Row { 58 | x: 5; z: 3; spacing: 2 59 | AbstractButton { 60 | id: toggleButton 61 | width: 15; height: 15 62 | checkable: true 63 | font.family: 'Cascadia Code Light' 64 | contentItem: Text { 65 | text: parent.checked ? '▶◀' : '◀▶' 66 | color: palette.button 67 | } 68 | } 69 | ButtonColor { palette.button: '#48abff'; text: '.' } 70 | ButtonColor { palette.button: '#ef476f'; text: '.' } 71 | ButtonColor { palette.button: '#ffd166'; text: '.' } 72 | ButtonColor { palette.button: '#06d6a0'; text: '.' } 73 | ButtonColor { palette.button: '#d5b9ff'; text: '.' } 74 | ButtonColor { palette.button: '#343536'; text: '.' } 75 | ButtonColor { palette.button: '#edc9aa'; text: '.' } 76 | Rectangle { color: window.palette.windowText ;width: 1; height: parent.height } 77 | BackColor { palette {button: '#f5f6f7'; buttonText: '#343536'} text: '.' } 78 | BackColor { palette {button: '#343536'; buttonText: '#f5f6f7'} text: '.' } 79 | BackColor { palette {button: '#1d1c21'; buttonText: '#ffffff'} text: '.' } 80 | } 81 | 82 | ScrollView { 83 | id: scrollView 84 | 85 | property real xScroll: ScrollBar.horizontal.position 86 | Component.onCompleted: ScrollBar.horizontal.position = settings.xScroll 87 | 88 | anchors.fill: parent 89 | 90 | contentWidth: 690 91 | contentHeight: height 92 | 93 | Grid { 94 | flow: Grid.LeftToRight 95 | 96 | rows: flow == Grid.LeftToRight ? 1 : -1 97 | columns: flow == Grid.LeftToRight ? -1 : 1 98 | 99 | padding: 25 100 | spacing: 25 101 | 102 | Column { 103 | spacing: 10 104 | Frame { 105 | Grid { 106 | rows: -1 107 | columns: 3 108 | spacing: 10 109 | Button { 110 | width: 55 111 | height: width 112 | text: "Color\npicker" 113 | font.family: carlito.name 114 | onClicked: colorPickerW.visible = true 115 | } 116 | 117 | Button { 118 | width: 55 119 | height: width 120 | text: "HL\nBtn" 121 | highlighted: true 122 | font { family: carlito.name; weight: Font.Medium; } 123 | } 124 | 125 | Button { 126 | width: 55 127 | height: width 128 | text: "Check\nBtn" 129 | checkable: true 130 | font.family: carlito.name 131 | } 132 | 133 | Button { 134 | width: 55 135 | height: width 136 | text: "Flat Btn" 137 | flat: true 138 | font.family: carlito.name 139 | } 140 | 141 | Button { 142 | width: 40 143 | height: width 144 | text: "Disabled\nBtn" 145 | enabled: false 146 | font.family: carlito.name 147 | } 148 | 149 | Button { 150 | width: 40 151 | height: width 152 | highlighted: true 153 | text: "w-40" 154 | font.family: carlito.name 155 | } 156 | } 157 | } 158 | 159 | Button { 160 | width: parent.width 161 | height: 45 162 | text: "Wide Checkable Button" 163 | checkable: true 164 | font.family: carlito.name 165 | radius: 10 166 | } 167 | 168 | Row { 169 | CheckBox { 170 | text: "Check\nbox" 171 | tristate: true 172 | font.family: carlito.name 173 | } 174 | 175 | Switch { 176 | text: "Switch" 177 | font.family: carlito.name 178 | } 179 | } 180 | 181 | Row { 182 | RadioButton { 183 | text: "Radio 1" 184 | font.family: carlito.name 185 | } 186 | 187 | RadioButton { 188 | text: "Radio 2" 189 | font.family: carlito.name 190 | } 191 | } 192 | 193 | Row { 194 | spacing: 5 195 | 196 | Text { 197 | height: 85 198 | text: "Busy Indicator \u2192" 199 | color: window.palette.windowText 200 | verticalAlignment: Text.AlignVCenter 201 | font { family: carlito.name } 202 | } 203 | 204 | Rectangle { 205 | width: 85; height: 85 206 | color: 'transparent' 207 | radius: 6 208 | border.width: 1 209 | border.color: Qt.darker(window.palette.base, 1.1) 210 | 211 | BusyIndicator { 212 | anchors.centerIn: parent 213 | width: 30 214 | height: 30 215 | running: false 216 | visible: true 217 | 218 | MouseArea { 219 | anchors.fill: parent 220 | onClicked: parent.running ^= true 221 | } 222 | } 223 | } 224 | } 225 | } 226 | 227 | Rectangle { 228 | width: 1 229 | height: window.height - 45 230 | color: '#000' 231 | opacity: 0.1 232 | } 233 | 234 | Column { 235 | spacing: 7 236 | 237 | ProgressBar { 238 | width: 180; 239 | value: slider1.value 240 | } 241 | 242 | Slider { 243 | id: slider1 244 | width: 180; 245 | value: 0.3 246 | } 247 | 248 | RangeSlider { 249 | width: 180; 250 | } 251 | 252 | SpinBox { 253 | font.family: carlito.name 254 | to: 10 255 | } 256 | 257 | Column { 258 | spacing: 10 259 | TextArea { 260 | width: 180 261 | text: 'Simple example of \nTextArea.' 262 | font.family: carlito.name 263 | } 264 | 265 | TextField { 266 | width: 180 267 | text: 'Example text field.' 268 | font.family: carlito.name 269 | } 270 | } 271 | } 272 | 273 | Rectangle { 274 | width: 1 275 | height: window.height - 45 276 | color: '#000' 277 | opacity: 0.1 278 | } 279 | 280 | Column { 281 | spacing: 5 282 | Text { 283 | width: parent.width 284 | text: "Dial" 285 | color: window.palette.windowText 286 | horizontalAlignment: Text.AlignHCenter 287 | font.family: carlito.name 288 | } 289 | 290 | Dial { 291 | x: (parent.width - width)/2 292 | width: 100 293 | height: 100 294 | } 295 | 296 | Text { 297 | topPadding: 10 298 | width: parent.width 299 | horizontalAlignment: Text.AlignHCenter 300 | 301 | text: "Tumbler" 302 | color: window.palette.windowText 303 | font.family: carlito.name 304 | } 305 | 306 | Tumbler { 307 | x: (parent.width - width)/2 308 | model: 40 309 | height: 75 310 | visibleItemCount: 3 311 | } 312 | 313 | Text { 314 | topPadding: 10 315 | width: parent.width 316 | horizontalAlignment: Text.AlignHCenter 317 | text: "ComboBox" 318 | color: window.palette.windowText 319 | font.family: carlito.name 320 | } 321 | 322 | 323 | ComboBox { 324 | id: comboBox 325 | model: 5 326 | editable: true 327 | } 328 | } 329 | } 330 | } 331 | 332 | Window { 333 | id: colorPickerW 334 | ColorPicker { 335 | id: colorPicker; 336 | onSelectedColorChanged: { 337 | window.palette.base = selectedColor 338 | window.palette.button = selectedColor 339 | window.palette.highlight = Qt.darker(selectedColor, 1.5) 340 | window.palette.buttonText = selectedColor.hslLightness < 0.7 ? '#fff' : '#333' 341 | } 342 | } 343 | title: '' 344 | flags: Qt.Tool 345 | minimumWidth: colorPicker.width 346 | minimumHeight: 200 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /example/example-1/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | qtquickcontrols2.conf 5 | ColorPicker.qml 6 | resources/Carlito-Regular.ttf 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/example-1/qtquickcontrols2.conf: -------------------------------------------------------------------------------- 1 | ; This file can be edited to change the style of the application 2 | ; Read "Qt Quick Controls 2 Configuration File" for details: 3 | ; https://doc.qt.io/qt/qtquickcontrols2-configuration.html 4 | 5 | -------------------------------------------------------------------------------- /example/example-1/resources/Carlito-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0smr/snow-white/1afa8a2d6e9bb9ecf7c30443e66d3948dc8aea5f/example/example-1/resources/Carlito-Regular.ttf -------------------------------------------------------------------------------- /example/example.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = \ 3 | example-1 \ 4 | -------------------------------------------------------------------------------- /extra/preview/preview-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0smr/snow-white/1afa8a2d6e9bb9ecf7c30443e66d3948dc8aea5f/extra/preview/preview-1.webp -------------------------------------------------------------------------------- /extra/preview/preview-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0smr/snow-white/1afa8a2d6e9bb9ecf7c30443e66d3948dc8aea5f/extra/preview/preview-2.webp -------------------------------------------------------------------------------- /snow-white.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = \ 3 | SnowWhite \ 4 | example 5 | 6 | example.depends = SnowWhite 7 | --------------------------------------------------------------------------------