├── .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 | ![2019-10-17](Image/demo_1.png) 18 | 19 | ![2019-10-17](Image/demo_2.png) 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 | --------------------------------------------------------------------------------