├── .gitignore
├── Image
├── Image.pri
├── fire.png
├── demo_1.png
├── demo_2.png
├── updown.png
├── volume-medium.png
└── img.qrc
├── Tools
└── Tools.pri
├── CustomComponent
├── CustomCircularViewDemo.qml
├── CustomItemShotDemo.qml
├── CustomButton2.qml
├── CustomVolumeSliderDemo.qml
├── CustomCircleProgressBar.qml
├── CustomButton.qml
├── CustomLoading4.qml
├── CustomSwitchButton.qml
├── CustomVolumeSlider.qml
├── CustomCircularView.qml
├── CustomLoading.qml
├── CustomLoading2.qml
├── CustomLoading3.qml
├── CustomDateRange.qml
├── CustomDesktopTip.qml
└── CustomItemShot.qml
├── README.md
├── BasicComponent
├── QmlInnerShadow.qml
├── QmlDropShadow.qml
├── BasicToolBar.qml
├── BasicToolSeparator.qml
├── BasicLabel.qml
├── BasicBusyIndicator.qml
├── BasicMenuBar.qml
├── BasicMenuSeparator.qml
├── BasicProgressBar.qml
├── QmlIconLabel.qml
├── BasicTabBar.qml
├── BasicTabButton.qml
├── BasicToolButton.qml
├── BasicMenuBarItem.qml
├── BasicScrollView.qml
├── BasicTextEdit.qml
├── BasicMenu.qml
├── BasicScrollBar.qml
├── BasicTextInput.qml
├── BasicMenuItem.qml
├── BasicTumbler.qml
├── BasicSwitch.qml
├── BasicDialog.qml
├── BasicTextArea.qml
├── GradientButton.qml
├── BasicRadioButton.qml
├── BasicDelayButton.qml
├── BasicTextField.qml
├── GradientCheckBox.qml
├── BasicSlider.qml
├── BasicCheckBox.qml
├── BasicSpinBox.qml
├── BasicButton.qml
├── GradientComboBox.qml
└── BasicComboBox.qml
├── main.qml
├── QmlComponentStyle.pro
├── main.cpp
├── qml.qrc
├── CustomComponentDemo.qml
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pro.user
2 | /bin
3 |
--------------------------------------------------------------------------------
/Image/Image.pri:
--------------------------------------------------------------------------------
1 | RESOURCES += $$PWD/img.qrc
2 |
--------------------------------------------------------------------------------
/Tools/Tools.pri:
--------------------------------------------------------------------------------
1 | HEADERS +=
2 |
3 | SOURCES +=
4 |
--------------------------------------------------------------------------------
/Image/fire.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongjianbo/QmlComponentStyle/HEAD/Image/fire.png
--------------------------------------------------------------------------------
/Image/demo_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongjianbo/QmlComponentStyle/HEAD/Image/demo_1.png
--------------------------------------------------------------------------------
/Image/demo_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongjianbo/QmlComponentStyle/HEAD/Image/demo_2.png
--------------------------------------------------------------------------------
/Image/updown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongjianbo/QmlComponentStyle/HEAD/Image/updown.png
--------------------------------------------------------------------------------
/Image/volume-medium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongjianbo/QmlComponentStyle/HEAD/Image/volume-medium.png
--------------------------------------------------------------------------------
/Image/img.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | fire.png
4 | volume-medium.png
5 | demo_1.png
6 | demo_2.png
7 | updown.png
8 |
9 |
10 |
--------------------------------------------------------------------------------
/CustomComponent/CustomCircularViewDemo.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 |
3 | // 轮播图展示
4 | Item {
5 | id: control
6 | width: 300
7 | height: 100
8 | CustomCircularView {
9 | anchors.fill: parent
10 | model: ["red", "green", "blue", "yellow", "purple"]
11 | delegate: Rectangle {
12 | width: control.width
13 | height: control.height
14 | color: modelData
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QmlComponentStyle
2 |
3 | - QML Component Style Demo Based on Qt5.15. QML组件样式Demo,以Qt5.15为基础版本。
4 |
5 | # Environment (开发环境)
6 |
7 | (2025-08-08)Win10/Win11 64bit + Qt5.15.2 + MSVC2019/MSVC2022 32bit/64bit
8 |
9 | # Warning(注意):
10 |
11 | - It is mainly to learn QML component style customization and lack of encapsulation.(主要是学习QMl组件样式自定义,欠缺封装。)
12 |
13 | - Mirror state is generally not considered in the code.(代码中一般没有考虑mirror(水平翻转)状态。)
14 |
15 | # Demo Show(展示):
16 |
17 | 
18 |
19 | 
20 |
--------------------------------------------------------------------------------
/BasicComponent/QmlInnerShadow.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtGraphicalEffects 1.15
3 |
4 | // 内阴影矩形
5 | // 龚建波 2025-07-25
6 | Rectangle {
7 | id: control
8 |
9 | // 右+下偏移
10 | property int shadowOffset: 2
11 | // 值越大越浅和远
12 | property int shadowRadius: 8
13 | // 阴影颜色
14 | property color shadowColor: "#33333333"
15 |
16 | color: "white"
17 | radius: 8
18 | layer.enabled: true
19 | layer.effect: InnerShadow {
20 | horizontalOffset: control.shadowOffset
21 | verticalOffset: control.shadowOffset
22 | radius: control.shadowRadius
23 | samples: 16
24 | color: control.shadowColor
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/BasicComponent/QmlDropShadow.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtGraphicalEffects 1.15
3 |
4 | // 外阴影矩形
5 | // 龚建波 2025-07-25
6 | Rectangle {
7 | id: control
8 |
9 | // 右+下偏移
10 | property int shadowOffset: 2
11 | // 值越大越浅和远
12 | property int shadowRadius: 8
13 | // 阴影颜色
14 | property color shadowColor: "#33333333"
15 |
16 | radius: 0
17 | layer.enabled: true
18 | layer.effect: DropShadow {
19 | transparentBorder: true
20 | horizontalOffset: control.shadowOffset
21 | verticalOffset: control.shadowOffset
22 | radius: control.shadowRadius
23 | samples: 16
24 | color: control.shadowColor
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Window 2.15
3 | import QtQuick.Controls 2.15
4 |
5 | ApplicationWindow {
6 | id: root
7 | visible: true
8 | width: 900
9 | height: 720
10 | title: "Qml Component Style: by GongJianBo"
11 |
12 | Loader {
13 | id: root_loader
14 | anchors.fill: parent
15 | source: "qrc:/BasicComponentDemo.qml"
16 | }
17 |
18 | menuBar: MenuBar {
19 | Menu {
20 | title: "Demo List"
21 | Action {
22 | text: "Basic"
23 | onTriggered: root_loader.setSource("qrc:/BasicComponentDemo.qml")
24 | }
25 | Action {
26 | text: "Custom"
27 | onTriggered: root_loader.setSource("qrc:/CustomComponentDemo.qml")
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/QmlComponentStyle.pro:
--------------------------------------------------------------------------------
1 | QT += quick
2 | QT += qml
3 | QT += quickcontrols2
4 |
5 | CONFIG += c++17
6 | CONFIG += utf8_source
7 | # msvc {
8 | # QMAKE_CFLAGS += /utf-8
9 | # QMAKE_CXXFLAGS += /utf-8
10 | # }
11 |
12 | CONFIG(debug, debug|release){
13 | # debug setting
14 | } else {
15 | # release setting
16 | }
17 |
18 | DESTDIR = $$PWD/bin
19 |
20 | # The following define makes your compiler emit warnings if you use
21 | # any Qt feature that has been marked deprecated (the exact warnings
22 | # depend on your compiler). Refer to the documentation for the
23 | # deprecated API to know how to port your code away from it.
24 | DEFINES += QT_DEPRECATED_WARNINGS
25 |
26 | SOURCES += \
27 | main.cpp
28 |
29 | RESOURCES += qml.qrc
30 |
31 | OTHER_FILES += \
32 | LICENSE \
33 | README.md
34 |
35 | INCLUDEPATH += $$PWD/Tools
36 | include($$PWD/Tools/Tools.pri)
37 |
38 | INCLUDEPATH += $$PWD/Image
39 | include($$PWD/Image/Image.pri)
40 |
--------------------------------------------------------------------------------
/BasicComponent/BasicToolBar.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 |
4 | // Qt5工具栏
5 | // 龚建波 2025-05-21
6 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ToolBar.qml
7 | T.ToolBar {
8 | id: control
9 |
10 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
11 | // 定义主题颜色
12 | property color themeColor: "darkCyan"
13 | // 定义背景颜色
14 | property color backgroundColor: themeColor
15 |
16 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
17 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
18 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
19 | // contentWidth + leftPadding + rightPadding)
20 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
21 | // contentHeight + topPadding + bottomPadding)
22 | implicitWidth: contentWidth + leftPadding + rightPadding
23 | implicitHeight: 30
24 |
25 | // 背景
26 | background: Rectangle {
27 | color: control.backgroundColor
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/BasicComponent/BasicToolSeparator.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 |
4 | // Qt5工具栏分隔线
5 | // 龚建波 2025-05-21
6 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ToolSeparator.qml
7 | T.ToolSeparator {
8 | id: control
9 |
10 | // 定义分割线颜色
11 | property color separatorColor: "white"
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 | // 如果是竖向左右留更大边距,如果是横向上下留更大边距
19 | padding: vertical ? 6 : 2
20 | verticalPadding: vertical ? 2 : 6
21 |
22 | // 分割线色块
23 | contentItem: Rectangle {
24 | // 如果是竖向宽度为1,如果是横向高度为1
25 | implicitWidth: vertical ? 1 : 20
26 | implicitHeight: vertical ? 20 : 1
27 | color: control.separatorColor
28 | opacity: 0.5
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/CustomComponent/CustomItemShotDemo.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 |
4 | // 截图展示
5 | Rectangle {
6 | id: control
7 |
8 | width: 300
9 | height: 300
10 |
11 | Rectangle {
12 | id: target
13 | anchors.fill: parent
14 | anchors.topMargin: 30
15 | border.width: 50
16 | border.color: "purple"
17 | color: "orange"
18 |
19 | Rectangle {
20 | width: 80
21 | height: 200
22 | anchors.centerIn: parent
23 | color: "cyan"
24 | }
25 | }
26 |
27 | // 对Item截图
28 | CustomItemShot {
29 | id: shot_area
30 | shotTarget: target // 要抓取的Item
31 | anchors.fill: target // 范围
32 | anchors.margins: 1
33 | visible: false
34 | }
35 |
36 | Button {
37 | width: 90
38 | height: 30
39 | text: "Shot"
40 | onClicked: {
41 | if (shot_area.visible) {
42 | shot_area.close()
43 | } else {
44 | shot_area.pop()
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/CustomComponent/CustomButton2.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Templates 2.15 as T
4 |
5 | // 自定义按钮
6 | T.AbstractButton {
7 | id: control
8 |
9 | text: qsTr("Button")
10 | property color textColor: down ? "white" : "black"
11 | property color color: down
12 | ? "#444"
13 | : hovered
14 | ? "#888"
15 | : "#666"
16 | property color borderColor: "black"
17 |
18 | implicitWidth: 90
19 | implicitHeight: 30
20 |
21 | font {
22 | family: "SimSun"
23 | pixelSize: 16
24 | }
25 |
26 | contentItem: Text {
27 | text: control.text
28 | color: control.textColor
29 | verticalAlignment: Text.AlignVCenter
30 | horizontalAlignment: Text.AlignHCenter
31 | renderType: Text.NativeRendering
32 | elide: Text.ElideRight
33 | padding: 5
34 | font: control.font
35 | }
36 |
37 | background: Rectangle {
38 | color: control.color
39 | border.color: borderColor
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/CustomComponent/CustomVolumeSliderDemo.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 |
3 | // 音量调节展示
4 | Rectangle {
5 | id: control
6 | width: 100
7 | height: 300
8 | color: "black"
9 | rotation: 0
10 | Column {
11 | anchors.centerIn: parent
12 | spacing: 16
13 | // 把slider和image拆分开是为了之后可以做成一个按钮式的
14 | // 点击image再弹slider出来
15 | CustomVolumeSlider {
16 | anchors.horizontalCenter: parent.horizontalCenter
17 | slider{
18 | from: 0
19 | to: 100
20 | stepSize: 1
21 | value: 45
22 | }
23 | }
24 | // Image from
25 | // https://www.iconfont.cn/collections/detail?spm=a313x.7781069.0.da5a778a4&cid=328
26 | Image {
27 | width: 32
28 | height: 32
29 | rotation: -control.rotation
30 | // 找这个icon是左对齐的,心塞
31 | // 我只下了这一个图,本来有好几个状态
32 | // anchors.horizontalCenterOffset: 10
33 | anchors.horizontalCenter: parent.horizontalCenter
34 | source: "qrc:/volume-medium.png"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/BasicComponent/BasicLabel.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Templates 2.15 as T
4 |
5 | // Qt5标签样式自定义
6 | // 龚建波 2025-04-23
7 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Label.qml
8 | // Label继承自Text,只是增加了padding/inset/background等没啥用的属性
9 | T.Label {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 |
16 | // 文本颜色
17 | color: mouse_area.containsMouse ? Qt.lighter(themeColor) : "black"
18 | // 边距
19 | padding: 3
20 | // 字体设置
21 | // 也可以给QApplication设置全局的默认字体
22 | font{
23 | family: "SimSun"
24 | pixelSize: 16
25 | }
26 | // 单独设置文本组件的渲染方式
27 | renderType: Text.NativeRendering
28 | // 文字对齐方式,设置Text宽高后设置才有意义
29 | verticalAlignment: Text.AlignVCenter
30 | horizontalAlignment: Text.AlignHCenter
31 | // 宽度不够时显示省略号
32 | elide: Text.ElideRight
33 | // html href链接颜色
34 | linkColor: mouse_area.containsMouse ? Qt.lighter(themeColor) : themeColor
35 |
36 | // 获取鼠标hover状态
37 | MouseArea {
38 | id: mouse_area
39 | anchors.fill: parent
40 | hoverEnabled: true
41 | acceptedButtons: Qt.NoButton
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | // QT += quickcontrols2
4 | // #include
5 | // #include
6 | // #include
7 |
8 | int main(int argc, char *argv[])
9 | {
10 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
11 |
12 | // Global RenderType settings(全局的RenderType设置)
13 | // 使用NativeTextRendering和QPainter绘制效果差不多,QtTextRendering有抗锯齿低分屏没那么清晰
14 | // 对单个Text设置renderType: Text.NativeRendering比较麻烦
15 | // QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering);
16 |
17 | // About MSVC Chinese characters(关于MSVC中文字符)
18 | // 测试:https://blog.csdn.net/gongjianbo1992/article/details/104078327
19 | // qDebug() << "中文测试";
20 |
21 | QGuiApplication app(argc, argv);
22 | // 可以修改QtQuick.Controls组件库的默认样式,但是比较简陋,可以用到一些对UI要求不高的场景
23 | // QQuickStyle::setStyle("Material");
24 |
25 | QQmlApplicationEngine engine;
26 | const QUrl url(QStringLiteral("qrc:/main.qml"));
27 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
28 | &app, [url](QObject *obj, const QUrl &objUrl) {
29 | if (!obj && url == objUrl)
30 | QCoreApplication::exit(-1);
31 | }, Qt::QueuedConnection);
32 | engine.load(url);
33 |
34 | return app.exec();
35 | }
36 |
--------------------------------------------------------------------------------
/BasicComponent/BasicBusyIndicator.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Templates 2.15 as T
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5繁忙等待指示器
7 | // 龚建波 2025-04-16
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\BusyIndicator.qml
9 | // 继承自Control,只增加了一个running动画运行状态属性,样式除了颜色其他没法自定义
10 | T.BusyIndicator {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义单个圆点边框颜色
17 | property color indicatorBorderColor: Qt.lighter(themeColor)
18 | // 定义单个圆点填充色
19 | property color indicatorFillColor: themeColor
20 |
21 | // 默认宽度
22 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
23 | // 默认高度
24 | implicitHeight: implicitContentHeight + topPadding + bottomPadding
25 | // 边距
26 | padding: 6
27 |
28 | // 指示器部件
29 | contentItem: BusyIndicatorImpl {
30 | implicitWidth: 80
31 | implicitHeight: 80
32 | // 单个圆点边框颜色
33 | pen: control.indicatorBorderColor
34 | // 单个圆圈填充色
35 | fill: control.indicatorFillColor
36 | // 等待动画运行状态
37 | running: control.running
38 | // 参考源码写法,非running时不显示
39 | opacity: control.running ? 1 : 0
40 | // 显示隐藏透明度过渡动画
41 | Behavior on opacity {
42 | OpacityAnimator { duration: 250 }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/CustomComponent/CustomCircleProgressBar.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtGraphicalEffects 1.15
3 |
4 | // 环形进度条,使用锥型渐变实现
5 | ConicalGradient {
6 | id: control
7 | width: 200
8 | height: 200
9 |
10 | property int barWidth: 20
11 | property color foregroundColor: Qt.rgba(0, 0.5, 0, 1)
12 | property color backgroundColor: Qt.rgba(0.7, 0.7, 0.7, 1)
13 | property color textColor: Qt.rgba(0, 0.5, 0, 1)
14 | property double minValue: 0
15 | property double maxValue: 100
16 | property double value: 0
17 | property double __progress: value / (maxValue - minValue)
18 |
19 | smooth: true
20 | antialiasing: true
21 | source: Rectangle {
22 | width: control.width
23 | height: control.height
24 | color: "transparent"
25 | border.color: control.backgroundColor
26 | border.width: control.barWidth
27 | radius: width / 2
28 | }
29 |
30 | gradient: Gradient {
31 | GradientStop { position: 0.0; color: control.foregroundColor }
32 | GradientStop { position: control.__progress; color: control.foregroundColor }
33 | GradientStop { position: control.__progress + 0.00001; color: control.backgroundColor }
34 | GradientStop { position: 1.00001; color: control.backgroundColor }
35 | }
36 |
37 | // 具体的设置可以再单独引出来
38 | Text {
39 | anchors.centerIn: parent
40 | text: (control.__progress * 100).toFixed(2) + " %"
41 | font.pixelSize: 20
42 | color: control.textColor
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/BasicComponent/BasicMenuBar.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5菜单栏
7 | // 龚建波 2025-07-25
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\MenuBar.qml
9 | T.MenuBar {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 | // 定义背景颜色
16 | property color backgroundColor: "white"
17 | // 定义边框颜色
18 | property color borderColor: themeColor
19 |
20 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
21 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
22 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
23 | // contentWidth + leftPadding + rightPadding)
24 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
25 | // contentHeight + topPadding + bottomPadding)
26 | implicitWidth: contentWidth + leftPadding + rightPadding
27 | implicitHeight: 30
28 | // 传递到按钮列表,作为按钮间隔
29 | spacing: 1
30 |
31 | // 菜单项样式
32 | delegate: BasicMenuBarItem { }
33 |
34 | // 按钮列表
35 | contentItem: Row {
36 | spacing: control.spacing
37 | Repeater {
38 | model: control.contentModel
39 | }
40 | }
41 |
42 | // 背景
43 | background: Rectangle {
44 | color: control.backgroundColor
45 | // 自定义样式,底部一条横线
46 | Rectangle {
47 | color: control.borderColor
48 | width: parent.width
49 | height: 1
50 | anchors.bottom: parent.bottom
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/BasicComponent/BasicMenuSeparator.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5菜单分割线
7 | // 龚建波 2025-07-25
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\MenuSeparator.qml
9 | T.MenuSeparator {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | // MenuSeparator没有附加属性来访问该对象,只能往父节点找
15 | property color themeColor: {
16 | var p = control.parent
17 | while (p) {
18 | if (p instanceof BasicMenu) {
19 | if ("themeColor" in p)
20 | return p.themeColor
21 | else
22 | break
23 | }
24 | p = p.parent
25 | }
26 | return "darkCyan"
27 | }
28 | // 定义背景颜色
29 | property color backgroundColor: "white"
30 | // 定义分割线颜色
31 | property color contentColor: themeColor
32 |
33 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
34 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
35 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
36 | implicitContentWidth + leftPadding + rightPadding)
37 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
38 | implicitContentHeight + topPadding + bottomPadding)
39 | // 边距
40 | padding: 0
41 | topPadding: 4
42 | bottomPadding: 4
43 | // 内容
44 | contentItem: Rectangle {
45 | implicitWidth: 160
46 | implicitHeight: 1
47 | color: contentColor
48 | }
49 | // 背景
50 | background: Rectangle {
51 | color: backgroundColor
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/CustomComponent/CustomButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 |
3 | // 自定义按钮
4 | Rectangle {
5 | id:control
6 |
7 | property alias text: control_text.text
8 | property alias font: control_text.font
9 | property alias textColor: control.color
10 | property alias content: control_text
11 | property alias mousearea: control_mousearea
12 |
13 | signal entered()
14 | signal exited()
15 | signal canceled()
16 | signal clicked()
17 | signal doubleClicked()
18 | signal pressAndHold()
19 | signal pressed()
20 | signal released()
21 |
22 | implicitWidth: 90
23 | implicitHeight: 30
24 |
25 | // white:fff, black:000
26 | color: control_mousearea.pressed
27 | ? "#444"
28 | : control_mousearea.containsMouse
29 | ? "#888"
30 | : "#666"
31 | border {
32 | color: "black"
33 | width: 1
34 | }
35 |
36 | Text {
37 | id: control_text
38 | anchors.fill: parent
39 | text: qsTr("Button")
40 | color: control_mousearea.pressed ? "white" : "black"
41 | verticalAlignment: Text.AlignVCenter
42 | horizontalAlignment: Text.AlignHCenter
43 | renderType: Text.NativeRendering
44 | elide: Text.ElideRight
45 | padding: 5
46 | font {
47 | family: "SimSun"
48 | pixelSize: 16
49 | }
50 | }
51 |
52 | MouseArea {
53 | id: control_mousearea
54 | anchors.fill: parent
55 | hoverEnabled: true
56 |
57 | onEntered: control.entered()
58 | onExited: control.exited()
59 | onCanceled: control.canceled()
60 | onClicked: control.clicked()
61 | onDoubleClicked: control.doubleClicked()
62 | onPressAndHold: control.pressAndHold()
63 | onPressed: control.pressed()
64 | onReleased: control.released()
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/BasicComponent/BasicProgressBar.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5进度条
7 | // 龚建波 2025-04-16
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ProgressBar.qml
9 | T.ProgressBar {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 |
16 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
17 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
18 | // inset和padding都是Control基类定义的,默认为0
19 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
20 | // implicitContentWidth + leftPadding + rightPadding)
21 | // 默认高度
22 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
23 | // implicitContentHeight + topPadding + bottomPadding)
24 | implicitWidth: 200
25 | implicitHeight: 10
26 |
27 | // 进度起始值,默认0.0
28 | // from: 0
29 | // 进度最大值,默认1.0
30 | // to: 100
31 | // 当前进度之,默认0.0
32 | // value: 50
33 | // 进度逻辑位置[0.0, 1.0] postion
34 | // 进度可视位置 visualPosition
35 | // 是否为不确定模式 indeterminate
36 | // 不确定模式下的进度条显示作正在进行中(类似BusyIndicator),但不显示已取得的进度
37 |
38 | // 进度条部件,可惜不能设置圆角
39 | contentItem: ProgressBarImpl {
40 | implicitWidth: 120
41 | implicitHeight: 10
42 | scale: control.mirrored ? -1 : 1
43 | // 进度
44 | progress: control.position
45 | // indeterminate表示没有具体进度值,是一个进度动画
46 | indeterminate: control.visible && control.indeterminate
47 | color: themeColor
48 | }
49 |
50 | // 背景框
51 | background: Rectangle {
52 | implicitWidth: 200
53 | implicitHeight: 10
54 | y: (control.height - height) / 2
55 | height: 10
56 | border.width: 1
57 | border.color: themeColor
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/BasicComponent/QmlIconLabel.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 |
5 | // QtQuick.control2带Icon的Label是个单独的控件:IconLabel
6 | // 但是自定义起来不大方便,比如没有renderType那就只能全局设置该属性
7 | // 而IconLabel源码中用的也比较多,我就单独组合一个
8 | // Item可以替换为Rectangle以设置背景色
9 | Item {
10 | id: control
11 |
12 | property alias source: _icon.source
13 | property alias text: _text.text
14 | property alias color: _text.color
15 | property alias font: _text.font
16 | property alias spacing: _row.spacing
17 | // 懒得写,直接全部引出
18 | property alias controlImage: _icon
19 | property alias controlText: _text
20 | property alias controlRow: _row
21 |
22 | implicitWidth: ((_icon.source && _text.implicitWidth) ? _row.spacing : 0)
23 | + _icon.implicitWidth + _text.implicitWidth
24 | + _row.leftPadding + _row.rightPadding
25 | implicitHeight: 30
26 | Row {
27 | id: _row
28 | // 图标和文本居中并排的话可以anchors.centerIn: parent
29 | height: parent.height
30 | width: parent.width
31 | spacing: 6
32 | padding: 0
33 | // leftPadding: 6
34 | // rightPadding: 6
35 |
36 | ColorImage {
37 | id: _icon
38 | anchors{
39 | verticalCenter: parent.verticalCenter
40 | }
41 | color: _text.color
42 | }
43 | Text {
44 | id: _text
45 | // 如果组件宽度固定,那文字区域宽度也是固定的,这样图标的位置也是固定的
46 | // 就不会因为文字长短不齐导致图标位置不固定了
47 | width: control.width - _icon.width - _row.leftPadding - _row.rightPadding
48 | anchors{
49 | verticalCenter: parent.verticalCenter
50 | }
51 | color: "black"
52 | renderType: Text.NativeRendering
53 | verticalAlignment: Text.AlignVCenter
54 | horizontalAlignment: Text.AlignHCenter
55 | // wrapMode: Text.NoWrap
56 | elide: Text.ElideRight
57 | font{
58 | family: "SimSun"
59 | pixelSize: 16
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/BasicComponent/BasicTabBar.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 |
4 | // Qt5导航栏/标签栏
5 | // 龚建波 2025-05-21
6 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\TabBar.qml
7 | T.TabBar {
8 | id: control
9 |
10 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
11 | // 定义主题颜色
12 | property color themeColor: "darkCyan"
13 | // 定义背景颜色
14 | property color backgroundColor: "white"
15 | // 定义边框颜色
16 | property color borderColor: themeColor
17 |
18 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
19 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
20 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
21 | // contentWidth + leftPadding + rightPadding)
22 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
23 | // contentHeight + topPadding + bottomPadding)
24 | implicitWidth: contentWidth + leftPadding + rightPadding
25 | implicitHeight: 30
26 | // 传递到按钮列表,作为按钮间隔
27 | spacing: 1
28 |
29 | // 按钮列表
30 | contentItem: ListView {
31 | model: control.contentModel
32 | currentIndex: control.currentIndex
33 | // 按钮间隔
34 | spacing: control.spacing
35 | // 横向
36 | orientation: ListView.Horizontal
37 | // 内容无法拖拽超出可滑动区域的边界,且快速滑动时不会回弹效果
38 | boundsBehavior: Flickable.StopAtBounds
39 | // content大于view大小时,允许滑动
40 | flickableDirection: Flickable.AutoFlickIfNeeded
41 | // 滑动结束时起点对齐
42 | snapMode: ListView.SnapToItem
43 |
44 | // 高亮时缓动动画用时
45 | highlightMoveDuration: 0
46 | // 尽量让高亮项保持在指定范围内。但是,突出显示可能会移动到列表末尾的范围之外,或者由于鼠标交互而移动。
47 | highlightRangeMode: ListView.ApplyRange
48 | // 高亮区间
49 | preferredHighlightBegin: 40
50 | preferredHighlightEnd: width - 40
51 | }
52 |
53 | // 背景
54 | background: Rectangle {
55 | color: control.backgroundColor
56 | // 自定义样式,底部一条横线
57 | Rectangle {
58 | color: control.borderColor
59 | width: parent.width
60 | height: 1
61 | anchors.bottom: parent.bottom
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/CustomComponent/CustomLoading4.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 |
4 | // 自定义loading效果
5 | // 龚建波 2022-02-08
6 | Item {
7 | id: control
8 |
9 | // item的颜色
10 | property color itemColor: "#0486FF"
11 | // 当前旋转的x轴还是y轴
12 | property int _transX: 0
13 | // 当前旋转的角度,0-180后切换到另一个轴旋转
14 | property double _transRotate: 0
15 | //
16 | property bool running: visible
17 |
18 | implicitHeight: 160
19 | implicitWidth: 160
20 |
21 | // 动画
22 | SequentialAnimation {
23 | loops: Animation.Infinite
24 | running: control.running
25 | NumberAnimation {
26 | target: control
27 | property: "_transRotate"
28 | from: 0
29 | to: 180
30 | duration: 1000
31 | }
32 | NumberAnimation {
33 | target: control
34 | property: "_transX"
35 | from: 0
36 | to: 1
37 | duration: 200
38 | }
39 | NumberAnimation {
40 | target: control
41 | property: "_transRotate"
42 | from: 180
43 | to: 0
44 | duration: 1000
45 | }
46 | NumberAnimation {
47 | target: control
48 | property: "_transX"
49 | from: 1
50 | to: 0
51 | duration: 200
52 | }
53 | }
54 |
55 | Item {
56 | id: content
57 | anchors.fill: parent
58 | anchors.margins: 8
59 |
60 | // 翻转的方块
61 | Rectangle {
62 | id: rect
63 | width: content.width
64 | height: content.height
65 | color: control.itemColor
66 | antialiasing: true
67 | transform: [
68 | Rotation {
69 | angle: control.running ? control._transRotate : 0
70 | axis{
71 | x: control._transX ? 1 : 0
72 | y: control._transX ? 0 : 1
73 | z: 0
74 | }
75 | origin{
76 | x: rect.width / 2
77 | y: rect.height / 2
78 | }
79 | }
80 | ]
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/BasicComponent/BasicTabButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Templates 2.15 as T
4 |
5 | // Qt5导航/标签按钮
6 | // 龚建波 2025-05-21
7 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\TabButton.qml
8 | T.TabButton {
9 | id: control
10 |
11 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
12 | // 定义主题颜色
13 | property color themeColor: TabBar.tabBar && TabBar.tabBar.themeColor
14 | ? TabBar.tabBar.themeColor
15 | : "darkCyan"
16 | // 定义文本颜色
17 | property color textColor: "white"
18 | // 定义背景颜色
19 | // pressed按下,hovered鼠标悬停,highlighted高亮,checked选中
20 | property color backgroundColor: (control.pressed || control.checked)
21 | ? Qt.darker(themeColor)
22 | : (control.hovered || control.highlighted)
23 | ? Qt.lighter(themeColor)
24 | : themeColor
25 |
26 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
27 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
28 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
29 | // implicitContentWidth + leftPadding + rightPadding)
30 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
31 | // implicitContentHeight + topPadding + bottomPadding)
32 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
33 | implicitHeight: 30
34 | // 边距
35 | padding: 0
36 | leftPadding: 12
37 | rightPadding: 12
38 | // 图标和文字间隔
39 | spacing: 6
40 | // 字体设置
41 | // 也可以给QApplication设置全局的默认字体
42 | font{
43 | family: "SimSun"
44 | pixelSize: 16
45 | }
46 |
47 | // 带图标的Label
48 | // 自定义的QmlIconLabel,参考源码IconLabel
49 | contentItem: QmlIconLabel {
50 | text: control.text
51 | font: control.font
52 | color: control.textColor
53 | spacing: control.spacing
54 | source: control.icon.source
55 | }
56 |
57 | // 背景
58 | background: Rectangle {
59 | // 留的1px是和自定义TabBar底部的样式配合的
60 | height: control.height - 1
61 | color: control.backgroundColor
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/BasicComponent/BasicToolButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Templates 2.15 as T
4 |
5 | // Qt5工具栏按钮
6 | // 龚建波 2025-05-22
7 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ToolButton.qml
8 | T.ToolButton {
9 | id: control
10 |
11 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
12 | // 定义主题颜色
13 | // ToolBar没有附加属性来访问该对象,只能往父节点找
14 | property color themeColor: {
15 | var p = control.parent
16 | while (p) {
17 | if (p instanceof BasicToolBar) {
18 | if ("themeColor" in p)
19 | return p.themeColor
20 | else
21 | break
22 | }
23 | p = p.parent
24 | }
25 | return "darkCyan"
26 | }
27 |
28 | // 定义文本颜色
29 | property color textColor: "white"
30 | // pressed按下,hovered鼠标悬停,highlighted高亮,checked选中
31 | property color backgroundColor: (control.pressed || control.checked)
32 | ? Qt.darker(themeColor)
33 | : (control.hovered || control.highlighted)
34 | ? Qt.lighter(themeColor)
35 | : themeColor
36 |
37 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
38 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
39 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
40 | // implicitContentWidth + leftPadding + rightPadding)
41 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
42 | // implicitContentHeight + topPadding + bottomPadding)
43 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
44 | implicitHeight: 30
45 | // 边距
46 | padding: 0
47 | leftPadding: 12
48 | rightPadding: 12
49 | // 图标和文字间隔
50 | spacing: 6
51 | // 字体设置
52 | // 也可以给QApplication设置全局的默认字体
53 | font{
54 | family: "SimSun"
55 | pixelSize: 16
56 | }
57 |
58 | // 带图标的Label
59 | // 自定义的QmlIconLabel,参考源码IconLabel
60 | contentItem: QmlIconLabel {
61 | text: control.text
62 | font: control.font
63 | color: control.textColor
64 | spacing: control.spacing
65 | source: control.icon.source
66 | }
67 |
68 | // 背景
69 | background: Rectangle {
70 | color: control.backgroundColor
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/BasicComponent/BasicMenuBarItem.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5菜单栏选项
7 | // 龚建波 2025-07-25
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\MenuBarItem.qml
9 | T.MenuBarItem {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | // 从访问Bar可以发现QML接口设计有一些没统一,TabBar有附加属性,MenuBarItem用成员属性,ToolBar没提供接口访问
15 | property color themeColor: menuBar && menuBar.themeColor
16 | ? menuBar.themeColor
17 | : "darkCyan"
18 | // 定义文本颜色
19 | property color textColor: "white"
20 | // 定义背景色
21 | // pressed按下,hovered鼠标悬停,highlighted菜单弹出/hover
22 | property color backgroundColor: (control.pressed)
23 | ? Qt.darker(themeColor)
24 | : (control.hovered || control.highlighted)
25 | ? Qt.lighter(themeColor)
26 | : themeColor
27 |
28 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
29 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
30 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
31 | // implicitContentWidth + leftPadding + rightPadding)
32 | // 默认高度
33 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
34 | // implicitContentHeight + topPadding + bottomPadding,
35 | // implicitIndicatorHeight + topPadding + bottomPadding)
36 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
37 | implicitHeight: 30
38 | // 边距
39 | padding: 0
40 | leftPadding: 12
41 | rightPadding: 12
42 | // 图标和文字间隔
43 | spacing: 6
44 | // 字体设置
45 | // 也可以给QApplication设置全局的默认字体
46 | font{
47 | family: "SimSun"
48 | pixelSize: 16
49 | }
50 |
51 | // 带图标的Label
52 | // 自定义的QmlIconLabel,参考源码IconLabel
53 | contentItem: QmlIconLabel {
54 | text: control.text
55 | font: control.font
56 | color: control.textColor
57 | spacing: control.spacing
58 | source: control.icon.source
59 | }
60 |
61 | // 背景
62 | background: Rectangle {
63 | // 留的1px是和自定义MenuBar底部的样式配合的
64 | height: control.height - 1
65 | color: control.backgroundColor
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/CustomComponent/CustomSwitchButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 |
3 | // 滑动按钮
4 | // qml圆角抗锯齿有点糊,索性去掉
5 | Rectangle {
6 | id: control
7 |
8 | property color offBorderColor: "black"
9 | property color offButtonColor: "white"
10 | property color offBackgroundColor: "gray"
11 | property color onBorderColor: Qt.darker("green")
12 | property color onButtonColor: Qt.lighter("green")
13 | property color onBackgroundColor: "green"
14 |
15 | implicitWidth: 90
16 | implicitHeight: 30
17 |
18 | radius: height / 2
19 | border.width: 0
20 |
21 | Rectangle {
22 | id: switch_button
23 | height: parent.height
24 | width: parent.width * 2 / 3
25 | radius: height / 2
26 | border.color: parent.color
27 | border.width: 2
28 | // 本来想加个Off/On的文本在上面,貌似没必要
29 | }
30 |
31 | MouseArea {
32 | anchors.fill: parent
33 | onClicked: {
34 | control.state = (control.state == "off") ? "on" : "off"
35 | }
36 | }
37 |
38 | state: "off"
39 | states: [
40 | State {
41 | name: "off"
42 | PropertyChanges {
43 | target: control
44 | border.color: offBorderColor
45 | color: offBackgroundColor
46 | }
47 | PropertyChanges {
48 | target: switch_button
49 | color: offButtonColor
50 | x: 0
51 | }
52 | },
53 | State {
54 | name: "on"
55 | PropertyChanges {
56 | target: control
57 | border.color: onBorderColor
58 | color: onBackgroundColor
59 | }
60 | PropertyChanges {
61 | target: switch_button
62 | color: onButtonColor
63 | x: control.width - switch_button.width
64 | }
65 | }
66 | ]
67 |
68 | transitions: [
69 | Transition {
70 | from: "off"
71 | to: "on"
72 | // 按钮加渐变过渡感觉很奇怪,参照手机上的按钮就没加渐变动画
73 | // ColorAnimation { targets: [control]; duration: 300 }
74 | NumberAnimation { targets: [switch_button]; property: "x"; duration: 300 }
75 | },
76 | Transition {
77 | from: "on"
78 | to: "off"
79 | // ColorAnimation { targets: [control]; duration: 200 }
80 | NumberAnimation { targets: [switch_button]; property: "x"; duration: 200 }
81 | }
82 | ]
83 | }
84 |
--------------------------------------------------------------------------------
/BasicComponent/BasicScrollView.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5滚动区域
7 | // 龚建波 2025-05-15
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ScrollView.qml
9 | T.ScrollView {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 | // 滚动条和边框的距离,区别于padding,padding是把内容挤压
16 | property int scrollBarPadding: 1
17 |
18 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
19 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
20 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
21 | // contentWidth + leftPadding + rightPadding)
22 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
23 | // contentHeight + topPadding + bottomPadding)
24 | implicitWidth: contentWidth + leftPadding + rightPadding
25 | implicitHeight: contentHeight + topPadding + bottomPadding
26 | // 内容宽高
27 | // contentWidth/Height默认绑定的是内部Item的implicitWidth/Height
28 | // 如果Item只设置width/height不会影响contentWidth/Height值
29 | // contentWidth: 100
30 | // contentHeight: 100
31 | // 边距
32 | padding: 6
33 | // 超出范围的内容不显示
34 | clip: true
35 |
36 | // 背景
37 | // 如果内容超出范围,会压在background的border上
38 | // 可以把ScrollView fill在一个Rectangle中加margin
39 | // 或者在上层叠加四个边,或者是在上层叠加背景透明的Rectangle
40 | background: Rectangle {
41 | border.color: control.themeColor
42 | border.width: 1
43 | }
44 |
45 | // 竖向滚动条
46 | // NOTE 如果继承自ScrollView而不是T.ScrollView,那ScrollView定义中的滚动条也会显示
47 | // TODO 横向和竖向都显示的时候,交叠部分留出空白
48 | ScrollBar.vertical: BasicScrollBar {
49 | parent: control
50 | // 在view右侧,离边scrollBarPadding的距离
51 | x: control.mirrored ? control.scrollBarPadding : control.width - width - control.scrollBarPadding
52 | y: control.scrollBarPadding
53 | z: 10
54 | height: control.height - control.scrollBarPadding * 2
55 | // 横向竖向active关联起来
56 | active: control.ScrollBar.horizontal.active
57 | // 是否为交互式滚动条,如可以鼠标或触摸拖动,默认true
58 | // interactive: true
59 | themeColor: control.themeColor
60 | }
61 |
62 | // 横向滚动条
63 | ScrollBar.horizontal: BasicScrollBar {
64 | parent: control
65 | // 在view底部,离边scrollBarPadding的距离
66 | x: control.scrollBarPadding
67 | y: control.height - height - control.scrollBarPadding
68 | z: 10
69 | width: control.width - control.scrollBarPadding * 2
70 | active: control.ScrollBar.vertical.active
71 | themeColor: control.themeColor
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/BasicComponent/BasicTextEdit.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 |
3 | // Qt5富文本编辑框
4 | // 龚建波 2025-08-7
5 | // 参考:qt-everywhere-src-5.15.2\qtdeclarative\src\quick\items\qquicktextedit_p.h
6 | // TextEdit 是 TextArea 父类,是 QtQuick 基础模块的组件,而 TextArea 是 Controls 模块的
7 | // 虽然 TextEdit 支持富文本,但是又没有 maximumLength/validator 等属性,纯文本还是用 TextInput/Field 好点
8 | TextEdit {
9 | id: control
10 |
11 | // 因为没有background等属性,需要嵌套在Rectangle等组件内
12 | // 默认宽高
13 | width: 200
14 | height: 30
15 | // 边距
16 | padding: 6
17 | leftPadding: padding + 2
18 | rightPadding: padding + 2
19 | // 文本颜色
20 | color: "black"
21 | // 字体设置
22 | font{
23 | family: "SimSun"
24 | pixelSize: 16
25 | }
26 | // 截取超出部分,似乎设置了还是会超出一点,可以在外部套一层来clip
27 | // clip: true
28 | // 默认Text.QtRendering看起来比较模糊
29 | renderType: Text.NativeRendering
30 | // 只读
31 | // readOnly: true
32 | // 文本左对齐
33 | horizontalAlignment: TextEdit.AlignLeft
34 | // 文本默认顶部对齐
35 | // verticalAlignment: TextEdit.AlignVCenter
36 | // 光标样式自定义
37 | // cursorDelegate: Rectangle {
38 | // width: 2
39 | // color: "blue"
40 | // property bool cursorRuning: control.cursorVisible
41 | // visible: false
42 | // SequentialAnimation on visible {
43 | // id: cursorAnimation
44 | // running: false
45 | // loops: Animation.Infinite
46 | // PropertyAnimation { from: true; to: false; duration: 750 }
47 | // PropertyAnimation { from: false; to: true; duration: 500 }
48 | // }
49 | // onCursorRuningChanged: {
50 | // cursorAnimation.running = cursorRuning
51 | // visible = cursorRuning
52 | // }
53 | // }
54 | // cursorVisible: focus
55 | // 允许鼠标选取文本块
56 | selectByMouse: true
57 | // 选中文本的颜色
58 | selectedTextColor: "white"
59 | // 选中文本背景色
60 | selectionColor: "blue"
61 | // 新的输入覆盖光标后面的文本
62 | // overwriteMode: true
63 | // 文本换行,默认NoWrap
64 | // wrapMode: TextInput.Wrap
65 | // 内容格式,可以指定纯文本或富文本
66 | // 默认TextEdit.PlainText纯文本
67 | // 还可以设置AutoText/RichText/MarkdownText
68 | // textFormat: TextEdit.AutoText
69 |
70 | // 一些只读属性
71 | // 一些复制粘贴相关的我略去了
72 | // 文本高度
73 | // contentHeight: real
74 | // 文本宽度
75 | // contentWidth: real
76 | // 文本行数
77 | // lineCount: int
78 | // 文本字符长度
79 | // length: int
80 | // 选中的文本
81 | // selectedText: string
82 |
83 | // 信号
84 | // 当按下Return或Enter键或文本输入失去焦点时
85 | // onEditingFinished: { console.log("edit onEditingFinished") }
86 | // 鼠标单击嵌入的链接,链接必须是富文本或HTML格式
87 | // 链接如: baidu
88 | // onLinkActivated: (link)=>{ console.log("edit onLinkActivated", link) }
89 | // 鼠标悬停在嵌入的链接,链接必须是富文本或HTML格式
90 | // onLinkHovered: (link)=>{ console.log("edit onLinkHovered", link) }
91 | }
92 |
--------------------------------------------------------------------------------
/CustomComponent/CustomVolumeSlider.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtGraphicalEffects 1.0
4 |
5 | // 音量调节Slider
6 | Rectangle {
7 | id: volume
8 | implicitWidth: 50
9 | implicitHeight: 200
10 | radius: width * 2 / 5
11 | color: Qt.rgba(80 / 255, 80 / 255, 80 / 255, 1)
12 | property alias slider: control
13 |
14 | Slider {
15 | id: control
16 | anchors.centerIn: parent
17 | implicitWidth: horizontal ? 180 : 6
18 | implicitHeight: horizontal ? 6 : 180
19 | orientation: Qt.Vertical
20 | padding: 0
21 |
22 | background: Rectangle {
23 | implicitWidth: control.implicitWidth
24 | implicitHeight: control.implicitHeight
25 | color: "#25282a"
26 | // radius: 2
27 |
28 | Rectangle {
29 | y: control.horizontal ? 0 : control.visualPosition * parent.height
30 | width: control.horizontal ? control.position * parent.width : parent.width
31 | height: control.horizontal ? parent.height : control.position * parent.height
32 | color: "#2674e8"
33 | // radius: 2
34 | }
35 | }
36 |
37 | // 这个拖动按钮可以找个图片替换
38 | handle: Item {
39 | x: control.leftPadding + (control.horizontal ? control.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2)
40 | y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.visualPosition * (control.availableHeight - height))
41 | // width: control.horizontal ? control.height : control.width
42 | // height: control.horizontal ? control.height : control.width
43 | implicitWidth: 12
44 | implicitHeight: 12
45 | Rectangle {
46 | id: rect_out
47 | anchors.fill: parent
48 | radius: width / 2
49 | color: Qt.rgba(175 / 255, 175 / 255, 175 / 255, 1)
50 | Rectangle {
51 | id: rect_in
52 | anchors.centerIn: parent
53 | width: parent.width / 3
54 | height: width
55 | radius: width / 2
56 | color: Qt.rgba(80 / 255, 80 / 255, 80 / 255, 1)
57 | }
58 | }
59 | DropShadow {
60 | id: shadow_out
61 | anchors.fill: rect_out
62 | source: rect_out
63 | horizontalOffset: -2
64 | verticalOffset: 2
65 | color: "#25282a"
66 | }
67 | }
68 | }
69 | MouseArea {
70 | anchors.fill: parent
71 | // 避免和handle冲突
72 | acceptedButtons: Qt.NoButton
73 | onWheel: {
74 | if (wheel.angleDelta.y < 0) {
75 | control.decrease()
76 | } else {
77 | control.increase()
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/BasicComponent/BasicMenu.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 | import QtQuick.Window 2.15
6 |
7 | // Qt5菜单
8 | // 龚建波 2025-07-25
9 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Menu.qml
10 | T.Menu {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: {
16 | var p = control.parent
17 | while (p) {
18 | if (p instanceof BasicMenu || p instanceof BasicMenuBar ||
19 | p instanceof BasicMenuItem || p instanceof BasicMenuBarItem) {
20 | if ("themeColor" in p)
21 | return p.themeColor
22 | else
23 | break
24 | }
25 | p = p.parent
26 | }
27 | return "darkCyan"
28 | }
29 | // 定义背景颜色
30 | property color backgroundColor: "white"
31 | // 定义边框颜色
32 | property color borderColor: themeColor
33 |
34 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
35 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
36 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
37 | // contentWidth + leftPadding + rightPadding)
38 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
39 | // contentHeight + topPadding + bottomPadding)
40 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
41 | contentWidth + leftPadding + rightPadding)
42 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
43 | contentHeight + topPadding + bottomPadding)
44 | // 外边距
45 | margins: 0
46 | // 边距,菜单项和菜单窗口边框的间隔
47 | padding: 1
48 | // 多级菜单时子菜单重叠像素数
49 | overlap: 1
50 | // 字体设置
51 | // 也可以给QApplication设置全局的默认字体
52 | font{
53 | family: "SimSun"
54 | pixelSize: 16
55 | }
56 |
57 | // 菜单项样式
58 | delegate: BasicMenuItem { }
59 |
60 | // 菜单内容用ListView组织
61 | contentItem: ListView {
62 | implicitHeight: contentHeight
63 | model: control.contentModel
64 | // 是否可交互(如拖动内容)
65 | interactive: Window.window ? contentHeight > Window.window.height : false
66 | clip: true
67 | currentIndex: control.currentIndex
68 | highlightMoveDuration: 0
69 | ScrollIndicator.vertical: ScrollIndicator {}
70 | }
71 |
72 | // 菜单弹框背景
73 | background: QmlDropShadow {
74 | implicitWidth: 160
75 | implicitHeight: 32
76 | color: control.backgroundColor
77 | border.width: 1
78 | border.color: control.borderColor
79 | }
80 |
81 | // 模态时窗口遮罩
82 | T.Overlay.modal: Rectangle {
83 | color: Color.transparent(control.palette.shadow, 0.5)
84 | }
85 |
86 | // 非模态时窗口遮罩
87 | T.Overlay.modeless: Rectangle {
88 | color: Color.transparent(control.palette.shadow, 0.12)
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 | BasicComponentDemo.qml
5 | CustomComponentDemo.qml
6 | BasicComponent/BasicBusyIndicator.qml
7 | BasicComponent/BasicButton.qml
8 | BasicComponent/BasicCheckBox.qml
9 | BasicComponent/BasicComboBox.qml
10 | BasicComponent/BasicDelayButton.qml
11 | BasicComponent/BasicDialog.qml
12 | BasicComponent/BasicLabel.qml
13 | BasicComponent/BasicMenu.qml
14 | BasicComponent/BasicMenuBar.qml
15 | BasicComponent/BasicMenuBarItem.qml
16 | BasicComponent/BasicMenuItem.qml
17 | BasicComponent/BasicMenuSeparator.qml
18 | BasicComponent/BasicProgressBar.qml
19 | BasicComponent/BasicRadioButton.qml
20 | BasicComponent/BasicScrollBar.qml
21 | BasicComponent/BasicSlider.qml
22 | BasicComponent/BasicSpinBox.qml
23 | BasicComponent/BasicSwitch.qml
24 | BasicComponent/BasicTabBar.qml
25 | BasicComponent/BasicTabButton.qml
26 | BasicComponent/BasicToolBar.qml
27 | BasicComponent/BasicToolButton.qml
28 | BasicComponent/BasicToolSeparator.qml
29 | BasicComponent/BasicTumbler.qml
30 | BasicComponent/GradientButton.qml
31 | BasicComponent/GradientCheckBox.qml
32 | BasicComponent/GradientComboBox.qml
33 | BasicComponent/QmlIconLabel.qml
34 | CustomComponent/CustomButton.qml
35 | CustomComponent/CustomButton2.qml
36 | CustomComponent/CustomCircleProgressBar.qml
37 | CustomComponent/CustomCircularView.qml
38 | CustomComponent/CustomCircularViewDemo.qml
39 | CustomComponent/CustomDateRange.qml
40 | CustomComponent/CustomDesktopTip.qml
41 | CustomComponent/CustomItemShot.qml
42 | CustomComponent/CustomItemShotDemo.qml
43 | CustomComponent/CustomLoading.qml
44 | CustomComponent/CustomLoading2.qml
45 | CustomComponent/CustomLoading3.qml
46 | CustomComponent/CustomLoading4.qml
47 | CustomComponent/CustomSwitchButton.qml
48 | CustomComponent/CustomVolumeSlider.qml
49 | CustomComponent/CustomVolumeSliderDemo.qml
50 | BasicComponent/BasicScrollView.qml
51 | BasicComponent/QmlDropShadow.qml
52 | BasicComponent/QmlInnerShadow.qml
53 | BasicComponent/BasicTextInput.qml
54 | BasicComponent/BasicTextField.qml
55 | BasicComponent/BasicTextEdit.qml
56 | BasicComponent/BasicTextArea.qml
57 |
58 |
59 |
--------------------------------------------------------------------------------
/BasicComponent/BasicScrollBar.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5滚动条
7 | // 龚建波 2025-05-18
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ScrollBar.qml
9 | T.ScrollBar {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 | // 定义滚动条颜色
16 | property color handleColor: control.pressed
17 | ? Qt.darker(themeColor)
18 | : control.hovered
19 | ? Qt.darker(themeColor, 1.15)
20 | : themeColor
21 |
22 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
23 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
24 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
25 | implicitContentWidth + leftPadding + rightPadding)
26 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
27 | implicitContentHeight + topPadding + bottomPadding)
28 | // 背景整体size和handle的间隔
29 | padding: 1
30 | // 参考源码设置
31 | visible: control.policy !== T.ScrollBar.AlwaysOff
32 | // 最小长度占比 [0.0, 1.0],他这个用百分比不用绝对值就很迷
33 | minimumSize: orientation == Qt.Horizontal ? height / width : width / height
34 | // 表示滚动条是否处于活跃状态,附加到滚动区域会自动设置
35 | // active: true
36 | // 是否为交互式滚动条,如可以鼠标或触摸拖动,默认true
37 | // interactive: true
38 | // 方向,横向或竖向,默认横向horizontal
39 | // orientation: Qt.Horizontal
40 | // 是否为横向-只读:horizontal
41 | // 是否为竖向-只读:vertical
42 | // 显示策略,默认内容越界且交互操作时才显示ScrollBar.AsNeeded
43 | // policy: ScrollBar.AsNeeded
44 | // 需要时显示:ScrollBar.AsNeeded
45 | // 永远不显示:ScrollBar.AlwaysOff
46 | // 永远显示:ScrollBar.AlwaysOn
47 | // 步进值,默认0
48 | // stepSize: 0
49 | // 滑块对齐模式
50 | // Slider.NoSnap 默认不对齐
51 | // Slider.SnapAlways 拖动手柄时,滑块会对齐合法值,如设置了stepSize那就根据步进值对齐
52 | // Slider.SnapOnRelease 滑块在拖动时不会对齐,而仅在释放手柄后对齐
53 | // snapMode: Slider.NoSnap
54 |
55 | // 根据步进值减少,未设置步进值则为0.1
56 | // decrease()
57 | // 根据步进值增加,未设置步进值则为0.1
58 | // increase()
59 |
60 | // handle滑块部分
61 | contentItem: Rectangle {
62 | implicitWidth: 10
63 | implicitHeight: 10
64 | // 圆角用短边1/2
65 | radius: control.horizontal ? height / 2 : width / 2
66 | color: control.handleColor
67 | // 参考源码,通过opacity来控制handle显示隐藏
68 | // 源码默认行为ScrollBar.AsNeeded是鼠标放上去或者滚动滚轮才会出现滚动条
69 | // 这里修改为widgets那种内容超出范围就显示的样子
70 | opacity: (control.policy === T.ScrollBar.AlwaysOn || control.size < 1.0) ? 1.0 : 0.0
71 | // 源码的交互动画这里没用到
72 | // opacity: 0.0
73 | // states: State {
74 | // name: "active"
75 | // when: control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0)
76 | // PropertyChanges { target: control.contentItem; opacity: 0.75 }
77 | // }
78 | // transitions: Transition {
79 | // from: "active"
80 | // SequentialAnimation {
81 | // PauseAnimation { duration: 450 }
82 | // NumberAnimation { target: control.contentItem; duration: 200; property: "opacity"; from:1.0; to: 0.0 }
83 | // }
84 | // }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/BasicComponent/BasicTextInput.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 |
3 | // Qt5普通编辑框(区别于富文本编辑框)
4 | // 龚建波 2025-07-25
5 | // 参考:qt-everywhere-src-5.15.2\qtdeclarative\src\quick\items\qquicktextinput_p.h
6 | // TextInput 是 TextField 父类,是 QtQuick 基础模块的组件,而 TextField 是 Controls 模块的
7 | TextInput {
8 | id: control
9 |
10 | // 因为没有background等属性,需要嵌套在Rectangle等组件内
11 | // 默认宽高
12 | width: 200
13 | height: 30
14 | // 边距
15 | padding: 6
16 | leftPadding: padding + 2
17 | rightPadding: padding + 2
18 | // 文本颜色
19 | color: "black"
20 | // 字体设置
21 | font{
22 | family: "SimSun"
23 | pixelSize: 16
24 | }
25 | // 截取超出部分
26 | clip: true
27 | // 默认Text.QtRendering看起来比较模糊
28 | renderType: Text.NativeRendering
29 | // 只读
30 | // readOnly: false
31 | // 文本左对齐
32 | horizontalAlignment: TextInput.AlignLeft
33 | // 文本默认顶部对齐
34 | verticalAlignment: TextInput.AlignVCenter
35 | // 光标样式自定义
36 | // cursorDelegate: Rectangle {
37 | // width: 2
38 | // color: "blue"
39 | // property bool cursorRuning: control.cursorVisible
40 | // visible: false
41 | // SequentialAnimation on visible {
42 | // id: cursorAnimation
43 | // running: false
44 | // loops: Animation.Infinite
45 | // PropertyAnimation { from: true; to: false; duration: 750 }
46 | // PropertyAnimation { from: false; to: true; duration: 500 }
47 | // }
48 | // onCursorRuningChanged: {
49 | // cursorAnimation.running = cursorRuning
50 | // visible = cursorRuning
51 | // }
52 | // }
53 | // cursorVisible: focus
54 | // 允许鼠标选取文本块
55 | selectByMouse: true
56 | // 选中文本的颜色
57 | selectedTextColor: "white"
58 | // 选中文本背景色
59 | selectionColor: "blue"
60 | // 超出宽度时开启允许滚动,默认为true
61 | // autoScroll: true
62 | // 显示模式:Normal普通文本,Password密码,NoEcho无显示,
63 | // PasswordEchoOnEdit显示在编辑时输入的字符,否则与相同TextInput.Password
64 | echoMode: TextInput.Normal
65 | // Password显示的字符,默认是个圆点
66 | // passwordCharacter: "*"
67 | // Password由普通字符到被特殊符号替换的间隔
68 | // passwordMaskDelay: 1000
69 | // 输入掩码,参照Widgets版本的的QLineEidt::inputMask,类似正则
70 | // inputMask: ">XXXXX;*"
71 | // 输入限制,如IntValidator,DoubleValidator,RegExpValidator
72 | // 设置validator后,内容为空似乎不会触发onAccepted和onEditingFinished了
73 | // validator: RegExpValidator { regExp: /[0-9]+/ }
74 | // 文本字符最大允许长度
75 | // maximumLength: 10
76 | // 新的输入覆盖光标后面的文本
77 | // overwriteMode: true
78 | // 文本换行,默认NoWrap
79 | // wrapMode: TextInput.Wrap
80 |
81 | // 一些只读属性
82 | // 一些复制粘贴相关的我略去了
83 | // 文本高度
84 | // contentHeight: real
85 | // 文本宽度
86 | // contentWidth: real
87 | // 与text属性不同,displayText包含来自输入法的部分文本输入
88 | // displayText: string
89 | // 文本字符长度
90 | // length: int
91 | // 选中的文本
92 | // selectedText: string
93 |
94 | // 信号
95 | // 当按下Return或Enter键时,将发出此信号
96 | // onAccepted: { console.log("edit onAccepted") }
97 | // 当按下Return或Enter键或文本输入失去焦点时
98 | // onEditingFinished: { console.log("edit onEditingFinished") }
99 | // 每当编辑文本时,都会发出此信号
100 | // 与textChanged不同的是,编程方式修改文本时不触发此信号
101 | // onTextEdited: { console.log("edit onTextEdited") }
102 | }
103 |
--------------------------------------------------------------------------------
/CustomComponent/CustomCircularView.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 |
4 | // 轮播图
5 | Item {
6 | id: control
7 |
8 | // 圆圈的宽度
9 | property int indicatorWidth: 10
10 | property int indicatorMargin: 10
11 | // 定时切换间隔
12 | property alias timerInterval: path_timer.interval
13 | // 切换动画执行时间
14 | property alias pathDuration: path_view.highlightMoveDuration
15 | property alias delegate: path_view.delegate
16 | property alias model: path_view.model
17 | // 页数
18 | property alias count: path_page.count
19 |
20 | PathView {
21 | id: path_view
22 | anchors.fill: parent
23 |
24 | // 此属性保存任何时候在路径上可见的项目数。
25 | // 将pathItemCount设置为undefined将显示路径上的所有项目。
26 | // 因为path代码的问题,设置为2最合适
27 | pathItemCount: 2
28 |
29 | // 测试时,把clip去掉就能看到完整的
30 | clip: true
31 |
32 | // 向前移动,即顺序0 1 2 3
33 | movementDirection: PathView.Positive
34 |
35 | // 切换的时间
36 | highlightMoveDuration: 1000
37 |
38 | // 视图中突出显示(当前项目)的首选范围,默认值PathView.StrictlyEnforceRange
39 | // 配合preferredHighlight的范围0.5 0.5,就能显示在正中,切换更自然
40 | // highlightRangeMode: PathView.StrictlyEnforceRange
41 |
42 | // 希望当前选定的项位于路径的中间,则将突出显示范围设置为0.5,0.5
43 | preferredHighlightBegin: 0.5
44 | preferredHighlightEnd: 0.5
45 | path: Path {
46 | startX: -path_view.width / 2
47 | startY: path_view.height / 2
48 |
49 | PathLine {
50 | x: path_view.pathItemCount * path_view.width - path_view.width / 2
51 | y: path_view.height / 2
52 | }
53 | }
54 | onModelChanged: {
55 | if (path_timer.running) {
56 | path_timer.restart()
57 | }
58 | }
59 |
60 | }
61 | // 定时切换
62 | Timer {
63 | id: path_timer
64 | running: control.visible
65 | repeat: true
66 | interval: 3000
67 | onTriggered: {
68 | // 至少两个才切换
69 | if (path_view.count > 1)
70 | path_view.currentIndex = (path_view.currentIndex + 1) % path_view.count
71 | }
72 | }
73 | // 右下角小圆圈
74 | PageIndicator {
75 | id: path_page
76 | anchors{
77 | right: parent.right
78 | bottom: parent.bottom
79 | margins: control.indicatorMargin
80 | }
81 | count: path_view.count
82 | currentIndex: path_view.currentIndex
83 | spacing: control.indicatorWidth
84 | delegate: Rectangle {
85 | width: control.indicatorWidth
86 | height: width
87 | radius: width / 2
88 | color: "white"
89 | // 非当前页就灰色
90 | opacity: index === path_page.currentIndex ? 1 : 0.6
91 | Behavior on opacity {
92 | OpacityAnimator {
93 | duration: 200
94 | }
95 | }
96 | // 点击跳转到该页
97 | // 还有问题,非连续的item,他会快速连续切换到目标index
98 | // 因为不是直接切换,有闪烁的感觉
99 | MouseArea{
100 | anchors.fill: parent
101 | onClicked: {
102 | path_view.currentIndex = index
103 | if (path_timer.running) {
104 | path_timer.restart()
105 | }
106 | }
107 | }
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/CustomComponent/CustomLoading.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 |
4 | // 自定义 loading 效果
5 | // 龚建波 2021-1-17
6 | Item {
7 | id: control
8 |
9 | // item圆圈个数
10 | property int itemCount: 10
11 | // item圆圈直径大小
12 | property int itemSize: 16
13 | // item变大范围
14 | property int itemExpand: 10
15 | // item圆圈颜色
16 | property color itemColor: "green"
17 | // 当前item,配合动画
18 | property int itemIndex: 0
19 | // 轮转一次的时长
20 | property int duration: 1500
21 | //
22 | property bool running: visible
23 |
24 | implicitHeight: 160
25 | implicitWidth: 160
26 |
27 | // index从0到count轮转,转到对应item就变大且不透明
28 | // animation也可以换成timer
29 | NumberAnimation {
30 | target: control
31 | property: "itemIndex"
32 | from: 0
33 | to: control.itemCount - 1
34 | loops: Animation.Infinite
35 | duration: control.duration
36 | running: control.running
37 | }
38 |
39 | // 加一层item.margin来容纳item大小变化
40 | Item {
41 | id: content
42 | anchors.fill: parent
43 | anchors.margins: control.itemExpand / 2 + 1
44 |
45 | Repeater {
46 | id: repeater
47 | model: control.itemCount
48 | Rectangle {
49 | id: item
50 | height: control.itemSize
51 | width: height
52 | x: content.width / 2 - width / 2
53 | y: content.height / 2 - height / 2
54 | radius: height / 2
55 | color: control.itemColor
56 |
57 | // 环绕在中心
58 | transform: [
59 | Translate {
60 | y: content.height / 2 - height / 2
61 | },
62 | Rotation {
63 | angle: index / repeater.count * 360
64 | origin.x: width / 2
65 | origin.y: height / 2
66 | }
67 | ]
68 |
69 | // 轮转到当前item时就切换状态
70 | state: control.itemIndex === index ? "current" : "normal"
71 | states: [
72 | State {
73 | name: "current"
74 | PropertyChanges {
75 | target: item
76 | opacity: 1
77 | height: control.itemSize + control.itemExpand
78 | }
79 | },
80 | State {
81 | name: "normal"
82 | PropertyChanges {
83 | target: item
84 | opacity: 0.1
85 | height: control.itemSize
86 | }
87 | }
88 | ]
89 |
90 | // 大小和透明度的状态过渡
91 | transitions: [
92 | Transition {
93 | from: "current"
94 | to: "normal"
95 | NumberAnimation {
96 | properties: "opacity,height"
97 | duration: control.duration
98 | }
99 | },
100 | Transition {
101 | from: "normal"
102 | to: "current"
103 | NumberAnimation {
104 | properties: "opacity,height"
105 | duration: 0
106 | }
107 | }
108 | ]
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/BasicComponent/BasicMenuItem.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 | import QtQuick.Shapes 1.15
6 |
7 | // Qt5菜单项
8 | // 龚建波 2025-07-25
9 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\MenuItem.qml
10 | T.MenuItem {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: menu && menu.themeColor
16 | ? menu.themeColor
17 | : "darkCyan"
18 | // 定义背景颜色
19 | property color backgroundColor: control.highlighted ? themeColor : "white"
20 | // 定义文本颜色
21 | property color textColor: control.highlighted ? "white" : "black"
22 | // 定义图标颜色
23 | property color indicatorColor: textColor
24 | // 展开二级菜单图标颜色
25 | property color arrowColor: textColor
26 |
27 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
28 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
29 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
30 | // implicitContentWidth + leftPadding + rightPadding)
31 | // 默认高度
32 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
33 | // implicitContentHeight + topPadding + bottomPadding,
34 | // implicitIndicatorHeight + topPadding + bottomPadding)
35 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
36 | implicitHeight: 30
37 | // 边距
38 | padding: 0
39 | leftPadding: 6
40 | rightPadding: 6
41 | // 小部件间隔
42 | spacing: 6
43 | // 字体设置
44 | // 也可以给QApplication设置全局的默认字体
45 | font{
46 | family: "SimSun"
47 | pixelSize: 16
48 | }
49 |
50 | // 文字和图标
51 | contentItem: QmlIconLabel {
52 | readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0
53 | readonly property real indicatorPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0
54 | controlRow.leftPadding: indicatorPadding
55 | controlRow.rightPadding: arrowPadding
56 | controlText.horizontalAlignment: Text.AlignLeft
57 |
58 | spacing: control.spacing
59 | source: control.icon.source
60 | text: control.text
61 | font: control.font
62 | color: control.textColor
63 | }
64 |
65 | // 勾选图标
66 | indicator: ColorImage {
67 | x: control.mirrored ? control.width - width - control.rightPadding : control.leftPadding
68 | y: control.topPadding + (control.availableHeight - height) / 2
69 |
70 | visible: control.checked
71 | source: control.checkable ? "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/check.png" : ""
72 | color: control.indicatorColor
73 | defaultColor: control.indicatorColor
74 | }
75 |
76 | // 展开二级菜单的图标
77 | arrow: ColorImage {
78 | x: control.mirrored ? control.leftPadding : control.width - width - control.rightPadding
79 | y: control.topPadding + (control.availableHeight - height) / 2
80 |
81 | visible: control.subMenu
82 | mirror: control.mirrored
83 | source: control.subMenu ? "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/arrow-indicator.png" : ""
84 | color: control.arrowColor
85 | defaultColor: control.arrowColor
86 | }
87 |
88 | // 背景
89 | background: Rectangle {
90 | implicitWidth: 120
91 | implicitHeight: 30
92 | // 参考源码,这应该是给border留的1px位置
93 | x: 1
94 | y: 1
95 | width: control.width - 2
96 | height: control.height - 2
97 | color: control.backgroundColor
98 | }
99 | }
100 |
101 |
--------------------------------------------------------------------------------
/CustomComponent/CustomLoading2.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 |
4 | // 自定义 loading 效果
5 | // 目前角度是设定死的,可以根据直径算
6 | // 龚建波 2021-1-17
7 | // 2021-2-11代码优化
8 | Item {
9 | id: control
10 |
11 | // item圆圈个数
12 | property int itemCount: 4
13 | // item圆圈直径
14 | property int itemSize: 20
15 | // item圆圈颜色
16 | property color itemColor: "green"
17 | // 转一次时长
18 | property int duration: 3000
19 | //
20 | property bool running: visible
21 |
22 | implicitHeight: 160
23 | implicitWidth: 160
24 |
25 | Item {
26 | id: content
27 | anchors.fill: parent
28 | anchors.margins: 5 + control.itemSize / 2
29 |
30 | Repeater {
31 | id: repeater
32 | model: control.itemCount
33 |
34 | // 旋转的小球
35 | Rectangle {
36 | id: item
37 | width: control.itemSize
38 | height: control.itemSize
39 | color: control.itemColor
40 | radius: width / 2
41 | // [1].圆边上点坐标计算公式(角度从右端逆时针增长)
42 | // x1 = x0 + r * cos(angle * PI / 180)
43 | // y1 = y0 + r * sin(angle * PI /180)
44 | // [2].js Math用的弧度
45 | // 1弧度bai=180/pai 度
46 | // 1度=pai/180 弧度
47 | // [3].Qt是屏幕坐标系,所以y值取反一下
48 | // y1 = y0 - r * sin(angle * PI /180)
49 | // 再把相位调到90度从顶上开始转,这样三角函数可以简化下
50 | // sin(π/2+α)=cosα
51 | // cos(π/2+α)=-sinα
52 | // 于是有了
53 | // x1 = x0 - r * sin(angle * PI / 180)
54 | // y1 = y0 - r * cos(angle * PI /180)
55 | // [4].此时逐渐增大角度就是逆时针转的,想要顺时针转又得再取反一次坐标
56 | // 或者改为逐渐减小
57 | // 我选择取反x
58 | // x1 = x0 + r * sin(angle * PI / 180)
59 | // [5].最后把小球的半径偏移去掉,就是围绕圆心转的效果了
60 | // 不然默认起点为左上角,会往右下角偏离一点
61 | // (我把wrapper的margin加了size/2,这样就不用减掉半径放值越界了)
62 | x: content.width / 2 - control.itemSize / 2 + content.width / 2 * Math.sin(rotate / 360 * 6.283185307179)
63 | y: content.height / 2 - control.itemSize / 2 - content.height / 2 * Math.cos(rotate / 360 * 6.283185307179)
64 | // rotate表示角度,范围[0,360],初始值目前为固定的
65 | property real rotate: -index * 20
66 |
67 |
68 | // 动画序列,根据顺序做了间隔
69 | SequentialAnimation {
70 | running: control.running
71 | loops: Animation.Infinite
72 | NumberAnimation {
73 | duration: index * 100
74 | }
75 | ParallelAnimation {
76 | NumberAnimation {
77 | target: item
78 | property: "rotate"
79 | from: -index * 20
80 | to: 360 - index * 20
81 | duration: control.duration
82 | easing.type: Easing.OutCubic
83 | }
84 | SequentialAnimation {
85 | NumberAnimation {
86 | target: item
87 | property: "opacity"
88 | from: 0
89 | to: 1
90 | duration: control.duration * 1 / 8
91 | }
92 | NumberAnimation {
93 | duration: control.duration * 3 / 4
94 | }
95 | }
96 | }
97 | NumberAnimation {
98 | duration: (control.itemCount - index) * 100
99 | }
100 | }
101 | }
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/BasicComponent/BasicTumbler.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5滑动选择框
7 | // 龚建波 2025-04-15
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Tumbler.qml
9 | T.Tumbler {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 | // 非选中时文本颜色
16 | property color normalTextColor: "white"
17 | // 选中时文本颜色
18 | property color selectedTextColor: "cyan"
19 | // 背景色
20 | property color backgroundColor: themeColor
21 | // 背景可设置渐变
22 | property Gradient backgroundGradient
23 |
24 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
25 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
26 | // inset和padding都是Control基类定义的,默认为0
27 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
28 | // implicitContentWidth + leftPadding + rightPadding)
29 | // 默认高度
30 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
31 | // implicitContentHeight + topPadding + bottomPadding)
32 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
33 | implicitHeight: implicitBackgroundHeight + topPadding + bottomPadding
34 | // 边距
35 | padding: 6
36 | // 字体设置
37 | // 也可以给QApplication设置全局的默认字体
38 | font{
39 | family: "SimSun"
40 | pixelSize: 16
41 | }
42 |
43 | // 数据源model
44 | // 元素个数count
45 | // 是否在移动moving
46 | // 当前选中索引currentIndex
47 | // 可见元素个数
48 | // visibleItemCount: 10
49 | // 是环绕滚动还是ListView上下滚动
50 | // wrap: false
51 |
52 | // 附加属性
53 | // Tumbler.displacement包含一个从-visibleItemCount/2到visibleItemCount/2的值,
54 | // 该值表示此项与当前项之间的距离,0表示完全是当前项。
55 | // Tumbler.tumbler类似于ListView.view,获取组件引用
56 |
57 | // 成员函数
58 | // positionViewAtIndex(int index, PositionMode mode)
59 | // 定位视图到指定位置
60 |
61 | // 选项样式
62 | delegate: Text {
63 | text: modelData
64 | color: (model.index === control.currentIndex)
65 | ? control.selectedTextColor
66 | : control.normalTextColor
67 | opacity: 1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2)
68 | font: control.font
69 | horizontalAlignment: Text.AlignHCenter
70 | verticalAlignment: Text.AlignVCenter
71 | renderType: Text.NativeRendering
72 | }
73 |
74 | // TumblerView中有PathView和ListView,通过wrap属性来切换,
75 | // Tumbler wrap为true则使用PathView,但是没导出内部view引用来对细节设置
76 | // 所以可以自己通过这两个view来实现,参照Customizing Tumbler
77 | contentItem: TumblerView {
78 | implicitWidth: 60
79 | implicitHeight: 200
80 | model: control.model
81 | delegate: control.delegate
82 | path: Path {
83 | startX: contentItem.width / 2
84 | startY: -contentItem.delegateHeight / 2
85 | PathLine {
86 | x: contentItem.width / 2
87 | y: (control.visibleItemCount + 1) * contentItem.delegateHeight - contentItem.delegateHeight / 2
88 | }
89 | }
90 | property real delegateHeight: control.availableHeight / control.visibleItemCount
91 | }
92 |
93 | // 背景色
94 | background: Rectangle {
95 | color: control.backgroundColor
96 | gradient: control.backgroundGradient
97 | }
98 |
99 | // 当前项上下加一条横线样式
100 | Rectangle {
101 | anchors.horizontalCenter: control.horizontalCenter
102 | y: control.height * (0.5 + 0.5 / control.visibleItemCount)
103 | width: control.width * 0.8
104 | height: 1
105 | color: selectedTextColor
106 | }
107 | Rectangle {
108 | anchors.horizontalCenter: control.horizontalCenter
109 | y: control.height * (0.5 - 0.5 / control.visibleItemCount)
110 | width: control.width * 0.8
111 | height: 1
112 | color: selectedTextColor
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/BasicComponent/BasicSwitch.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5滑动开关按钮
7 | // 龚建波 2025-04-10
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Switch.qml
9 | // Switch继承自AbstractButton,增加了position和visualPosition两个属性
10 | T.Switch {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义文本颜色
17 | property color textColor: "black"
18 | // 按钮区域背景颜色
19 | property color indicatorBackgroundColor: control.checked
20 | ? themeColor
21 | : "gray"
22 | // 滑块边框颜色
23 | property color indicatorBorderColor: control.checked
24 | ? themeColor
25 | : "gray"
26 | // 按下时滑块颜色
27 | property color indicatorButtonColor: control.down
28 | ? Qt.lighter(themeColor)
29 | : "white"
30 | // 滑块边框宽度
31 | property int indicatorBorderWidth: control.visualFocus ? 2 : 1
32 | // 按钮区域背景上下边距
33 | property int indicatorVerticalPadding: 6
34 |
35 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
36 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
37 | // inset和padding都是Control基类定义的,默认为0
38 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
39 | // implicitContentWidth + leftPadding + rightPadding)
40 | // 默认高度
41 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
42 | // implicitContentHeight + topPadding + bottomPadding,
43 | // implicitIndicatorHeight + topPadding + bottomPadding)
44 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
45 | implicitHeight: implicitIndicatorHeight + topPadding + bottomPadding
46 | // 边距
47 | padding: 6
48 | // 图标和文字间距
49 | spacing: 6
50 | // 字体设置
51 | // 也可以给QApplication设置全局的默认字体
52 | font{
53 | family: "SimSun"
54 | pixelSize: 16
55 | }
56 |
57 | // 滑块区域
58 | indicator: PaddedRectangle {
59 | // 按钮背景
60 | implicitWidth: 56
61 | implicitHeight: 28
62 |
63 | x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
64 | y: control.topPadding + (control.availableHeight - height) / 2
65 |
66 | radius: height / 2
67 | leftPadding: 0
68 | rightPadding: 0
69 | // 上下通过padding收缩,留出空白
70 | padding: control.indicatorVerticalPadding
71 | color: control.indicatorBackgroundColor
72 |
73 | // 滑块
74 | Rectangle {
75 | x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2)))
76 | y: (parent.height - height) / 2
77 | width: 28
78 | height: 28
79 | radius: height / 2
80 | color: control.indicatorButtonColor
81 | border.width: control.indicatorBorderWidth
82 | border.color: control.indicatorBorderColor
83 | // 切换状态时的缓动动画
84 | Behavior on x {
85 | enabled: !control.down
86 | SmoothedAnimation { velocity: 200 }
87 | }
88 | }
89 | }
90 |
91 | // 文本区域
92 | contentItem: CheckLabel {
93 | leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
94 | rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
95 |
96 | text: control.text
97 | font: control.font
98 | color: control.textColor
99 | horizontalAlignment: Text.AlignLeft
100 | verticalAlignment: Text.AlignVCenter
101 | renderType: Text.NativeRendering
102 | elide: Text.ElideRight
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/BasicComponent/BasicDialog.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 |
4 | // Qt5按钮样式自定义
5 | // 龚建波 2025-03-05
6 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Dialog.qml
7 | // Qt5 Dialog和QWidgets的对话框不一样,不能在Window外显示
8 | // Dialog继承自Popup,增加了header和footer
9 | Dialog {
10 | id: control
11 |
12 | // 默认宽高,根据内容计算
13 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
14 | // contentWidth + leftPadding + rightPadding,
15 | // implicitHeaderWidth,
16 | // implicitFooterWidth)
17 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
18 | // contentHeight + topPadding + bottomPadding
19 | // + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
20 | // + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
21 | width: 500
22 | height: 500
23 | // title: "Dialog"
24 | // 模态显示,默认false
25 | modal: true
26 | // 默认为点击别处就关闭对话框,可以设置closePolicy修改决策
27 | // closePolicy: Popup.NoAutoClose
28 | // 可设置居中,默认为parent左上角
29 | anchors.centerIn: parent
30 | // Overlay为弹出窗口提供图层,确保弹出窗口显示在其他内容上方
31 | // 绑定到Window上
32 | parent: Overlay.overlay
33 | // 模态显示时,overlay区域默认显示半透明遮罩,这里重新设置了
34 | Overlay.modal: Rectangle {
35 | // 默认值Color.transparent(control.palette.shadow, 0.5)
36 | color: Qt.rgba(0.5, 0.1, 0.1, 0.2)
37 | }
38 | // 是否显示modal时的overlay遮罩,默认绑定modal
39 | // dim: true
40 | // 当dim=true,模态显示Overlay.modal遮罩,非模态显示Overlay.modeless
41 | Overlay.modeless: Rectangle {
42 | // 默认值Color.transparent(control.palette.shadow, 0.12)
43 | color: Qt.rgba(0.5, 0.1, 0.1, 0.2)
44 | }
45 | // 显示隐藏的过渡
46 | // 这里写个淡入淡出
47 | // 这里还有个问题就是退场动画还没执行overlay就消失了
48 | enter: Transition {
49 | NumberAnimation {
50 | properties: "opacity"
51 | duration: 1000
52 | from: 0
53 | to: 1
54 | }
55 | }
56 | exit: Transition {
57 | NumberAnimation{
58 | properties: "opacity"
59 | duration: 1000
60 | from: 1
61 | to: 0
62 | }
63 | }
64 | // 在没有设置footer的时候,standardButtons枚举可以启用默认的按钮
65 | // standardButtons: Dialog.Cancel | Dialog.Ok
66 | // contentItem的边距
67 | padding: 10
68 | // 上中下的间距
69 | spacing: 10
70 |
71 | // 整个对话框的背景
72 | background: Rectangle {
73 | color: "white"
74 | border.color: "gray"
75 | radius: 10
76 | }
77 |
78 | // 头部
79 | header: Rectangle {
80 | id: header_item
81 | height: 50
82 | color: "transparent"
83 | border.color: "green"
84 | property int pressX: 0
85 | property int pressY: 0
86 | property bool onMoving: false
87 | }
88 |
89 | // 中心区域
90 | contentItem: Rectangle {
91 | border.color: "red"
92 | }
93 |
94 | // 底部
95 | footer: Rectangle {
96 | height: 50
97 | color: "transparent"
98 | border.color: "blue"
99 |
100 | // accept()/done() 触发 accepted()信号
101 | // reject() 触发 rejected() 信号
102 | // 等等相关信号
103 |
104 | Row {
105 | anchors.centerIn: parent
106 | spacing: 10
107 | Button {
108 | text: "accept"
109 | onClicked: control.accept()
110 | }
111 | Button {
112 | text: "done 0"
113 | onClicked: control.done(0)
114 | }
115 | Button {
116 | text: "close"
117 | // 直接close,或者visible=false是没有信号的
118 | onClicked: control.close()
119 | }
120 | }
121 | }
122 |
123 | // Dialog.result属性值对应调用的函数,都可以看做done的wrapper
124 | onAccepted: console.log("onAccepted", control.result)
125 | onAboutToHide: console.log("onAboutToHide")
126 | onAboutToShow: console.log("onAboutToShow")
127 | onClosed: console.log("onClosed")
128 | onOpened: console.log("onOpened")
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/CustomComponentDemo.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Window 2.15
3 | import QtQuick.Controls 2.15
4 |
5 | import "./CustomComponent"
6 |
7 | // 展示自定义组合组件
8 | ScrollView {
9 | id: control
10 |
11 | Column {
12 | anchors{
13 | left: parent.left
14 | top: parent.top
15 | margins: 10
16 | }
17 | spacing: 10
18 |
19 | Row {
20 | spacing: 10
21 | // 普通按钮
22 | CustomButton {}
23 | // 普通按钮2
24 | CustomButton2 {}
25 | // 开关按钮
26 | CustomSwitchButton {}
27 | // 桌面右下角弹框
28 | CustomButton2 {
29 | text: "Pop"
30 | textColor: "white"
31 | onClicked: pop.showTip()
32 | // 桌面右下角弹框
33 | CustomDesktopTip {
34 | id: pop
35 | title: qsTr("DesktopTip")
36 | content: Rectangle {
37 | width: 300
38 | height: 200
39 | color: "green"
40 | Text {
41 | anchors.centerIn: parent
42 | text: qsTr("DesktopTip")
43 | }
44 | // 测试上层也有个MouseArea对对话框的MouseArea影响
45 | MouseArea {
46 | anchors.fill: parent
47 | // 目前还不能设置hoverEnabled,不然parent的MouseArea没法判断
48 | // hoverEnabled: true
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | Row {
56 | spacing: 10
57 | // 音量调节
58 | CustomVolumeSliderDemo {}
59 | // Item截图
60 | CustomItemShotDemo {}
61 | }
62 |
63 | // 轮播图
64 | CustomCircularViewDemo {}
65 |
66 | // 环形进度条
67 | CustomCircleProgressBar {
68 | // 测试用,循环设置进度值
69 | NumberAnimation on value {
70 | from: 0; to: 100
71 | duration: 5000
72 | running: true
73 | loops: Animation.Infinite
74 | }
75 | }
76 |
77 | Row {
78 | spacing: 10
79 | // 日期范围选择
80 | CustomDateRange {
81 | id: date_range
82 | height: 40
83 | width: 300
84 | }
85 | Button {
86 | text: "近一周"
87 | onClicked: {
88 | let begin_date = new Date()
89 | date_range.endDate = new Date()
90 | begin_date.setDate(begin_date.getDate() - 6)
91 | date_range.beginDate = begin_date
92 | }
93 | }
94 | Button {
95 | text: "近一月"
96 | onClicked: {
97 | let begin_date = new Date()
98 | date_range.endDate = new Date()
99 | begin_date.setMonth(begin_date.getMonth() - 1)
100 | date_range.beginDate = begin_date
101 | }
102 | }
103 | }
104 |
105 | Row {
106 | spacing: 10
107 | CustomLoading {
108 | running: loading_runing.checked
109 | itemColor: "red"
110 | }
111 | CustomLoading2 {
112 | running: loading_runing.checked
113 | itemColor: "blue"
114 | }
115 | Button {
116 | id: loading_runing
117 | checkable: true
118 | checked: true
119 | text: checked ? "stop" : "running"
120 | }
121 | }
122 |
123 | Row {
124 | spacing: 10
125 | CustomLoading3 {
126 | running: loading_runing.checked
127 | itemColor: "orange"
128 | }
129 | CustomLoading4 {
130 | running: loading_runing.checked
131 | itemColor: "purple"
132 | }
133 | }
134 |
135 | // 底部空白
136 | Item {
137 | width: 100
138 | height: 500
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/BasicComponent/BasicTextArea.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5富文本编辑框
7 | // 龚建波 2025-08-07
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\TextArea.qml
9 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\quicktemplates2\qquicktextarea_p.h
10 | T.TextArea {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义文本颜色
17 | property alias textColor: control.color
18 | // 定义背景颜色
19 | property color backgroundColor: "white"
20 | // 定义边框宽度
21 | property int borderWidth: control.activeFocus ? 2 : 1
22 | // 定义边框颜色
23 | property color borderColor: (activeFocus || hovered) ? themeColor : "black"
24 | // 定义边框圆角
25 | property int radius: 0
26 |
27 | // 默认宽高
28 | // implicitWidth: Math.max(contentWidth + leftPadding + rightPadding,
29 | // implicitBackgroundWidth + leftInset + rightInset,
30 | // placeholder.implicitWidth + leftPadding + rightPadding)
31 | // implicitHeight: Math.max(contentHeight + topPadding + bottomPadding,
32 | // implicitBackgroundHeight + topInset + bottomInset,
33 | // placeholder.implicitHeight + topPadding + bottomPadding)
34 | implicitWidth: 200
35 | implicitHeight: 30
36 | // 边距
37 | padding: 6
38 | leftPadding: padding + 2
39 | rightPadding: padding + 2
40 | // 字体设置
41 | font{
42 | family: "SimSun"
43 | pixelSize: 16
44 | }
45 | // 截取超出部分,TextField文字内容默认是不会超出的
46 | // clip: true
47 | // 默认Text.QtRendering看起来比较模糊
48 | renderType: Text.NativeRendering
49 | // 只读
50 | // readOnly: false
51 | // 文本左对齐
52 | horizontalAlignment: TextInput.AlignLeft
53 | // 文本默认顶部对齐
54 | // verticalAlignment: TextInput.AlignVCenter
55 | // 文本颜色
56 | color: "black"
57 | // 允许鼠标选取文本块
58 | selectByMouse: true
59 | // 选中文本的颜色
60 | selectedTextColor: "white"
61 | // 选中文本背景色
62 | selectionColor: "blue"
63 | // 空文本时提示信息颜色
64 | placeholderTextColor: Color.transparent(control.color, 0.5)
65 | // 是否检测hover鼠标悬停,默认会跟随父组件的设置
66 | hoverEnabled: true
67 | // 新的输入覆盖光标后面的文本
68 | // overwriteMode: true
69 | // 文本换行,默认NoWrap
70 | // wrapMode: TextInput.Wrap
71 |
72 | // 一些只读属性
73 | // 一些复制粘贴相关的我略去了
74 | // 文本高度
75 | // contentHeight: real
76 | // 文本宽度
77 | // contentWidth: real
78 | // 文本行数
79 | // lineCount: int
80 | // 文本字符长度
81 | // length: int
82 | // 选中的文本
83 | // selectedText: string
84 |
85 | // 附加属性
86 | // flickable: TextArea
87 |
88 | // 信号
89 | // 当按下Return或Enter键或文本输入失去焦点时
90 | // onEditingFinished: { console.log("edit onEditingFinished") }
91 | // 鼠标单击嵌入的链接,链接必须是富文本或HTML格式
92 | // 链接如: baidu
93 | // onLinkActivated: (link)=>{ console.log("edit onLinkActivated", link) }
94 | // 鼠标悬停在嵌入的链接,链接必须是富文本或HTML格式
95 | // onLinkHovered: (link)=>{ console.log("edit onLinkHovered", link) }
96 | // 鼠标按下
97 | // onPressed: { console.log("edit onPressed") }
98 | // 鼠标弹起释放
99 | // onReleased: { console.log("edit onReleased") }
100 | // 鼠标长按
101 | // onPressAndHold: { console.log("edit onPressAndHold") }
102 |
103 | // 内容为空时的提示信息
104 | PlaceholderText {
105 | id: placeholder
106 | x: control.leftPadding
107 | y: control.topPadding
108 | width: control.width - (control.leftPadding + control.rightPadding)
109 | height: control.height - (control.topPadding + control.bottomPadding)
110 |
111 | text: control.placeholderText
112 | font: control.font
113 | color: control.placeholderTextColor
114 | verticalAlignment: control.verticalAlignment
115 | visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
116 | elide: Text.ElideRight
117 | renderType: control.renderType
118 | }
119 |
120 | // 背景
121 | background: Rectangle {
122 | implicitWidth: 200
123 | implicitHeight: 30
124 | color: control.backgroundColor
125 | border.width: control.borderWidth
126 | border.color: control.borderColor
127 | radius: control.radius
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/CustomComponent/CustomLoading3.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Shapes 1.12
4 |
5 | // 自定义loading效果
6 | // 龚建波 2022-02-09
7 | Item {
8 | id: control
9 |
10 | implicitWidth: 160
11 | implicitHeight: 160
12 |
13 | property int itemMargin: 5
14 | property int itemBorder: 4
15 | // item的宽度
16 | property int itemWidth: 16
17 | // item的颜色
18 | property color itemColor: "#0486FF"
19 | //
20 | property double _rotate: _from
21 | property double _from: 30
22 | property double _to: 150
23 | //
24 | property bool running: visible
25 |
26 | // 动画1旋转
27 | RotationAnimation {
28 | target: sp
29 | from: 0
30 | to: 360
31 | loops: Animation.Infinite
32 | running: control.running
33 | duration: 3000
34 | }
35 |
36 | // 动画2控制长度形变
37 | SequentialAnimation {
38 | loops: Animation.Infinite
39 | running: control.running
40 | NumberAnimation {
41 | target: control
42 | property: "_rotate"
43 | from: _from
44 | to: _to
45 | duration: 2000
46 | }
47 | NumberAnimation {
48 | duration: 2000 // 停顿
49 | }
50 | NumberAnimation {
51 | target: control
52 | property: "_rotate"
53 | from: _to
54 | to: _from
55 | duration: 1000
56 | }
57 | NumberAnimation {
58 | duration: 2000 // 停顿
59 | }
60 | }
61 |
62 | Shape {
63 | id: sp
64 | width: control.width
65 | height: control.height
66 | layer{
67 | enabled: true
68 | smooth: true
69 | samples: 16
70 | }
71 |
72 | // 底部浅色
73 | ShapePath {
74 | strokeWidth: itemWidth + itemBorder * 2
75 | strokeColor: Qt.lighter(itemColor, 1.9)
76 | strokeStyle: ShapePath.SolidLine
77 | fillColor: "transparent"
78 |
79 | capStyle: ShapePath.RoundCap
80 | joinStyle: ShapePath.RoundJoin
81 |
82 | PathAngleArc {
83 | centerX: width / 2
84 | centerY: height / 2
85 | radiusX: width / 2 - itemWidth / 2 - itemMargin - itemBorder
86 | radiusY: height / 2 - itemWidth / 2 - itemMargin - itemBorder
87 | startAngle: 0
88 | sweepAngle: 360
89 | moveToStart: true
90 | }
91 | }
92 |
93 | // 底部深色
94 | ShapePath {
95 | strokeWidth: itemWidth
96 | strokeColor: Qt.lighter(itemColor, 1.8)
97 | strokeStyle: ShapePath.SolidLine
98 | fillColor: "transparent"
99 |
100 | capStyle: ShapePath.RoundCap
101 | joinStyle: ShapePath.RoundJoin
102 |
103 | PathAngleArc {
104 | centerX: width / 2
105 | centerY: height / 2
106 | radiusX: width / 2 - itemWidth / 2 - itemMargin - itemBorder
107 | radiusY: height / 2 - itemWidth / 2 - itemMargin - itemBorder
108 | startAngle: 0
109 | sweepAngle: 360
110 | moveToStart: true
111 | }
112 | }
113 |
114 | // 旋转的曲线
115 | ShapePath {
116 | strokeWidth: itemWidth
117 | strokeColor: itemColor
118 | strokeStyle: ShapePath.SolidLine
119 | fillColor: "transparent"
120 |
121 | capStyle: ShapePath.RoundCap
122 | joinStyle: ShapePath.RoundJoin
123 |
124 | PathAngleArc {
125 | centerX: width / 2
126 | centerY: height / 2
127 | radiusX: width / 2 - itemWidth / 2 - itemMargin - itemBorder
128 | radiusY: height / 2 - itemWidth / 2 - itemMargin - itemBorder
129 | startAngle: 0
130 | sweepAngle: control.running ? -_rotate : 360
131 | moveToStart: true
132 | }
133 |
134 | PathAngleArc {
135 | centerX: width / 2
136 | centerY: height / 2
137 | radiusX: width / 2 - itemWidth / 2 - itemMargin - itemBorder
138 | radiusY: height / 2 - itemWidth / 2 - itemMargin - itemBorder
139 | startAngle: 180
140 | sweepAngle: control.running ? -_rotate : 360
141 | moveToStart: true
142 | }
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/BasicComponent/GradientButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5按钮样式自定义
7 | // 龚建波 2025-03-06
8 | // 基础样式定义见BasicButton
9 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Button.qml
10 | T.Button {
11 | id:control
12 |
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 | // 定义文本颜色
16 | property color textColor: "white"
17 | // 定义背景渐变色
18 | property Gradient backgroundGradient: (control.pressed || control.checked)
19 | ? _pressGradient
20 | : (control.hovered || control.highlighted)
21 | ? _hoverGradient
22 | : _normalGradient
23 | property Gradient _pressGradient: Gradient {
24 | GradientStop { position: 0.0; color: Qt.darker(themeColor) }
25 | GradientStop { position: 0.5; color: themeColor }
26 | GradientStop { position: 1.0; color: Qt.lighter(themeColor) }
27 | }
28 | property Gradient _hoverGradient: Gradient {
29 | GradientStop { position: 0.0; color: themeColor }
30 | GradientStop { position: 0.5; color: Qt.darker(themeColor) }
31 | GradientStop { position: 1.0; color: themeColor }
32 | }
33 | property Gradient _normalGradient: Gradient {
34 | GradientStop { position: 0.0; color: Qt.lighter(themeColor) }
35 | GradientStop { position: 0.5; color: themeColor }
36 | GradientStop { position: 1.0; color: Qt.lighter(themeColor) }
37 | }
38 | // 定义边框圆角
39 | property int radius: 4
40 |
41 | // 默认宽高
42 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
43 | implicitHeight: implicitContentHeight + topPadding + bottomPadding
44 | // 边距
45 | padding: 0
46 | leftPadding: 8
47 | rightPadding: 8
48 | spacing: 6
49 | // 字体设置
50 | // 也可以给QApplication设置全局的默认字体
51 | font{
52 | family: "SimSun"
53 | pixelSize: 16
54 | }
55 | // 按钮图标设置
56 | // icon.width: 24
57 | // icon.height: 24
58 | // icon.source: "url"
59 | icon.color: textColor
60 | hoverEnabled: true
61 |
62 | // 按钮中显示的内容
63 | contentItem: Item {
64 | implicitWidth: btn_text.implicitWidth + btn_icon.iconWidth
65 | implicitHeight: 30
66 | // Row会根据内容计算出implicitWidth
67 | Row {
68 | id: btn_row
69 | // 居中显示
70 | anchors.centerIn: parent
71 | // 图标和文字间隔
72 | spacing: control.spacing
73 | // 用impl模块的ColorImage,便于设置图标颜色
74 | ColorImage {
75 | id: btn_icon
76 | // 除了text外的内容宽度
77 | property int iconWidth: (btn_icon.implicitWidth > 0 ? btn_icon.implicitWidth + btn_row.spacing : 0)
78 | // 在Row内上下居中
79 | anchors.verticalCenter: parent.verticalCenter
80 | // 借用Button已有的icon接口设置图标url
81 | source: control.icon.source
82 | // 借用Button已有的icon接口设置图标尺寸
83 | sourceSize: Qt.size(control.icon.width, control.icon.height)
84 | // 借用Button已有的icon接口设置图标颜色
85 | color: control.icon.color
86 | }
87 | Text {
88 | id: btn_text
89 | // 如果按钮宽度小于默认宽度文字需要显示省略号,得先设置Text宽度
90 | width: (control.width - btn_icon.iconWidth - control.leftPadding - control.rightPadding)
91 | // 在Row内上下居中
92 | anchors.verticalCenter: parent.verticalCenter
93 | // 按钮文字内容
94 | text: control.text
95 | // 文字颜色
96 | color: control.textColor
97 | // 单独设置文本组件的渲染方式
98 | renderType: Text.NativeRendering
99 | // 文字对齐方式,设置Text宽高后设置才有意义
100 | verticalAlignment: Text.AlignVCenter
101 | horizontalAlignment: Text.AlignHCenter
102 | // 换行设置,按钮一般没有换行
103 | // wrapMode: Text.NoWrap
104 | // 文字超出按钮范围显示省略号
105 | elide: Text.ElideMiddle
106 | // 字体设置
107 | font: control.font
108 | }
109 | }
110 | }
111 |
112 | // 背景
113 | background: Rectangle {
114 | // control设置了背景无关的宽高,这里也可以不设置默认宽高
115 | implicitWidth: control.implicitWidth
116 | implicitHeight: control.implicitHeight
117 | // 背景圆角
118 | radius: control.radius
119 | // 背景渐变
120 | gradient: control.backgroundGradient
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/BasicComponent/BasicRadioButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5单选框样式自定义
7 | // 龚建波 2025-03-18
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\RadioButton.qml
9 | // RadioButton继承自AbstractButton,并将checkable/autoExclusive设置为true
10 | T.RadioButton {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义文本颜色
17 | property color textColor: "black"
18 | // 定义勾选框背景色
19 | property color indicatorBackgroundColor: "transparent"
20 | // 定义勾选框颜色
21 | // pressed按下,hovered鼠标悬停,highlighted高亮,checked选中
22 | property color indicatorBorderColor: control.pressed
23 | ? Qt.darker(themeColor)
24 | : (control.hovered || control.highlighted)
25 | ? Qt.lighter(themeColor)
26 | : control.checked
27 | ? themeColor
28 | : themeColor
29 | // 定义勾选图标颜色
30 | property color indicatorButtonColor: indicatorBorderColor
31 | // 定义勾选框边框宽度
32 | property int indicatorBorderWidth: 1
33 | // 定义勾选框圆角,<0则为圆形勾选框
34 | property int indicatorRadius: -1
35 |
36 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
37 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
38 | // inset和padding都是Control基类定义的,默认为0
39 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
40 | // implicitContentWidth + leftPadding + rightPadding)
41 | // 默认高度
42 | // implicitIndicatorHeight为indicator勾选部件的默认高度
43 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
44 | // implicitContentHeight + topPadding + bottomPadding,
45 | // implicitIndicatorHeight + topPadding + bottomPadding)
46 | // 通过内容计算宽高可能会导致一组按钮的宽高都不齐,可以用固定的默认宽高,或者固定高但是宽度自适应
47 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
48 | implicitHeight: implicitIndicatorHeight + topPadding + bottomPadding
49 | // 边距
50 | padding: 0
51 | // 上下边距可以直接用verticalPadding,因为遇到过相关bug就单独设置下
52 | topPadding: 3
53 | bottomPadding: 3
54 | // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
55 | leftPadding: 8
56 | rightPadding: 8
57 | // 图标和文字间距
58 | spacing: 6
59 | // 字体设置
60 | // 也可以给QApplication设置全局的默认字体
61 | font{
62 | family: "SimSun"
63 | pixelSize: 16
64 | }
65 |
66 | // Control组件点击之后,后续按空格也会触发点击,可以把空格过滤掉
67 | Keys.onPressed: event.accepted = (event.key === Qt.Key_Space)
68 | Keys.onReleased: event.accepted = (event.key === Qt.Key_Space)
69 | // 是否可以选中
70 | // checkable: true
71 | // 独占性,true则表示单选,在分组内互斥
72 | // autoExclusive: true
73 | // 是否检测hover鼠标悬停,默认会跟随父组件的设置
74 | hoverEnabled: true
75 | // 文本内容
76 | // text: "RadioButton"
77 |
78 | // 勾选图标
79 | indicator: Rectangle {
80 | implicitWidth: 24
81 | implicitHeight: 24
82 | // 设置勾选图标位置
83 | x: control.text ? control.leftPadding : control.leftPadding + (control.availableWidth - width) / 2
84 | y: control.topPadding + (control.availableHeight - height) / 2
85 | // <0则为圆形勾选框
86 | radius: control.indicatorRadius < 0 ? width / 2 : control.indicatorRadius
87 | color: control.indicatorBackgroundColor
88 | border.width: control.indicatorBorderWidth
89 | border.color: control.indicatorBorderColor
90 | // 选中后的实心圆圈
91 | Rectangle {
92 | anchors.fill: parent
93 | anchors.margins: parent.width / 6 + control.indicatorBorderWidth
94 | // 和边框一样的圆角
95 | radius: control.indicatorRadius < 0 ? width / 2 : control.indicatorRadius
96 | color: control.indicatorButtonColor
97 | // 选中后显示
98 | visible: control.checked
99 | }
100 | }
101 |
102 | // 文本描述
103 | // 源码中用的CheckLabel继承自Text
104 | contentItem: Text {
105 | // 文字内容
106 | text: control.text
107 | // 字体设置
108 | font: control.font
109 | // 颜色设置
110 | color: control.textColor
111 | // 文字对齐方式
112 | horizontalAlignment: Text.AlignLeft
113 | verticalAlignment: Text.AlignVCenter
114 | // 单独设置文本组件的渲染方式
115 | renderType: Text.NativeRendering
116 | // 文字超出按钮范围显示省略号
117 | elide: Text.ElideRight
118 | // contentItem宽度是包含了indicator的,通过padding把位置留出来
119 | leftPadding: control.indicator.width + control.spacing
120 | rightPadding: 0
121 | }
122 |
123 | // 背景
124 | // background: Rectangle { }
125 | }
126 |
--------------------------------------------------------------------------------
/BasicComponent/BasicDelayButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5延时按钮样式自定义
7 | // 龚建波 2025-04-09
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\DelayButton.qml
9 | // DelayButton继承自AbstractButton,增加了delay/progress/transition三个属性
10 | T.DelayButton {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义选中后主题色
17 | property color maskThemeColor: "green"
18 | // 定义未选中文本颜色
19 | property color textColor: "white"
20 | // 定义选中后文本颜色
21 | property color maskTextColor: "yellow"
22 | // 定义背景颜色
23 | // pressed按下,hovered鼠标悬停,highlighted高亮,checked选中
24 | // 判断了pressed动画会跳过一段
25 | property color backgroundColor: (control.hovered)
26 | ? Qt.lighter(themeColor)
27 | : themeColor
28 | // 定义选中后背景色
29 | property color maskBackgroundColor: (control.hovered)
30 | ? Qt.lighter(maskThemeColor)
31 | : maskThemeColor
32 | // 定义边框宽度
33 | property int borderWidth: 0
34 | // 定义边框颜色
35 | property color borderColor: Qt.darker(themeColor)
36 | // 定义边框圆角
37 | property int radius: 0
38 |
39 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
40 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
41 | // inset和padding都是Control基类定义的,默认为0
42 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
43 | // implicitContentWidth + leftPadding + rightPadding)
44 | // 默认高度
45 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
46 | // implicitContentHeight + topPadding + bottomPadding)
47 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
48 | implicitHeight: implicitBackgroundHeight + topPadding + bottomPadding
49 | // 边距
50 | padding: 0
51 | // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
52 | leftPadding: 8
53 | rightPadding: 8
54 | // 字体设置
55 | // 也可以给QApplication设置全局的默认字体
56 | font{
57 | family: "SimSun"
58 | pixelSize: 16
59 | }
60 |
61 | // Control组件点击之后,后续按空格也会触发点击,可以把空格过滤掉
62 | Keys.onPressed: event.accepted = (event.key === Qt.Key_Space)
63 | Keys.onReleased: event.accepted = (event.key === Qt.Key_Space)
64 | // 动画时长,默认300ms
65 | delay: 500
66 | // 动画进度[0.0-1.0],只读属性
67 | // onProgressChanged: { }
68 | // 过渡动画
69 | transition: Transition {
70 | NumberAnimation {
71 | duration: control.delay * (control.pressed ? 1.0 - control.progress : 0.3 * control.progress)
72 | }
73 | }
74 |
75 | // 按钮中显示的内容
76 | contentItem: ItemGroup {
77 |
78 | // 未选中时的文字
79 | ClippedText {
80 | clip: control.progress > 0
81 | clipX: -control.leftPadding + control.progress * control.width
82 | clipWidth: (1.0 - control.progress) * control.width
83 | visible: control.progress < 1
84 |
85 | text: control.text
86 | font: control.font
87 | // opacity: enabled ? 1 : 0.3
88 | color: textColor
89 | renderType: Text.NativeRendering
90 | horizontalAlignment: Text.AlignHCenter
91 | verticalAlignment: Text.AlignVCenter
92 | elide: Text.ElideRight
93 | }
94 |
95 | // 按下或选中时的遮罩文字
96 | ClippedText {
97 | clip: control.progress > 0
98 | clipX: -control.leftPadding
99 | clipWidth: control.progress * control.width
100 | visible: control.progress > 0
101 |
102 | text: control.text
103 | font: control.font
104 | // opacity: enabled ? 1 : 0.3
105 | color: maskTextColor
106 | renderType: Text.NativeRendering
107 | horizontalAlignment: Text.AlignHCenter
108 | verticalAlignment: Text.AlignVCenter
109 | elide: Text.ElideRight
110 | }
111 | }
112 |
113 | // 背景
114 | background: Rectangle {
115 | implicitWidth: 100
116 | // ItemGroup implicitWidth/Height属性只读,所以设置background的
117 | implicitHeight: 30
118 | // 背景圆角
119 | radius: control.radius
120 | // 背景颜色
121 | color: control.backgroundColor
122 | // 背景边框
123 | border.width: control.borderWidth
124 | border.color: control.borderColor
125 |
126 | // 按下或选中时的遮罩背景
127 | PaddedRectangle {
128 | radius: control.radius
129 | width: control.progress * parent.width
130 | height: parent.height
131 | color: control.maskBackgroundColor
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/CustomComponent/CustomDateRange.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls 1.4 as Old
4 |
5 | // 日期范围选择
6 | // (使用Control1的日历实现)
7 | Rectangle {
8 | id: control
9 | implicitWidth: 250
10 | implicitHeight: 30
11 |
12 | property var beginDate // 起始时间
13 | property var endDate // 截止时间
14 | property bool __selectedBegin: true // 当前选中的是起始时间吗
15 | property date __minDate // 最小可选时间
16 | property date __maxDate // 最大可选时间
17 |
18 | property color normalTextColor: "#3D3E40"
19 | property color unselectTextColor: normalTextColor
20 | property color selectTextColor: "#305FDE"
21 |
22 | property alias font: item_center.font
23 | property alias beginText: item_begin.text
24 | property alias endText: item_end.text
25 |
26 | Component.onCompleted: {
27 | let min_date = new Date()
28 | min_date.setFullYear(1949)
29 | __minDate = min_date
30 | let max_date = new Date()
31 | max_date.setFullYear(3049)
32 | __maxDate = max_date
33 | }
34 | radius: 4
35 | border.color: "#D8DCE6"
36 |
37 | // 起始日期
38 | Text {
39 | id: item_begin
40 | anchors{
41 | verticalCenter: parent.verticalCenter
42 | left: parent.left
43 | right: item_center.left
44 | leftMargin: 5
45 | }
46 | height: parent.height
47 | verticalAlignment: Text.AlignVCenter
48 | horizontalAlignment: Text.AlignHCenter
49 |
50 | text: beginDate
51 | ? Qt.formatDate(beginDate, "yyyy-MM-dd")
52 | : ""
53 | font: control.font
54 | color:!item_pop.visible
55 | ? normalTextColor
56 | : __selectedBegin
57 | ? selectTextColor
58 | : unselectTextColor
59 |
60 | MouseArea {
61 | anchors.fill: parent
62 | onClicked: {
63 | __selectedBegin = true
64 | selectBeginChange()
65 | item_pop.open()
66 | }
67 | }
68 | }
69 |
70 | Text {
71 | id: item_center
72 | anchors.centerIn: parent
73 | text: "-"
74 | color: normalTextColor
75 | font{
76 | pixelSize: 14
77 | family: "Microsoft YaHei"
78 | }
79 | }
80 |
81 | // 截止日期
82 | Text {
83 | id: item_end
84 | anchors{
85 | verticalCenter: parent.verticalCenter
86 | left: item_center.right
87 | right: parent.right
88 | rightMargin: 5
89 | }
90 | height: parent.height
91 | verticalAlignment: Text.AlignVCenter
92 | horizontalAlignment: Text.AlignHCenter
93 | text: endDate
94 | ? Qt.formatDate(endDate, "yyyy-MM-dd")
95 | : ""
96 | font: control.font
97 | color:!item_pop.visible
98 | ? normalTextColor
99 | : !__selectedBegin
100 | ? selectTextColor
101 | : unselectTextColor
102 |
103 | MouseArea {
104 | anchors.fill: parent
105 | onClicked: {
106 | __selectedBegin = false
107 | selectBeginChange()
108 | item_pop.open()
109 | }
110 | }
111 | }
112 |
113 | Popup {
114 | id:item_pop
115 | width: 250
116 | height: 250
117 | y: control.height + 1
118 | closePolicy: Popup.CloseOnPressOutsideParent |
119 | Popup.CloseOnReleaseOutsideParent |
120 | Popup.CloseOnEscape
121 | background: Old.Calendar {
122 | id: item_calendar
123 | anchors.fill: parent
124 | focus: true
125 | onClicked: {
126 | selectDate()
127 | }
128 | onDoubleClicked: {
129 | selectDate()
130 | item_pop.close()
131 | }
132 | function selectDate() {
133 | if (item_pop.visible) {
134 | if (control.__selectedBegin)
135 | control.beginDate = selectedDate
136 | else
137 | control.endDate = selectedDate
138 | }
139 | }
140 | }
141 | }
142 |
143 | function selectBeginChange() {
144 | if (__selectedBegin) {
145 | item_calendar.selectedDate = beginDate
146 | item_calendar.minimumDate = __minDate
147 | item_calendar.maximumDate = endDate
148 | } else {
149 | item_calendar.selectedDate = endDate
150 | item_calendar.minimumDate = beginDate
151 | item_calendar.maximumDate = __maxDate
152 | }
153 | }
154 |
155 | function clear() {
156 | beginDate = undefined
157 | endDate = undefined
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/BasicComponent/BasicTextField.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5普通编辑框(区别于富文本编辑框)
7 | // 龚建波 2025-07-28
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\TextField.qml
9 | // 只有TextField有默认的边框
10 | T.TextField {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义文本颜色
17 | property alias textColor: control.color
18 | // 定义背景颜色
19 | property color backgroundColor: "white"
20 | // 定义边框宽度
21 | property int borderWidth: control.activeFocus ? 2 : 1
22 | // 定义边框颜色
23 | property color borderColor: (activeFocus || hovered) ? themeColor : "black"
24 | // 定义边框圆角
25 | property int radius: 0
26 |
27 | // 默认宽高
28 | // implicitWidth: implicitBackgroundWidth + leftInset + rightInset
29 | // || Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
30 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
31 | // contentHeight + topPadding + bottomPadding,
32 | // placeholder.implicitHeight + topPadding + bottomPadding)
33 | implicitWidth: 200
34 | implicitHeight: 30
35 | // 边距
36 | padding: 6
37 | leftPadding: padding + 2
38 | rightPadding: padding + 2
39 | // 字体设置
40 | font{
41 | family: "SimSun"
42 | pixelSize: 16
43 | }
44 | // 截取超出部分,TextField文字内容默认是不会超出的
45 | // clip: true
46 | // 默认Text.QtRendering看起来比较模糊
47 | renderType: Text.NativeRendering
48 | // 只读
49 | // readOnly: false
50 | // 文本左对齐
51 | horizontalAlignment: TextInput.AlignLeft
52 | // 文本默认顶部对齐
53 | verticalAlignment: TextInput.AlignVCenter
54 | // 文本颜色
55 | color: "black"
56 | // 允许鼠标选取文本块
57 | selectByMouse: true
58 | // 选中文本的颜色
59 | selectedTextColor: "white"
60 | // 选中文本背景色
61 | selectionColor: "blue"
62 | // 空文本时提示信息颜色
63 | placeholderTextColor: Color.transparent(control.color, 0.5)
64 | // 是否检测hover鼠标悬停,默认会跟随父组件的设置
65 | hoverEnabled: true
66 | // 超出宽度时开启允许滚动,默认为true
67 | // autoScroll: true
68 | // 显示模式:Normal普通文本,Password密码,NoEcho无显示,
69 | // PasswordEchoOnEdit显示在编辑时输入的字符,否则与相同TextInput.Password
70 | echoMode: TextInput.Normal
71 | // Password显示的字符,默认是个圆点
72 | // passwordCharacter: "*"
73 | // Password由普通字符到被特殊符号替换的间隔
74 | // passwordMaskDelay: 1000
75 | // 输入掩码,参照Widgets版本的的QLineEidt::inputMask,类似正则
76 | // inputMask: ">XXXXX;*"
77 | // 输入限制,如IntValidator,DoubleValidator,RegExpValidator
78 | // 设置validator后,内容为空似乎不会触发onAccepted和onEditingFinished了
79 | // validator: RegExpValidator { regExp: /[0-9]+/ }
80 | // 文本字符最大允许长度
81 | // maximumLength: 10
82 | // 新的输入覆盖光标后面的文本
83 | // overwriteMode: true
84 | // 文本换行,默认NoWrap
85 | // wrapMode: TextInput.Wrap
86 |
87 | // 一些只读属性
88 | // 一些复制粘贴相关的我略去了
89 | // 文本高度
90 | // contentHeight: real
91 | // 文本宽度
92 | // contentWidth: real
93 | // 与text属性不同,displayText包含来自输入法的部分文本输入
94 | // displayText: string
95 | // 文本字符长度
96 | // length: int
97 | // 选中的文本
98 | // selectedText: string
99 |
100 | // 信号
101 | // 当按下Return或Enter键时,将发出此信号
102 | // onAccepted: { console.log("edit onAccepted") }
103 | // 当按下Return或Enter键或文本输入失去焦点时
104 | // onEditingFinished: { console.log("edit onEditingFinished") }
105 | // 每当编辑文本时,都会发出此信号
106 | // 与textChanged不同的是,编程方式修改文本时不触发此信号
107 | // onTextEdited: { console.log("edit onTextEdited") }
108 | // 鼠标按下
109 | // onPressed: { console.log("edit onPressed") }
110 | // 鼠标弹起释放
111 | // onReleased: { console.log("edit onReleased") }
112 | // 鼠标长按
113 | // onPressAndHold: { console.log("edit onPressAndHold") }
114 |
115 | // 内容为空时的提示信息
116 | PlaceholderText {
117 | id: placeholder
118 | x: control.leftPadding
119 | y: control.topPadding
120 | width: control.width - (control.leftPadding + control.rightPadding)
121 | height: control.height - (control.topPadding + control.bottomPadding)
122 |
123 | text: control.placeholderText
124 | font: control.font
125 | color: control.placeholderTextColor
126 | verticalAlignment: control.verticalAlignment
127 | visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
128 | elide: Text.ElideRight
129 | renderType: control.renderType
130 | }
131 |
132 | // 背景
133 | background: Rectangle {
134 | implicitWidth: 200
135 | implicitHeight: 30
136 | color: control.backgroundColor
137 | border.width: control.borderWidth
138 | border.color: control.borderColor
139 | radius: control.radius
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/BasicComponent/GradientCheckBox.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5勾选框样式自定义
7 | // 龚建波 2025-03-07
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\CheckBox.qml
9 | T.CheckBox {
10 | id:control
11 |
12 | // 定义主题颜色
13 | property color themeColor: "darkCyan"
14 | // 定义文本颜色
15 | property color textColor: "white"
16 | // 定义背景渐变色
17 | property Gradient backgroundGradient: control.pressed
18 | ? _pressGradient
19 | : (control.hovered || control.highlighted)
20 | ? _hoverGradient
21 | : control.checked
22 | ? _normalGradient
23 | : _normalGradient
24 | property Gradient _pressGradient: Gradient{
25 | GradientStop { position: 0.0; color: Qt.darker(themeColor) }
26 | GradientStop { position: 0.5; color: themeColor }
27 | GradientStop { position: 1.0; color: Qt.lighter(themeColor) }
28 | }
29 | property Gradient _hoverGradient: Gradient{
30 | GradientStop { position: 0.0; color: themeColor }
31 | GradientStop { position: 0.5; color: Qt.darker(themeColor) }
32 | GradientStop { position: 1.0; color: themeColor }
33 | }
34 | property Gradient _normalGradient: Gradient{
35 | GradientStop { position: 0.0; color: Qt.lighter(themeColor) }
36 | GradientStop { position: 0.5; color: themeColor }
37 | GradientStop { position: 1.0; color: Qt.lighter(themeColor) }
38 | }
39 | // 定义边框圆角
40 | property int radius: 4
41 |
42 | // 默认宽高
43 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
44 | implicitHeight: implicitIndicatorHeight + topPadding + bottomPadding
45 | // 边距
46 | padding: 0
47 | // 上下边距可以直接用verticalPadding,因为遇到过相关bug就单独设置下
48 | topPadding: 3
49 | bottomPadding: 3
50 | // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
51 | leftPadding: 8
52 | rightPadding: 8
53 | // 图标和文字间距
54 | spacing: 6
55 | // 字体设置
56 | // 也可以给QApplication设置全局的默认字体
57 | font{
58 | family: "SimSun"
59 | pixelSize: 16
60 | }
61 | // 勾选图标设置
62 | // icon.width: 24
63 | // icon.height: 24
64 | // 这个图标资源是Control模块默认提供的
65 | icon.source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/check.png"
66 | icon.color: textColor
67 | hoverEnabled: true
68 |
69 | // 勾选图标
70 | indicator: Rectangle {
71 | implicitWidth: 24
72 | implicitHeight: 24
73 | // 设置勾选图标位置
74 | x: control.text ? control.leftPadding : control.leftPadding + (control.availableWidth - width) / 2
75 | y: (parent.height - height) / 2
76 | // indicator默认层级在background上,这里只是显示勾选部件的边框,所以color设置成透明
77 | color: "transparent"
78 | // 图标外面的矩形框
79 | border.width: 1
80 | border.color: icon.color
81 | // 用impl模块的ColorImage,便于设置图标颜色
82 | ColorImage {
83 | anchors.centerIn: parent
84 | // 借用Button已有的icon接口设置图标url
85 | source: control.icon.source
86 | // 借用Button已有的icon接口设置图标尺寸
87 | sourceSize: Qt.size(control.icon.width, control.icon.height)
88 | // 借用Button已有的icon接口设置图标颜色
89 | color: control.icon.color
90 | // 勾选状态下显示勾选图标
91 | visible: control.checkState === Qt.Checked
92 | }
93 | // 部分选中(Qt.PartiallyChecked)时不显示勾,显示一个实心方形
94 | // 设置CheckBox的tristate为true后才支持三态
95 | Rectangle {
96 | anchors.centerIn: parent
97 | width: parent.width / 2
98 | height: parent.height / 2
99 | color: icon.color
100 | // 半选状态下显示实心方形
101 | visible: control.checkState === Qt.PartiallyChecked
102 | }
103 | }
104 |
105 | // 文本描述
106 | // 源码中用的CheckLabel继承自Text,只是默认设置了AlignLeft+AlignVCenter+ElideRight
107 | contentItem: Text {
108 | // 文字内容
109 | text: control.text
110 | // 字体设置
111 | font: control.font
112 | // 颜色设置
113 | color: control.textColor
114 | // 文字对齐方式
115 | horizontalAlignment: Text.AlignHCenter
116 | verticalAlignment: Text.AlignVCenter
117 | // 单独设置文本组件的渲染方式
118 | renderType: Text.NativeRendering
119 | // 文字超出按钮范围显示省略号
120 | elide: Text.ElideRight
121 | // contentItem宽度是包含了indicator的,通过padding把位置留出来
122 | leftPadding: control.indicator.width + control.spacing
123 | rightPadding: 0
124 | }
125 |
126 | // 背景
127 | background: Rectangle {
128 | // control设置了背景无关的宽高,这里也可以不设置默认宽高
129 | implicitWidth: control.implicitWidth
130 | implicitHeight: control.implicitHeight
131 | // 背景圆角
132 | radius: control.radius
133 | // 背景渐变
134 | gradient: control.backgroundGradient
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/BasicComponent/BasicSlider.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Templates 2.15 as T
4 |
5 | // Qt5滑块
6 | // 龚建波 2025-05-13
7 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Slider.qml
8 | T.Slider {
9 | id: control
10 |
11 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
12 | // 定义主题颜色
13 | property color themeColor: "darkCyan"
14 | // 本来想在原来的基础上增加个opposite direction相反方向的属性
15 | // 奈何handle是和鼠标判定区域挂钩的,所以要做这个的话就不能用原来的handle
16 | // 相当于自己重新组合一个slider控件
17 | // 定义滑块按钮颜色
18 | property color handleColor: control.pressed
19 | ? Qt.darker(themeColor)
20 | : control.hovered
21 | ? Qt.darker(themeColor, 1.15)
22 | : themeColor
23 | // 定义滑块按钮边框颜色
24 | property color handleBorderColor: handleColor
25 | // 定义已完成部分背景颜色
26 | property color completeColor: Qt.darker(themeColor, 1.3)
27 | // 定义未完成部分背景颜色
28 | property color incompleteColor: Qt.lighter(themeColor)
29 | // 支持滚轮滑动
30 | property bool acceptWheel: true
31 |
32 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
33 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
34 | // inset和padding都是Control基类定义的,默认为0
35 | implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
36 | implicitHandleWidth + leftPadding + rightPadding)
37 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
38 | implicitHandleHeight + topPadding + bottomPadding)
39 | // 边距
40 | padding: 6
41 | // 起始值,默认0
42 | // from: 0
43 | // 终止值,默认1
44 | // to: 1
45 | // 当前值,默认0
46 | // value: 0
47 | // 步进值,默认0
48 | // stepSize: 0
49 | // 方向,横向或竖向,默认横向horizontal
50 | // orientation: Qt.Horizontal
51 | // 是否为横向-只读:horizontal
52 | // 是否为竖向-只读:vertical
53 | // handle的逻辑位置-只读:position,归一化到[0, 1]
54 | // handle的视觉位置-只读:visualPosition,归一化到[0, 1]
55 | // 滑块对齐模式
56 | // Slider.NoSnap 默认不对齐
57 | // Slider.SnapAlways 拖动手柄时,滑块会对齐合法值,如设置了stepSize那就根据步进值对齐
58 | // Slider.SnapOnRelease 滑块在拖动时不会对齐,而仅在释放手柄后对齐
59 | // snapMode: Slider.NoSnap
60 | // 启动触摸拖动事件的阈值 (以逻辑像素为单位),鼠标拖动阈值不会受到影响
61 | // touchDragThreshold: Qt.styleHints.startDragDistance
62 | // 在拖动手柄时滑块是否为value属性提供实时更新,默认true
63 | // live: true
64 |
65 | // 拖动滑块后触发,直接修改value或者调用增减函数不会触发
66 | // onMoved: { console.log("onMoved", value) }
67 | // 根据步进值减少,未设置步进值则为0.1
68 | // decrease()
69 | // 根据步进值增加,未设置步进值则为0.1
70 | // increase()
71 | // 根据位置换算对应的值
72 | // real valueAt(real position)
73 |
74 | // 滑块按钮
75 | handle: Rectangle {
76 | x: control.leftPadding + (control.horizontal ? control.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2)
77 | y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.visualPosition * (control.availableHeight - height))
78 | // 一个长方体的滑块
79 | implicitWidth: control.horizontal ? 12 : 20
80 | implicitHeight: control.horizontal ? 20 : 12
81 | color: handleColor
82 | border.width: 1
83 | border.color: handleBorderColor
84 | }
85 |
86 | // 背景作为滑动槽
87 | background: Rectangle {
88 | // 以control.horizontal为例:
89 | // control.availableHeight为去掉padding的高度
90 | // 所以如果control很高的话,只根据padding来算y可能就贴着上面
91 | // 这时候就需要加一部分(control.availableHeight - height) / 2来对齐
92 | x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2)
93 | y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)
94 | implicitWidth: control.horizontal ? 200 : 6
95 | implicitHeight: control.horizontal ? 6 : 200
96 | width: control.horizontal ? control.availableWidth : implicitWidth
97 | height: control.horizontal ? implicitHeight : control.availableHeight
98 | radius: control.horizontal ? height / 2 : width / 2
99 | color: control.incompleteColor
100 | // 通过scale=-1来翻转
101 | scale: control.horizontal && control.mirrored ? -1 : 1
102 |
103 | // 表示已经完成的部分,叠加到上层
104 | Rectangle {
105 | y: control.horizontal ? 0 : control.visualPosition * parent.height
106 | width: control.horizontal ? control.position * parent.width : parent.width
107 | height: control.horizontal ? parent.height : control.position * parent.height
108 | radius: control.horizontal ? parent.height / 2 : parent.width / 2
109 | color: control.completeColor
110 | }
111 | }
112 |
113 | // 添加鼠标滚轮滑动支持
114 | MouseArea {
115 | visible: acceptWheel
116 | anchors.fill: parent
117 | // 不接收点击事件避免和handle冲突
118 | acceptedButtons: Qt.NoButton
119 | // 不接收hover事件
120 | hoverEnabled: false
121 | onWheel: {
122 | if (wheel.angleDelta.y < 0) {
123 | // 根据步进值减少
124 | control.decrease()
125 | } else {
126 | // 根据步进值增加
127 | control.increase()
128 | }
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/CustomComponent/CustomDesktopTip.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Window 2.15
3 | import QtQuick.Controls 2.15
4 |
5 | // 桌面右下角弹框
6 | // 使用时给content赋值一个Item展示内容,
7 | // 如content: Rectangle{}
8 | // 目前还存在得问题:
9 | // 1.被模态框阻塞无法点击
10 | // 2.鼠标是否hover的判断,会被content的MouseArea遮盖,
11 | // 要么content的MouseArea不设置hoverEnable
12 | // TODO: 消除提示 QSGThreadedRenderLoop: expose event received for window ...
13 | Window {
14 | id: control
15 | visible: false
16 | color: "transparent"
17 | // 透明度
18 | opacity: 0
19 | // 无边框-提示框
20 | flags: Qt.FramelessWindowHint | Qt.ToolTip
21 | // 默认非模态
22 | modality: Qt.NonModal
23 | // 初始位置,在屏幕右下角
24 | x: Screen.virtualX + Screen.width - width
25 | y: Screen.virtualY + Screen.height
26 | // 大小绑定
27 | width: content_loader.width
28 | height: content_loader.height + title_item.height
29 | // 标题
30 | property alias title: title_text.text
31 | // 内容
32 | property alias content: content_loader.sourceComponent
33 |
34 | MouseArea {
35 | id: tip_mouse
36 | anchors.fill: parent
37 | hoverEnabled: true
38 | }
39 | // 标题栏
40 | Rectangle {
41 | id: title_item
42 | height: 30
43 | width: control.width
44 | // 标题文字
45 | Text {
46 | id: title_text
47 | anchors{
48 | verticalCenter: parent.verticalCenter
49 | left: parent.left
50 | leftMargin: 10
51 | }
52 | }
53 | // 关闭按钮-可以用image替换
54 | Rectangle {
55 | // 不能绑定text大小,不然会变
56 | width: 60
57 | height: 20
58 | anchors{
59 | verticalCenter: parent.verticalCenter
60 | right: parent.right
61 | rightMargin: 10
62 | }
63 | color: close_mouse.containsMouse ? "gray" : "white"
64 | border.color: "black"
65 | Text {
66 | id: close_text
67 | anchors.centerIn: parent
68 | // 鼠标放上去显示关闭,否则显示倒计时
69 | text: (close_mouse.containsMouse || close_timer.time_count < 1)
70 | ? qsTr("Close")
71 | : close_timer.time_count + " S"
72 | }
73 | MouseArea {
74 | id: close_mouse
75 | anchors.fill: parent
76 | hoverEnabled: true
77 | onClicked: control.hideTip()
78 | }
79 | // 关闭倒计时
80 | Timer {
81 | id: close_timer
82 | property int time_count: 0
83 | interval: 1000
84 | repeat: true
85 | onTriggered: {
86 | // 倒计时为0,且鼠标不在上面
87 | if (time_count < 1 &&
88 | !tip_mouse.containsMouse &&
89 | !close_mouse.containsMouse) {
90 | hideTip()
91 | } else {
92 | time_count--
93 | }
94 | }
95 | }
96 | }
97 | }
98 | // 用于加载内容
99 | Loader {
100 | id: content_loader
101 | anchors.top: title_item.bottom
102 | }
103 |
104 | // 显示-动画组
105 | ParallelAnimation {
106 | id: show_anim
107 | // 透明度-从透明到显示
108 | NumberAnimation {
109 | target: control
110 | property: "opacity"
111 | // 从当前值到显示
112 | from: control.opacity
113 | to: 1
114 | duration: 2000
115 | }
116 | // 位置
117 | NumberAnimation {
118 | target: control
119 | property: "y"
120 | // 从当前值到显示
121 | from: control.y
122 | to: Screen.virtualY + Screen.height - height
123 | duration: 2000
124 | }
125 | // 动画开始,显示窗口
126 | onStarted: {
127 | close_timer.time_count = 5
128 | control.show()
129 | }
130 | // 动画结束启动关闭倒计时
131 | onFinished: {
132 | close_timer.start()
133 | }
134 | }
135 |
136 | // 关闭-动画组
137 | ParallelAnimation {
138 | id: hide_anim
139 | // 透明度-从显示到透明
140 | NumberAnimation {
141 | target: control
142 | property: "opacity"
143 | // 从当前值到隐藏
144 | from: control.opacity
145 | to: 0
146 | duration: 2000
147 | }
148 | // 位置
149 | NumberAnimation {
150 | target: control
151 | property: "y"
152 | // 从当前值到隐藏
153 | from: control.y
154 | to: Screen.virtualY + Screen.height
155 | duration: 2000
156 | }
157 | // 结束动画开始
158 | onStarted: {
159 | close_timer.time_count = 0
160 | }
161 | // 动画结束关闭窗口
162 | onFinished: {
163 | close_timer.stop()
164 | control.close()
165 | }
166 | }
167 |
168 | // 显示弹框
169 | function showTip()
170 | {
171 | hide_anim.stop()
172 | show_anim.start()
173 | }
174 | // 隐藏弹框
175 | function hideTip()
176 | {
177 | show_anim.stop()
178 | hide_anim.start()
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/BasicComponent/BasicCheckBox.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5勾选框样式自定义
7 | // 龚建波 2025-03-07
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\CheckBox.qml
9 | // CheckBox继承自AbstractButton,增加了checkState/nextCheckState/tristate三个属性
10 | T.CheckBox {
11 | id:control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义文本颜色
17 | property color textColor: "black"
18 | // 定义勾选框背景色
19 | property color indicatorBackgroundColor: "transparent"
20 | // 定义勾选框颜色
21 | // pressed按下,hovered鼠标悬停,highlighted高亮,checked选中
22 | property color indicatorBorderColor: control.pressed
23 | ? Qt.darker(themeColor)
24 | : (control.hovered || control.highlighted)
25 | ? Qt.lighter(themeColor)
26 | : control.checked
27 | ? themeColor
28 | : themeColor
29 | // 定义勾选图标颜色
30 | property color indicatorButtonColor: indicatorBorderColor
31 | // 定义勾选框边框宽度
32 | property int indicatorBorderWidth: 1
33 | // 定义勾选框圆角
34 | property int indicatorRadius: 0
35 |
36 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
37 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
38 | // inset和padding都是Control基类定义的,默认为0
39 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
40 | // implicitContentWidth + leftPadding + rightPadding)
41 | // 默认高度
42 | // implicitIndicatorHeight为indicator勾选部件的默认高度
43 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
44 | // implicitContentHeight + topPadding + bottomPadding,
45 | // implicitIndicatorHeight + topPadding + bottomPadding)
46 | // 通过内容计算宽高可能会导致一组按钮的宽高都不齐,可以用固定的默认宽高,或者固定高但是宽度自适应
47 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
48 | implicitHeight: implicitIndicatorHeight + topPadding + bottomPadding
49 | // 边距
50 | padding: 0
51 | // 上下边距可以直接用verticalPadding,因为遇到过相关bug就单独设置下
52 | topPadding: 3
53 | bottomPadding: 3
54 | // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
55 | leftPadding: 8
56 | rightPadding: 8
57 | // 图标和文字间距
58 | spacing: 6
59 | // 字体设置
60 | // 也可以给QApplication设置全局的默认字体
61 | font{
62 | family: "SimSun"
63 | pixelSize: 16
64 | }
65 | // 勾选图标设置
66 | // icon.width: 24
67 | // icon.height: 24
68 | // 这个图标资源是Control模块默认提供的
69 | icon.source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/check.png"
70 | // icon.color: indicatorButtonColor
71 |
72 | // Control组件点击之后,后续按空格也会触发点击,可以把空格过滤掉
73 | Keys.onPressed: event.accepted = (event.key === Qt.Key_Space)
74 | Keys.onReleased: event.accepted = (event.key === Qt.Key_Space)
75 | // 是否可以选中,设置tristate后似乎禁止勾选失效了
76 | // checkable: false
77 | // 是否启用三态模式,选中/半选中/未选中
78 | // 半选中一般用在树形列表,只选择了子项中的一部分时
79 | tristate: false
80 | // 当前的选中状态
81 | // - Qt.Unchecked 未选中
82 | // - Qt.PartiallyChecked 半选中
83 | // - Qt.Checked 选中
84 | // checkState: Qt.Checked
85 | // 点击后下一次的状态
86 | // nextCheckState: function() { return (checkState === Qt.Checked) ? Qt.Unchecked : Qt.Checked }
87 | // 是否检测hover鼠标悬停,默认会跟随父组件的设置
88 | hoverEnabled: true
89 | // 文本内容
90 | // text: "CheckBox"
91 |
92 | // 按下后移出按钮范围触发取消
93 | // onCanceled: console.log("canceled")
94 | // 点击信号
95 | // onClicked: console.log("clicked")
96 | // 点击并变更状态后触发toggled,设置tristate后没触发
97 | // onToggled: console.log("toggled")
98 |
99 | // 勾选图标
100 | indicator: Rectangle {
101 | implicitWidth: 24
102 | implicitHeight: 24
103 | // 设置勾选图标位置
104 | x: control.text ? control.leftPadding : control.leftPadding + (control.availableWidth - width) / 2
105 | y: control.topPadding + (control.availableHeight - height) / 2
106 | // indicator默认层级在background上,这里只是显示勾选部件的边框,所以color设置成透明
107 | color: control.indicatorBackgroundColor
108 | // 图标外面的矩形框
109 | radius: control.indicatorRadius
110 | border.width: control.indicatorBorderWidth
111 | border.color: control.indicatorBorderColor
112 | // 用impl模块的ColorImage,便于设置图标颜色
113 | ColorImage {
114 | anchors.centerIn: parent
115 | // 借用Button已有的icon接口设置图标url
116 | source: control.icon.source
117 | // 借用Button已有的icon接口设置图标尺寸
118 | sourceSize: Qt.size(control.icon.width, control.icon.height)
119 | // 借用Button已有的icon接口设置图标颜色
120 | color: control.indicatorButtonColor
121 | // 勾选状态下显示勾选图标
122 | visible: control.checkState === Qt.Checked
123 | }
124 | // 部分选中(Qt.PartiallyChecked)时不显示勾,显示一个实心方形
125 | // 设置CheckBox的tristate为true后才支持三态
126 | Rectangle {
127 | anchors.centerIn: parent
128 | width: parent.width / 2
129 | height: parent.height / 2
130 | radius: control.indicatorRadius
131 | color: control.indicatorButtonColor
132 | // 半选状态下显示实心方形
133 | visible: control.checkState === Qt.PartiallyChecked
134 | }
135 | }
136 |
137 | // 文本描述
138 | // 源码中用的CheckLabel继承自Text,只是默认设置了AlignLeft+AlignVCenter+ElideRight
139 | contentItem: Text {
140 | // 文字内容
141 | text: control.text
142 | // 字体设置
143 | font: control.font
144 | // 颜色设置
145 | color: control.textColor
146 | // 文字对齐方式
147 | horizontalAlignment: Text.AlignHCenter
148 | verticalAlignment: Text.AlignVCenter
149 | // 单独设置文本组件的渲染方式
150 | renderType: Text.NativeRendering
151 | // 文字超出按钮范围显示省略号
152 | elide: Text.ElideRight
153 | // contentItem宽度是包含了indicator的,通过padding把位置留出来
154 | leftPadding: control.indicator.width + control.spacing
155 | rightPadding: 0
156 | }
157 |
158 | // 背景
159 | // background: Rectangle { }
160 | }
161 |
--------------------------------------------------------------------------------
/BasicComponent/BasicSpinBox.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import QtQuick.Controls.impl 2.15
4 | import QtQuick.Templates 2.15 as T
5 |
6 | // Qt5数值编辑框样式自定义
7 | // 龚建波 2025-04-24
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\SpinBox.qml
9 | T.SpinBox {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 | // 定义文本颜色
16 | property color textColor: "black"
17 | // 定义边框颜色
18 | property color borderColor: themeColor
19 | // 定义边框宽度
20 | property int borderWidth: 1
21 | // 定义文本背景色
22 | property color backgroundNormalColor: "white"
23 | // 定义文本背景焦点色
24 | property color backgroundFocusColor: backgroundNormalColor
25 | // 定义按钮图标颜色
26 | property color indicatorNormalColor: "black"
27 | property color indicatorDisableColor: "gray"
28 | // 定义按钮颜色
29 | property color buttonNormalColor: "white"
30 | property color buttonHoverColor: Qt.lighter(buttonNormalColor)
31 | property color buttonPressColor: Qt.darker(buttonNormalColor)
32 |
33 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
34 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右按钮宽度+三者间距
35 | // inset和padding都是Control基类定义的,默认为0
36 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
37 | // contentItem.implicitWidth + 2 * padding +
38 | // up.implicitIndicatorWidth +
39 | // down.implicitIndicatorWidth)
40 | // 默认高度
41 | // implicitHeight: Math.max(implicitContentHeight + topPadding + bottomPadding,
42 | // implicitBackgroundHeight,
43 | // up.implicitIndicatorHeight,
44 | // down.implicitIndicatorHeight)
45 | implicitWidth: 120
46 | implicitHeight: 30
47 |
48 | // 部件间隔,没用到
49 | // spacing: 0
50 | // 边距
51 | padding: 0
52 | // 左边距包含了左侧按钮宽度
53 | leftPadding: padding + (down.indicator ? down.indicator.width : 0)
54 | // 右边距包含了右侧按钮宽度
55 | rightPadding: padding + (up.indicator ? up.indicator.width : 0)
56 | // 字体设置
57 | // 也可以给QApplication设置全局的默认字体
58 | font{
59 | family: "SimSun"
60 | pixelSize: 16
61 | }
62 |
63 | // 最小值,默认0
64 | // from: 0
65 | // 最大值,默认99
66 | // to: 99
67 | // 当前值,默认0
68 | // value: 0
69 | // 点加减后的步进值,默认1
70 | // stepSize: 1
71 | // 输入行为,传给InputMethod,如Qt.ImhDate限制为日期格式
72 | // inputMethodHints: Qt.ImhNone
73 | // wrap为true则上下按钮设置是循环设置范围内值的,默认false
74 | // wrap: true
75 | // 限制输入
76 | validator: IntValidator {
77 | // 语言区域会影响字符格式化
78 | locale: control.locale.name
79 | // 最小值
80 | bottom: Math.min(control.from, control.to)
81 | // 最大值
82 | top: Math.max(control.from, control.to)
83 | }
84 | // 值到文本映射,返回的值给control.displayText
85 | // textFromValue: function(val) { return val }
86 | // 文本到值映射
87 | // valueFromText: function(txt) { return txt }
88 |
89 | // 中间的数值框
90 | contentItem: Rectangle {
91 | // 增加z值可以把border叠加到按钮之上
92 | z: 2
93 | color: control.activeFocus
94 | ? backgroundFocusColor
95 | : backgroundNormalColor
96 | // 编辑/显示内容
97 | TextInput {
98 | anchors.fill: parent
99 | // 显示的内容,可以通过textFromValue转换
100 | text: control.displayText
101 | // 一些编辑器属性绑定SpinBox设置的属性
102 | readOnly: !control.editable
103 | validator: control.validator
104 | inputMethodHints: control.inputMethodHints
105 | // 文本颜色
106 | color: control.textColor
107 | // 鼠标框选文本样式
108 | selectByMouse: true
109 | selectedTextColor: "white"
110 | selectionColor: "black"
111 | // 字体设置
112 | font: control.font
113 | // 单独设置文本组件的渲染方式
114 | renderType: Text.NativeRendering
115 | // 居中对齐
116 | horizontalAlignment: Qt.AlignHCenter
117 | verticalAlignment: Qt.AlignVCenter
118 | }
119 | // 编辑框边框
120 | Rectangle {
121 | anchors.fill: parent
122 | anchors.margins: 0
123 | // 负边距和按钮边框重合
124 | anchors.leftMargin: -control.borderWidth
125 | anchors.rightMargin: -control.borderWidth
126 | color: "transparent"
127 | border.color: control.borderColor
128 | border.width: control.borderWidth
129 | }
130 | }
131 |
132 | // 增加按钮
133 | up.indicator: Rectangle {
134 | // 定位到右侧
135 | x: parent.width - width
136 | height: parent.height
137 | implicitWidth: 30
138 | implicitHeight: 30
139 | // 按钮背景色
140 | color: up.pressed
141 | ? control.buttonPressColor
142 | : up.hovered
143 | ? control.buttonHoverColor
144 | : control.buttonNormalColor
145 | // 按钮边框
146 | border.width: control.borderWidth
147 | border.color: control.borderColor
148 | // 加号
149 | Rectangle {
150 | x: (parent.width - width) / 2
151 | y: (parent.height - height) / 2
152 | width: parent.width / 3
153 | height: 2
154 | color: enabled ? control.indicatorNormalColor : control.indicatorDisableColor
155 | }
156 | Rectangle {
157 | x: (parent.width - width) / 2
158 | y: (parent.height - height) / 2
159 | width: 2
160 | height: parent.width / 3
161 | color: enabled ? control.indicatorNormalColor : control.indicatorDisableColor
162 | }
163 | }
164 |
165 | // 减少按钮
166 | down.indicator: Rectangle {
167 | // 定位到左侧
168 | x: 0
169 | height: parent.height
170 | implicitWidth: 30
171 | implicitHeight: 30
172 | // 按钮背景色
173 | color: down.pressed
174 | ? control.buttonPressColor
175 | : down.hovered
176 | ? control.buttonHoverColor
177 | : control.buttonNormalColor
178 | // 按钮边框
179 | border.width: control.borderWidth
180 | border.color: control.borderColor
181 | // 减号
182 | Rectangle {
183 | x: (parent.width - width) / 2
184 | y: (parent.height - height) / 2
185 | width: parent.width / 3
186 | height: 2
187 | color: enabled ? control.indicatorNormalColor : control.indicatorDisableColor
188 | }
189 | }
190 |
191 | // 不设置整体背景
192 | background: Item { }
193 | }
194 |
--------------------------------------------------------------------------------
/BasicComponent/BasicButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5按钮样式自定义
7 | // 龚建波 2025-03-05
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\Button.qml
9 | // Button继承自AbstractButton,只增加了flat和highlighted两个不常用的属性
10 | T.Button {
11 | id: control
12 |
13 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
14 | // 定义主题颜色
15 | property color themeColor: "darkCyan"
16 | // 定义文本颜色
17 | property color textColor: "white"
18 | // 定义背景颜色
19 | // pressed按下,hovered鼠标悬停,highlighted高亮,checked选中
20 | property color backgroundColor: (control.pressed || control.checked)
21 | ? Qt.darker(themeColor)
22 | : (control.hovered || control.highlighted)
23 | ? Qt.lighter(themeColor)
24 | : themeColor
25 | // 定义边框宽度
26 | property int borderWidth: 0
27 | // 定义边框颜色
28 | property color borderColor: Qt.darker(themeColor)
29 | // 定义边框圆角
30 | property int radius: 0
31 |
32 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
33 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
34 | // inset和padding都是Control基类定义的,默认为0
35 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
36 | // implicitContentWidth + leftPadding + rightPadding)
37 | // 默认高度
38 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
39 | // implicitContentHeight + topPadding + bottomPadding)
40 | // 通过内容计算宽高可能会导致一组按钮的宽高都不齐,可以用固定的默认宽高,或者固定高但是宽度自适应
41 | implicitWidth: implicitContentWidth + leftPadding + rightPadding
42 | implicitHeight: implicitContentHeight + topPadding + bottomPadding
43 | // 边距
44 | padding: 0
45 | // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
46 | leftPadding: 8
47 | rightPadding: 8
48 | // 图标和文字间距
49 | spacing: 6
50 | // checkable: true
51 | // 字体设置
52 | // 也可以给QApplication设置全局的默认字体
53 | font{
54 | family: "SimSun"
55 | pixelSize: 16
56 | }
57 | // 按钮图标设置
58 | // icon.width: 24
59 | // icon.height: 24
60 | // icon.source: "url"
61 | icon.color: textColor
62 |
63 | // Control组件点击之后,后续按空格也会触发点击,可以把空格过滤掉
64 | Keys.onPressed: event.accepted = (event.key === Qt.Key_Space)
65 | Keys.onReleased: event.accepted = (event.key === Qt.Key_Space)
66 | // 可以绑定一个Action到按钮,这样点击后直接触发Action的逻辑,Action一般和菜单或者快捷键相关
67 | // action: Action { }
68 | // 在内容contentItem和背景background图层间可以设置indicator来实现一些样式效果,如点击时的水波纹
69 | // 三者的堆叠关系可以通过设置z值来调节谁在前谁在后
70 | // indicator: Rectangle { anchors.fill: parent; color: "red" }
71 | // 独占性,即同父时达到ButtonGroup那种单选效果
72 | autoExclusive: false
73 | // 是否可以选中
74 | checkable: false
75 | // 是否选中
76 | checked: false
77 | // 是否检测hover鼠标悬停,默认会跟随父组件的设置
78 | hoverEnabled: true
79 | // 按住后是否重复触发点击
80 | autoRepeat: false
81 | // 按住后重复触发点击的初次判断延时ms
82 | autoRepeatDelay: 300
83 | // 按住后重复触发点击的间隔时间ms
84 | autoRepeatInterval: 100
85 | // 组件默认样式下的显示方式,可以纯文本、纯图标、上下组合、左右组合
86 | // display: AbstractButton.TextBesideIcon
87 | // 文本内容
88 | // text: "Button"
89 | // 组件在视觉上是否处于按下状态,而pressed只读属性是物理意义上的按下
90 | // down: false
91 | // pressed和hovered只读属性,反应按键的按下或者鼠标悬停状态
92 | // pressX和pressY只读属性,反应按键按下时点击的位置
93 |
94 | // 按下后移出按钮范围触发取消
95 | // onCanceled: console.log("canceled")
96 | // 按钮点击信号
97 | // onClicked: console.log("clicked")
98 | // 按钮双击信号,会先触发一次clicked
99 | // onDoubleClicked: console.log("double clicked")
100 | // 按钮长按信号
101 | // onPressAndHold: console.log("press and hold")
102 | // 按钮按下信号
103 | // onPressed: console.log("pressed")
104 | // 按钮弹起信号
105 | // onReleased: console.log("released")
106 |
107 | // 按钮中显示的内容
108 | // 源码用的impl里的IconLabel,带图标和文字,但不能单独设置renderType: Text.NativeRendering
109 | // 但可以用QQuickWindow设置全局的renderType
110 | // contentItem: IconLabel {
111 | // spacing: control.spacing
112 | // mirrored: control.mirrored
113 | // display: control.display
114 | // icon: control.icon
115 | // text: control.text
116 | // font: control.font
117 | // color: control.textColor
118 | // }
119 | contentItem: Item {
120 | implicitWidth: btn_text.implicitWidth + btn_icon.iconWidth
121 | implicitHeight: 30
122 | // Row会根据内容计算出implicitWidth
123 | Row {
124 | id: btn_row
125 | // 居中显示
126 | anchors.centerIn: parent
127 | // 图标和文字间隔
128 | spacing: control.spacing
129 | // 用impl模块的ColorImage,便于设置图标颜色
130 | ColorImage {
131 | id: btn_icon
132 | // 除了text外的内容宽度
133 | property int iconWidth: (btn_icon.implicitWidth > 0 ? btn_icon.implicitWidth + btn_row.spacing : 0)
134 | // 在Row内上下居中
135 | anchors.verticalCenter: parent.verticalCenter
136 | // 借用Button已有的icon接口设置图标url
137 | source: control.icon.source
138 | // 借用Button已有的icon接口设置图标尺寸
139 | sourceSize: Qt.size(control.icon.width, control.icon.height)
140 | // 借用Button已有的icon接口设置图标颜色
141 | color: control.icon.color
142 | }
143 | Text {
144 | id: btn_text
145 | // 如果按钮宽度小于默认宽度文字需要显示省略号,得先设置Text宽度
146 | width: (control.width - btn_icon.iconWidth - control.leftPadding - control.rightPadding)
147 | // 在Row内上下居中
148 | anchors.verticalCenter: parent.verticalCenter
149 | // 按钮文字内容
150 | text: control.text
151 | // 文字颜色
152 | color: control.textColor
153 | // 单独设置文本组件的渲染方式
154 | renderType: Text.NativeRendering
155 | // 文字对齐方式,设置Text宽高后设置才有意义
156 | verticalAlignment: Text.AlignVCenter
157 | horizontalAlignment: Text.AlignHCenter
158 | // 换行设置,按钮一般没有换行
159 | // wrapMode: Text.NoWrap
160 | // 文字超出按钮范围显示省略号
161 | elide: Text.ElideMiddle
162 | // 字体设置
163 | font: control.font
164 | }
165 | }
166 | }
167 |
168 | // 背景
169 | background: Rectangle {
170 | // control设置了背景无关的宽高,这里也可以不设置默认宽高
171 | implicitWidth: control.implicitWidth
172 | implicitHeight: control.implicitHeight
173 | // 背景圆角
174 | radius: control.radius
175 | // 背景颜色
176 | color: control.backgroundColor
177 | // 背景边框
178 | border.width: control.borderWidth
179 | border.color: control.borderColor
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/BasicComponent/GradientComboBox.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5下拉框样式自定义
7 | // 龚建波 2025-03-10
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ComboBox.qml
9 | T.ComboBox {
10 | id: control
11 |
12 | // 定义主题颜色
13 | property color themeColor: "darkCyan"
14 | // 定义文本颜色
15 | property color textColor: "white"
16 | // 定义背景渐变色
17 | property Gradient backgroundGradient: (control.pressed || control.checked)
18 | ? _pressGradient
19 | : (control.hovered || control.highlighted)
20 | ? _hoverGradient
21 | : _normalGradient
22 | property Gradient _pressGradient: Gradient {
23 | GradientStop { position: 0.0; color: Qt.darker(themeColor) }
24 | GradientStop { position: 0.5; color: themeColor }
25 | GradientStop { position: 1.0; color: Qt.lighter(themeColor) }
26 | }
27 | property Gradient _hoverGradient: Gradient {
28 | GradientStop { position: 0.0; color: themeColor }
29 | GradientStop { position: 0.5; color: Qt.darker(themeColor) }
30 | GradientStop { position: 1.0; color: themeColor }
31 | }
32 | property Gradient _normalGradient: Gradient {
33 | GradientStop { position: 0.0; color: Qt.lighter(themeColor) }
34 | GradientStop { position: 0.5; color: themeColor }
35 | GradientStop { position: 1.0; color: Qt.lighter(themeColor) }
36 | }
37 | property Gradient _lightToNormal: Gradient {
38 | GradientStop { position: 0.0; color: Qt.lighter(themeColor) }
39 | GradientStop { position: 0.6; color: themeColor }
40 | }
41 | property Gradient _normalToDark: Gradient {
42 | GradientStop { position: 0.2; color: themeColor }
43 | GradientStop { position: 0.9; color: Qt.darker(themeColor) }
44 | }
45 | // 定义边框圆角
46 | property int radius: 4
47 | // 定义每个item的高度
48 | property int itemHeight: height
49 | // 定义文本的左右padding
50 | property int itemPadding: 10
51 | // 定义下拉选项高亮颜色
52 | property color itemHighlightColor: Qt.darker(themeColor)
53 | // 定义下拉选项默认颜色
54 | property color itemNormalColor: themeColor
55 | // 定义下拉图标宽度,小于0就用图标宽度
56 | property int indicatorWidth: -1
57 | // 定义下拉按钮左右边距,不然rightPadding和indicator宽度循环引用计算
58 | property int indicatorPadding: 3
59 | // 定义下拉按钮图标
60 | property url indicatorSource: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/double-arrow.png"
61 | // 定义下拉图标颜色
62 | property color indicatorColor: textColor
63 | // 定义下拉列表展示选项个数,多余的需要滚动
64 | property int showCount: 5
65 | // 定义文本显示的回调
66 | // 如文字左右加[],{ return String('[%1]').arg(value); }
67 | // 如根据编码显示对应文字,{ return switch(value) { case:... }; }
68 | // 不能用在编辑的场景
69 | property var showFunc: function(value){
70 | return value;
71 | }
72 |
73 | // 默认固定宽高
74 | implicitWidth: 120
75 | implicitHeight: 30
76 | // 边距
77 | padding: 0
78 | // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
79 | leftPadding: 0
80 | // 右边距需要把按钮位置留出来
81 | rightPadding: indicator.width
82 | // 图标和文字间距,用indicatorPadding计算,这里为0
83 | spacing: 0
84 | // 字体设置
85 | // 也可以给QApplication设置全局的默认字体
86 | font{
87 | family: "SimSun"
88 | pixelSize: 16
89 | }
90 | // 是否检测hover鼠标悬停,默认会跟随父组件的设置
91 | hoverEnabled: true
92 |
93 | // 下拉选项样式
94 | delegate: ItemDelegate {
95 | // 选项宽高
96 | width: ListView.view.width
97 | height: control.itemHeight
98 | // 选项边距
99 | padding: control.padding
100 | leftPadding: control.itemPadding
101 | rightPadding: control.itemPadding
102 | contentItem: Text {
103 | // 选项文字内容
104 | text: control.showFunc(control.textRole
105 | ? (Array.isArray(control.model)
106 | ? modelData[control.textRole]
107 | : model[control.textRole])
108 | : modelData)
109 | // 字体设置
110 | font: control.font
111 | // 颜色设置
112 | color: control.textColor
113 | // 文字对齐方式
114 | horizontalAlignment: Text.AlignLeft
115 | verticalAlignment: Text.AlignVCenter
116 | // 单独设置文本组件的渲染方式
117 | renderType: Text.NativeRendering
118 | // 文字超出按钮范围显示省略号
119 | elide: Text.ElideRight
120 | }
121 | // 是否检测hover鼠标悬停
122 | hoverEnabled: control.hoverEnabled
123 | // 选项背景
124 | background: Rectangle {
125 | gradient: (control.highlightedIndex === index)
126 | ? _normalToDark
127 | : _lightToNormal
128 | }
129 | }
130 |
131 | // 下拉按钮图标
132 | indicator: Item {
133 | id: box_indicator
134 | // 下拉图标定位
135 | x: control.width - width
136 | y: control.topPadding + (control.availableHeight - height) / 2
137 | // 下拉按钮区域占的尺寸
138 | width: (indicatorWidth < 0 ? box_indicator_img.width : indicatorWidth) + control.indicatorPadding * 2
139 | height: control.height
140 | // 下拉按钮图标
141 | ColorImage {
142 | id: box_indicator_img
143 | anchors.centerIn: parent
144 | // 图标颜色
145 | color: control.indicatorColor
146 | // 图标url
147 | source: control.indicatorSource
148 | }
149 | }
150 |
151 | // 当前展示内容项,可以单独封装然后组合在这里
152 | // 如果不需要支持编辑,用Text或者Label也行
153 | contentItem: T.TextField {
154 | id: content_edit
155 | // 左右边距
156 | leftPadding: control.itemPadding
157 | rightPadding: control.itemPadding
158 | // 文字内容
159 | text: control.editable
160 | ? control.editText
161 | : control.showFunc(control.displayText)
162 | // 字体设置
163 | font: control.font
164 | // 文字颜色
165 | color: control.textColor
166 | // 默认鼠标选取文本设置为false
167 | selectByMouse: true
168 | // 选中文本的颜色
169 | selectedTextColor: "white"
170 | // 选中文本背景色
171 | selectionColor: "black"
172 | // 超出区域后截断不显示
173 | clip: true
174 | // 单独设置文本组件的渲染方式
175 | renderType: Text.NativeRendering
176 | // 文字对齐方式
177 | horizontalAlignment: Text.AlignLeft
178 | verticalAlignment: Text.AlignVCenter
179 | // 设置为可编辑时,才允许编辑
180 | enabled: control.editable
181 | // 设置为可编辑时,填充的文本自动滚动到末尾
182 | autoScroll: control.editable
183 | // 下拉时只读
184 | readOnly: control.down
185 | // 对输入内容限制,如字符隐藏/只允许输入数字等
186 | // 区别于echoMode,如密码非明文显示 echoMode: TextInput.Password
187 | inputMethodHints: control.inputMethodHints
188 | // 输入的正则限制
189 | validator: control.validator
190 | // 编辑框背景
191 | background: Rectangle {
192 | // 可编辑时才显示
193 | visible: control.enabled && control.editable
194 | color: content_edit.activeFocus
195 | ? Qt.rgba(0.6, 0.6, 0.6, 0.6)
196 | : "transparent"
197 | }
198 | }
199 |
200 | // 背景
201 | background: Rectangle {
202 | // control设置了背景无关的宽高,这里也可以不设置默认宽高
203 | implicitWidth: control.implicitWidth
204 | implicitHeight: control.implicitHeight
205 | // 背景圆角
206 | radius: control.radius
207 | // 背景渐变
208 | gradient: control.backgroundGradient
209 | }
210 |
211 | // 弹出框
212 | popup: T.Popup {
213 | // 默认向下弹出,如果距离不够,y会自动调整(Popup的特性,会被限制在Window内)
214 | y: control.height
215 | width: control.width
216 | // 根据定义的showCount来设置最多显示item个数
217 | implicitHeight: control.delegateModel
218 | ? (control.delegateModel.count < showCount
219 | ? contentItem.implicitHeight
220 | : control.showCount * control.itemHeight) + 2
221 | : 0
222 | // 留给边框的位置
223 | padding: 1
224 | contentItem: ListView {
225 | implicitHeight: contentHeight
226 | // 超出区域后截断不显示
227 | clip: true
228 | // 列表内容
229 | model: control.popup.visible ? control.delegateModel : null
230 | // 选中项同步
231 | currentIndex: control.highlightedIndex
232 | // 滚动效果
233 | // - NoSnap 默认任意位置停止NoSnap
234 | // - SnapToItem 滑动结束时顶部对齐(不会只漏半截),滑到末尾才是底部对齐
235 | // - SnapOneItem 滑动结束时最多移动一项
236 | // SnapToItem可能会导致原地来回跳无法滚动,SnapOneItem不适合做滚动
237 | // snapMode: ListView.NoSnap
238 | // 高亮移动动画时间,源码设置为0
239 | highlightMoveDuration: 0
240 | // ScrollBar.horizontal: ScrollBar { visible: false }
241 | // 竖向滚动条,可以单独封装然后组合在这里
242 | ScrollBar.vertical: ScrollBar {
243 | id: box_bar
244 | // 滚动条宽度
245 | implicitWidth: 10
246 | visible: (control.delegateModel && control.delegateModel.count > showCount)
247 | // 滚动条整体背景
248 | // background: Rectangle { }
249 | // 拖动的滑动条样式
250 | contentItem: Rectangle {
251 | implicitWidth: 10
252 | radius: width / 2
253 | color: box_bar.pressed
254 | ? Qt.rgba(0.6, 0.6, 0.6)
255 | : Qt.rgba(0.6, 0.6, 0.6, 0.5)
256 | }
257 | }
258 | }
259 |
260 | // 弹出框背景(只有popup.padding显示出来了,其余部分被delegate背景遮挡)
261 | background: Rectangle {
262 | color: themeColor
263 | // color: Qt.lighter(themeColor)
264 | radius: control.radius
265 | }
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/CustomComponent/CustomItemShot.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Dialogs 1.2
3 |
4 | // 框选截图工具
5 | // 使用时需要anchors.fill目标,并把目标赋值给shotTarget
6 | // pop()初始化显示,close()隐藏
7 | // 注意要放在target Item的外部,不然截图组件的影像也被捕获了
8 |
9 | // 2019-12-21
10 | // 设置preventStealing: true防止鼠标事件被偷走
11 | // 如在ScrollView或Map中,MouseArea不能正常处理拖动效果
12 | Item {
13 | id: control
14 | visible: false
15 |
16 | property int areaMinSize: 35
17 | property int indicatorWidth: 14
18 | property color indicatorColor: "red"
19 | property alias areaBorder: shot_area.border
20 | property alias areaColo: shot_area.color
21 | property alias centerText: center_text.text
22 | property alias textColor: center_text.color
23 | property alias textFont: center_text.font
24 | // 要截图的Item,通过ShaderEffectSource来获取目标区域
25 | property alias shotTarget: shot_shader.sourceItem
26 |
27 | // 尺寸改变时,避免超出范围
28 | onWidthChanged: {
29 | if (shot_area.x + shot_area.width > control.width) {
30 | shot_area.width = control.width - shot_area.x
31 | }
32 | }
33 | onHeightChanged: {
34 | if (shot_area.y + shot_area.height > control.height) {
35 | shot_area.height = control.height - shot_area.y
36 | }
37 | }
38 |
39 | function pop() {
40 | shot_area.x = 0
41 | shot_area.y = 0
42 | shot_area.width = control.width
43 | shot_area.height = control.height
44 | control.visible = true
45 | }
46 |
47 | function close() {
48 | control.visible = false
49 | }
50 |
51 | function requestshot() {
52 | shot_dailog.open()
53 | }
54 |
55 | function quickitemshot(filepath) {
56 | shot_shader.sourceRect = Qt.rect(shot_area.x
57 | , shot_area.y
58 | , shot_area.width
59 | , shot_area.height)
60 | if (shotTarget && filepath) {
61 | shot_shader.grabToImage(function(result) {
62 | var saveresult = result.saveToFile(filepath)
63 | console.log("quickitemshot", saveresult, filepath)
64 | })
65 | }
66 | }
67 |
68 | FileDialog {
69 | id: shot_dailog
70 | visible: false
71 | title: "保存截图"
72 |
73 | // folder: shortcuts.home
74 | nameFilters: ["Image(*.png)"]
75 | // 另存文件名
76 | selectExisting: false
77 | // 确认
78 | onAccepted: {
79 | var selectpath = shot_dailog.fileUrl.toString()
80 | if (selectpath) {
81 | // 去掉url中的"file:///"
82 | control.quickitemshot(selectpath.replace(/^(file:\/{3})/, ""))
83 | }
84 | }
85 | // 取消
86 | // onRejected:
87 | // 如果不设置文件路径,那么就要App设置("organizationName", "organizationDomain")
88 | settings.fileName: "dialog.ini"
89 | }
90 |
91 | // 用于截图
92 | ShaderEffectSource {
93 | id: shot_shader
94 | visible: false
95 | anchors.fill: shot_area
96 | // 数据源
97 | // sourceItem:
98 | // 截图区域
99 | // sourceRect: shot_area.layer.sourceRect
100 | }
101 |
102 | //截屏区域
103 | //这里没有处理和窗口等比缩放
104 | Rectangle {
105 | id: shot_area
106 | implicitWidth: control.width
107 | implicitHeight: control.height
108 |
109 | color: Qt.rgba(0, 1, 0, 0.3)
110 | border{
111 | width: 1
112 | color: "red"
113 | }
114 |
115 | Text {
116 | id: center_text
117 | anchors.centerIn: parent
118 | text: "双击保存截图"
119 | color: "red"
120 | font{
121 | pixelSize: 16
122 | weight: Font.Bold
123 | }
124 | }
125 |
126 | MouseArea {
127 | id: click_area
128 | anchors.fill: parent
129 | property int pressPointX: 0
130 | property int pressPointY: 0
131 | preventStealing: true
132 | onPressed: {
133 | pressPointX = mouse.x
134 | pressPointY = mouse.y
135 | }
136 |
137 | onDoubleClicked: control.requestshot()
138 | onPositionChanged: {
139 | // 判断范围,贴边移动的情况需要细化处理
140 | if ((shot_area.x + mouse.x - pressPointX > 0)
141 | && (shot_area.x + shot_area.width + mouse.x - pressPointX <= control.width)) {
142 | shot_area.x += mouse.x - pressPointX
143 | }
144 | if ((shot_area.y + mouse.y - pressPointY > 0)
145 | && (shot_area.y + shot_area.height + mouse.y - pressPointY <= control.height)){
146 | shot_area.y += mouse.y - pressPointY
147 | }
148 | }
149 | }
150 |
151 | // 四角的四个拖动按钮
152 | Rectangle {
153 | id: btn_top_left
154 | width: control.indicatorWidth
155 | height: control.indicatorWidth
156 | radius: control.indicatorWidth / 2
157 | color: control.indicatorColor
158 | anchors{
159 | left: parent.left
160 | top: parent.top
161 | margins: -radius
162 | }
163 | MouseArea {
164 | property int pressPosX: 0
165 | property int pressPosY: 0
166 | anchors.fill: parent
167 | preventStealing: true
168 | onClicked: {
169 | pressPosX = mouse.x
170 | pressPosY = mouse.y
171 | }
172 | onPositionChanged: {
173 | var new_width = shot_area.width - mouse.x - pressPosX
174 | var new_height = shot_area.height - mouse.y - pressPosY
175 | if (new_width >= control.areaMinSize) {
176 | shot_area.width = new_width
177 | shot_area.x += mouse.x - pressPosX
178 | if (shot_area.x < 0) {
179 | shot_area.width += shot_area.x
180 | shot_area.x = 0
181 | }
182 | }
183 | if (new_height >= control.areaMinSize) {
184 | shot_area.height = new_height
185 | shot_area.y += mouse.y - pressPosY
186 | if (shot_area.y < 0) {
187 | shot_area.height += shot_area.y
188 | shot_area.y = 0
189 | }
190 | }
191 | }
192 | }
193 | }
194 | Rectangle {
195 | id: btn_top_right
196 | width: control.indicatorWidth
197 | height: control.indicatorWidth
198 | radius: control.indicatorWidth / 2
199 | color: control.indicatorColor
200 | anchors{
201 | right: parent.right
202 | top: parent.top
203 | margins: -radius
204 | }
205 | MouseArea {
206 | property int pressPosX: 0
207 | property int pressPosY: 0
208 | anchors.fill: parent
209 | preventStealing: true
210 | onClicked: {
211 | pressPosX = mouse.x
212 | pressPosY = mouse.y
213 | }
214 | onPositionChanged: {
215 | var new_width = shot_area.width + mouse.x - pressPosX
216 | var new_height = shot_area.height - mouse.y - pressPosY
217 | if (new_width >= control.areaMinSize) {
218 | shot_area.width = new_width
219 | if (shot_area.x + shot_area.width > control.width) {
220 | shot_area.width = control.width - shot_area.x
221 | }
222 | }
223 | if (new_height >= control.areaMinSize) {
224 | shot_area.height = new_height
225 | shot_area.y += mouse.y - pressPosY
226 | if (shot_area.y < 0) {
227 | shot_area.height += shot_area.y
228 | shot_area.y = 0
229 | }
230 | }
231 | }
232 | }
233 | }
234 | Rectangle {
235 | id: btn_bottom_left
236 | width: control.indicatorWidth
237 | height: control.indicatorWidth
238 | radius: control.indicatorWidth / 2
239 | color: control.indicatorColor
240 | anchors{
241 | left: parent.left
242 | bottom: parent.bottom
243 | margins: -radius
244 | }
245 | MouseArea {
246 | property int pressPosX: 0
247 | property int pressPosY: 0
248 | anchors.fill: parent
249 | preventStealing: true
250 | onClicked: {
251 | pressPosX = mouse.x
252 | pressPosY = mouse.y
253 | }
254 | onPositionChanged: {
255 | var new_width = shot_area.width - mouse.x - pressPosX
256 | var new_height = shot_area.height + mouse.y - pressPosY
257 | if (new_width >= control.areaMinSize) {
258 | shot_area.width = new_width
259 | shot_area.x += mouse.x - pressPosX
260 | if (shot_area.x < 0) {
261 | shot_area.width += shot_area.x
262 | shot_area.x = 0
263 | }
264 | }
265 | if (new_height >= control.areaMinSize) {
266 | shot_area.height = new_height
267 | if (shot_area.y + shot_area.height > control.height) {
268 | shot_area.height = control.height - shot_area.y
269 | }
270 | }
271 | }
272 | }
273 | }
274 | Rectangle {
275 | width: control.indicatorWidth
276 | height: control.indicatorWidth
277 | radius: control.indicatorWidth / 2
278 | color: control.indicatorColor
279 | anchors{
280 | right: parent.right
281 | bottom: parent.bottom
282 | margins: -radius
283 | }
284 | MouseArea {
285 | property int pressPosX: 0
286 | property int pressPosY: 0
287 | anchors.fill: parent
288 | preventStealing: true
289 | onClicked: {
290 | pressPosX = mouse.x
291 | pressPosY = mouse.y
292 | }
293 | onPositionChanged: {
294 | var new_width = shot_area.width + mouse.x - pressPosX
295 | var new_height = shot_area.height + mouse.y - pressPosY
296 | if (new_width >= control.areaMinSize) {
297 | shot_area.width = new_width
298 | if (shot_area.x + shot_area.width > control.width) {
299 | shot_area.width = control.width - shot_area.x
300 | }
301 | }
302 | if (new_height >= control.areaMinSize) {
303 | shot_area.height = new_height
304 | if (shot_area.y + shot_area.height > control.height) {
305 | shot_area.height = control.height - shot_area.y
306 | }
307 | }
308 | }
309 | }
310 | }
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/BasicComponent/BasicComboBox.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Templates 2.15 as T
3 | import QtQuick.Controls 2.15
4 | import QtQuick.Controls.impl 2.15
5 |
6 | // Qt5下拉框样式自定义
7 | // 龚建波 2025-03-10
8 | // 参考:qt-everywhere-src-5.15.2\qtquickcontrols2\src\imports\controls\ComboBox.qml
9 | T.ComboBox {
10 | id: control
11 |
12 | // 可以像源码一样,定义一个全局的样式,然后取全局样式中对应的颜色
13 | // 定义主题颜色
14 | property color themeColor: "darkCyan"
15 | // 定义文本颜色
16 | property color textColor: "white"
17 | // 定义背景颜色
18 | // down按下或下拉状态,hovered悬停状态
19 | property color backgroundColor: control.down
20 | ? Qt.darker(themeColor)
21 | : control.hovered
22 | ? Qt.lighter(themeColor)
23 | : themeColor
24 | // 定义边框宽度
25 | property int borderWidth: 1
26 | // 定义边框颜色
27 | property color borderColor: Qt.darker(themeColor)
28 | // 定义边框圆角
29 | property int radius: 0
30 | // 定义每个item的高度
31 | property int itemHeight: height
32 | // 定义文本的左右padding
33 | property int itemPadding: 10
34 | // 定义下拉选项高亮颜色
35 | property color itemHighlightColor: Qt.darker(themeColor)
36 | // 定义下拉选项默认颜色
37 | property color itemNormalColor: themeColor
38 | // 定义下拉图标宽度,小于0就用图标宽度
39 | property int indicatorWidth: -1
40 | // 定义下拉按钮左右边距,不然rightPadding和indicator宽度循环引用计算
41 | property int indicatorPadding: 3
42 | // 定义下拉按钮图标
43 | property url indicatorSource: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/double-arrow.png"
44 | // 定义下拉图标颜色
45 | property color indicatorColor: textColor
46 | // 定义下拉列表展示选项个数,多余的需要滚动
47 | property int showCount: 5
48 | // 定义文本显示的回调
49 | // 如文字左右加[],{ return String('[%1]').arg(value); }
50 | // 如根据编码显示对应文字,{ return switch(value) { case:... }; }
51 | // 不能用在编辑的场景
52 | property var showFunc: function(value){
53 | return value;
54 | }
55 |
56 | // 默认宽度,参考Qt源码的写法,实际应用可以删减
57 | // Math.max表示取两者中最大值,1为默认背景宽度+左右偏移值,2为默认内容宽度+左右边距
58 | // inset和padding都是Control基类定义的,默认为0
59 | // implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
60 | // implicitContentWidth + leftPadding + rightPadding)
61 | // 默认高度
62 | // implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
63 | // implicitContentHeight + topPadding + bottomPadding)
64 | // 默认固定宽高
65 | implicitWidth: 120
66 | implicitHeight: 30
67 | // 边距
68 | padding: 0
69 | // 左右边距可以直接用horizontalPadding,因为遇到过相关bug就单独设置下
70 | leftPadding: 0
71 | // 右边距需要把按钮位置留出来
72 | rightPadding: indicator.width
73 | // 图标和文字间距,用indicatorPadding计算,这里为0
74 | spacing: 0
75 | // 字体设置
76 | // 也可以给QApplication设置全局的默认字体
77 | font{
78 | family: "SimSun"
79 | pixelSize: 16
80 | }
81 | // 是否检测hover鼠标悬停,默认会跟随父组件的设置
82 | hoverEnabled: true
83 |
84 | // Control组件点击之后,后续按空格也会触发点击,可以把空格过滤掉
85 | Keys.onPressed: event.accepted = (event.key === Qt.Key_Space)
86 | Keys.onReleased: event.accepted = (event.key === Qt.Key_Space)
87 | // 切换选项时currentIndexChanged先触发,编辑时editTextChanged先触发
88 | // 当前项在model的序号currentIndex
89 | // onCurrentIndexChanged: console.log("index", currentIndex)
90 | // 当前项文本currentText
91 | // onCurrentTextChanged: console.log("text", currentText)
92 | // 当前项显示文本,只在不可编辑时修饰contentItem文本的显示
93 | // 而我们自定义的showFunc是contentItem和delegate的文本都会修饰
94 | // displayText: "Display:" + currentText
95 | // onDisplayTextChanged: console.log("display", displayText)
96 | // 当前编辑的文本editText,如果对应不到model中的值则currentIndex=-1,其他值为空
97 | // onEditTextChanged: console.log("edit", editText)
98 | // 当前项内容currentValue
99 | // onCurrentValueChanged: console.log("value", currentValue)
100 | // 从model的哪个role获取值显示
101 | // valueRole: "displayRole"
102 | // 数据源model
103 | // 元素个数count
104 | // 组件在视觉上是否处于按下状态,而pressed只读属性是物理意义上的按下
105 | // down: false
106 | // 编辑框是否可以鼠标拖选文本,此处编辑框自定义没绑定这个属性直接设置的true
107 | // selectTextByMouse: true
108 | // 对输入内容限制,如字符隐藏/只允许输入数字等
109 | // 区别于echoMode,如密码非明文显示 echoMode: TextInput.Password
110 | // inputMethodHints: Qt.ImhNoPredictiveText
111 | // 输入的正则限制
112 | // validator: IntValidator{ bottom: 0; top: 100; }
113 |
114 | // 列表中enter或return选中触发
115 | // onAccepted: console.log("accepted")
116 | // 用户选择某项后触发
117 | // onActivated: console.log("activated", index)
118 | // 高亮项变化时触发,如鼠标hover移动
119 | // onHighlighted: console.log("highlighted", index)
120 |
121 | // 递减currentIndex
122 | // void decrementCurrentIndex()
123 | // 递增currentIndex
124 | // void incrementCurrentIndex()
125 | // 选中编辑框中文本
126 | // void selectAll()
127 | // 根据index获取文本,未匹配返回空
128 | // string textAt(int index)
129 | // 根据值查找index,未匹配返回-1
130 | // int indexOfValue(object value)
131 | // 根据文本查找index,未匹配返回-1
132 | // 枚举可以指定文本匹配规则,如区分大小写/正则等
133 | // int find(string text, enumeration flags)
134 |
135 | // 下拉选项样式
136 | // 源码用的ItemDelegate继承自AbstractButton,多了一个highlighted属性
137 | // 且background设置了默认宽高
138 | delegate: T.ItemDelegate {
139 | // 选项宽高
140 | width: ListView.view.width
141 | height: control.itemHeight
142 | // 选项边距
143 | padding: control.padding
144 | leftPadding: control.itemPadding
145 | rightPadding: control.itemPadding
146 | // 每个选项可以设置icon,一般用不到,暂略
147 | // icon.color: control.textColor
148 | contentItem: Text {
149 | // 选项文字内容
150 | text: control.showFunc(control.textRole
151 | ? (Array.isArray(control.model)
152 | ? modelData[control.textRole]
153 | : model[control.textRole])
154 | : modelData)
155 | // 字体设置
156 | font: control.font
157 | // 颜色设置
158 | color: control.textColor
159 | // 文字对齐方式
160 | horizontalAlignment: Text.AlignLeft
161 | verticalAlignment: Text.AlignVCenter
162 | // 单独设置文本组件的渲染方式
163 | renderType: Text.NativeRendering
164 | // 文字超出按钮范围显示省略号
165 | elide: Text.ElideRight
166 | }
167 | // 是否检测hover鼠标悬停
168 | hoverEnabled: control.hoverEnabled
169 | // 选项背景
170 | background: Rectangle {
171 | // radius: control.radius
172 | // 选项背景色
173 | color: (control.highlightedIndex === index)
174 | ? control.itemHighlightColor
175 | : control.itemNormalColor
176 | // 底部一条分隔线
177 | Rectangle {
178 | height: 1
179 | width: parent.width
180 | anchors.bottom: parent.bottom
181 | color: Qt.lighter(itemNormalColor)
182 | }
183 | }
184 | }
185 |
186 | // 下拉按钮图标
187 | indicator: Item {
188 | id: box_indicator
189 | // 下拉图标定位
190 | x: control.width - width
191 | y: control.topPadding + (control.availableHeight - height) / 2
192 | // 下拉按钮区域占的尺寸
193 | width: (indicatorWidth < 0 ? box_indicator_img.width : indicatorWidth) + control.indicatorPadding * 2
194 | height: control.height
195 | // 下拉按钮图标
196 | ColorImage {
197 | id: box_indicator_img
198 | anchors.centerIn: parent
199 | // 图标颜色
200 | color: control.indicatorColor
201 | // 图标url
202 | source: control.indicatorSource
203 | }
204 | }
205 |
206 | // 当前展示内容项,可以单独封装然后组合在这里
207 | // 如果不需要支持编辑,用Text或者Label也行
208 | contentItem: T.TextField {
209 | id: content_edit
210 | // 左右边距
211 | leftPadding: control.itemPadding
212 | rightPadding: control.itemPadding
213 | // 文字内容
214 | text: control.editable
215 | ? control.editText
216 | : control.showFunc(control.displayText)
217 | // 字体设置
218 | font: control.font
219 | // 文字颜色
220 | color: control.textColor
221 | // 默认鼠标选取文本设置为false
222 | selectByMouse: true
223 | // 选中文本的颜色
224 | selectedTextColor: "white"
225 | // 选中文本背景色
226 | selectionColor: "black"
227 | // 超出区域后截断不显示
228 | clip: true
229 | // 单独设置文本组件的渲染方式
230 | renderType: Text.NativeRendering
231 | // 文字对齐方式
232 | horizontalAlignment: Text.AlignLeft
233 | verticalAlignment: Text.AlignVCenter
234 | // 设置为可编辑时,才允许编辑
235 | enabled: control.editable
236 | // 设置为可编辑时,填充的文本自动滚动到末尾
237 | autoScroll: control.editable
238 | // 下拉时只读
239 | readOnly: control.down
240 | // 对输入内容限制,如字符隐藏/只允许输入数字等
241 | // 区别于echoMode,如密码非明文显示 echoMode: TextInput.Password
242 | inputMethodHints: control.inputMethodHints
243 | // 输入的正则限制
244 | validator: control.validator
245 | // 编辑框背景
246 | background: Item {
247 | // 可编辑时才显示
248 | visible: control.enabled && control.editable
249 | // 文字和下拉按钮之间的分割线
250 | Rectangle {
251 | // 编辑框有焦点才显示
252 | visible: content_edit.activeFocus
253 | anchors.left: parent.right
254 | // 分割线宽高
255 | height: parent.height
256 | width: control.borderWidth
257 | // 分割线颜色
258 | color: control.borderColor
259 | }
260 | }
261 | }
262 |
263 | // 背景
264 | background: Rectangle {
265 | // control设置了背景无关的宽高,这里也可以不设置默认宽高
266 | implicitWidth: control.implicitWidth
267 | implicitHeight: control.implicitHeight
268 | // 背景圆角
269 | radius: control.radius
270 | // 背景颜色
271 | color: control.backgroundColor
272 | // 背景边框
273 | border.width: control.borderWidth
274 | border.color: control.borderColor
275 | }
276 |
277 | // 弹出框
278 | popup: T.Popup {
279 | // 默认向下弹出,如果距离不够,y会自动调整(Popup的特性,会被限制在Window内)
280 | y: control.height
281 | width: control.width
282 | // 根据定义的showCount来设置最多显示item个数
283 | implicitHeight: control.delegateModel
284 | ? (control.delegateModel.count < showCount
285 | ? contentItem.implicitHeight
286 | : control.showCount * control.itemHeight) + control.borderWidth * 2
287 | : 0
288 | // 留给边框的位置
289 | padding: control.borderWidth
290 | contentItem: ListView {
291 | implicitHeight: contentHeight
292 | // 超出区域后截断不显示
293 | clip: true
294 | // 列表内容
295 | model: control.popup.visible ? control.delegateModel : null
296 | // 选中项同步
297 | currentIndex: control.highlightedIndex
298 | // 滚动效果
299 | // - NoSnap 默认任意位置停止NoSnap
300 | // - SnapToItem 滑动结束时顶部对齐(不会只漏半截),滑到末尾才是底部对齐
301 | // - SnapOneItem 滑动结束时最多移动一项
302 | // SnapToItem可能会导致原地来回跳无法滚动,SnapOneItem不适合做滚动
303 | // snapMode: ListView.NoSnap
304 | // 高亮移动动画时间,源码设置为0
305 | highlightMoveDuration: 0
306 | // ScrollBar.horizontal: ScrollBar { visible: false }
307 | // 竖向滚动条,可以单独封装然后组合在这里
308 | ScrollBar.vertical: ScrollBar {
309 | id: box_bar
310 | // 滚动条宽度
311 | implicitWidth: 10
312 | visible: (control.delegateModel && control.delegateModel.count > showCount)
313 | // 滚动条整体背景
314 | // background: Rectangle { }
315 | // 拖动的滑动条样式
316 | contentItem: Rectangle {
317 | implicitWidth: 10
318 | radius: width / 2
319 | color: box_bar.pressed
320 | ? Qt.rgba(0.6, 0.6, 0.6)
321 | : Qt.rgba(0.6, 0.6, 0.6, 0.5)
322 | }
323 | }
324 | }
325 |
326 | // 弹出框背景(只有popup.padding显示出来了,其余部分被delegate背景遮挡)
327 | background: Rectangle {
328 | border.width: control.borderWidth
329 | border.color: control.borderColor
330 | // color: Qt.lighter(themeColor)
331 | radius: control.radius
332 | }
333 | }
334 | }
335 |
--------------------------------------------------------------------------------