diff --git a/example/T_CalendarPicker.qml b/example/T_CalendarPicker.qml index 26c6044d..0540f62c 100644 --- a/example/T_CalendarPicker.qml +++ b/example/T_CalendarPicker.qml @@ -8,26 +8,29 @@ FluScrollablePage{ title:"CalendarPicker" + FluArea{ + width: parent.width + Layout.topMargin: 20 + height: 350 + paddings: 1 + FluCalendarView{ + + } + } FluArea{ width: parent.width Layout.topMargin: 20 height: 80 paddings: 10 - ColumnLayout{ - anchors{ verticalCenter: parent.verticalCenter left: parent.left } - FluCalendarPicker{ } - } } - - } diff --git a/example/T_Carousel.qml b/example/T_Carousel.qml deleted file mode 100644 index 8358a4fb..00000000 --- a/example/T_Carousel.qml +++ /dev/null @@ -1,36 +0,0 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Window 2.15 -import QtQuick.Controls 2.15 -import QtGraphicalEffects 1.15 -import FluentUI 1.0 - -FluScrollablePage{ - - title:"Carousel" - - FluArea{ - width: parent.width - height: 370 - paddings: 10 - Layout.topMargin: 20 - Column{ - spacing: 15 - anchors{ - verticalCenter: parent.verticalCenter - left:parent.left - } - FluText{ - text:"轮播图,支持无限轮播,无限滑动,用ListView实现的组件" - } - FluCarousel{ - id:carousel - Layout.topMargin: 20 - Layout.leftMargin: 5 - Component.onCompleted: { - carousel.setData([{url:"qrc:/res/image/banner_1.jpg"},{url:"qrc:/res/image/banner_2.jpg"},{url:"qrc:/res/image/banner_3.jpg"}]) - } - } - } - } -} diff --git a/example/T_ColorPicker.qml b/example/T_ColorPicker.qml new file mode 100644 index 00000000..1ba0aa78 --- /dev/null +++ b/example/T_ColorPicker.qml @@ -0,0 +1,57 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Window 2.15 +import QtGraphicalEffects 1.15 +import FluentUI 1.0 + +FluScrollablePage{ + + title:"ColorPicker" + + FluArea{ + width: parent.width + height: 280 + Layout.topMargin: 20 + paddings: 10 + ColumnLayout{ + anchors{ + verticalCenter: parent.verticalCenter + left:parent.left + } + FluText{ + text:"此颜色组件是Github上大佬封装的" + } + FluTextButton{ + leftPadding: 0 + rightPadding: 0 + text:"https://github.com/rshest/qml-colorpicker" + onClicked: { + Qt.openUrlExternally(text) + } + } + FluColorView{ + + } + } + } + + FluArea{ + width: parent.width + Layout.topMargin: 20 + height: 60 + paddings: 10 + + RowLayout{ + FluText{ + text:"点击选择颜色->" + Layout.alignment: Qt.AlignVCenter + } + FluColorPicker{ + + } + } + } + +} + diff --git a/example/page/AboutPage.qml b/example/page/AboutPage.qml index cde73107..235300b1 100644 --- a/example/page/AboutPage.qml +++ b/example/page/AboutPage.qml @@ -35,7 +35,7 @@ FluWindow { fontStyle: FluText.Title } FluText{ - text:"v1.1.2" + text:"v1.1.3" fontStyle: FluText.Body Layout.alignment: Qt.AlignBottom } diff --git a/example/page/MainPage.qml b/example/page/MainPage.qml index fcd7bc28..f9c9c334 100644 --- a/example/page/MainPage.qml +++ b/example/page/MainPage.qml @@ -79,6 +79,13 @@ FluWindow { } } + FluPaneItem{ + title:"ColorPicker" + onTap:{ + nav_view.push("qrc:/T_ColorPicker.qml") + } + } + FluPaneItemHeader{ title:"Surface" } @@ -97,13 +104,6 @@ FluWindow { } } - FluPaneItem{ - title:"Calendar" - onTap:{ - nav_view.push("qrc:/T_Calendar.qml") - } - } - FluPaneItem{ title:"Badge" onTap:{ diff --git a/example/qml.qrc b/example/qml.qrc index bdb97871..93fa8550 100644 --- a/example/qml.qrc +++ b/example/qml.qrc @@ -34,7 +34,6 @@ T_DatePicker.qml T_MultiWindow.qml T_Menu.qml - T_Carousel.qml res/image/banner_1.jpg res/image/banner_2.jpg res/image/banner_3.jpg @@ -44,5 +43,6 @@ T_Badge.qml T_Calendar.qml T_CalendarPicker.qml + T_ColorPicker.qml diff --git a/src/Fluent.cpp b/src/Fluent.cpp index bd347ac2..4c8af0e4 100644 --- a/src/Fluent.cpp +++ b/src/Fluent.cpp @@ -47,6 +47,8 @@ void Fluent::registerTypes(const char *uri){ qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluCalendarView.qml"),uri,major,minor,"FluCalendarView"); qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluDatePicker.qml"),uri,major,minor,"FluDatePicker"); qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluTimePicker.qml"),uri,major,minor,"FluTimePicker"); + qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluColorView.qml"),uri,major,minor,"FluColorView"); + qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluColorPicker.qml"),uri,major,minor,"FluColorPicker"); qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluCarousel.qml"),uri,major,minor,"FluCarousel"); diff --git a/src/colorpicker/ColorPicker.qml b/src/colorpicker/ColorPicker.qml new file mode 100644 index 00000000..b86308e4 --- /dev/null +++ b/src/colorpicker/ColorPicker.qml @@ -0,0 +1,234 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 +import "content" + +Rectangle { + id: colorPicker + property color colorValue: "transparent" + property bool enableAlphaChannel: true + property bool enableDetails: true + property int colorHandleRadius : 8 + property color _changingColorValue : _hsla(hueSlider.value, sbPicker.saturation,sbPicker.brightness, alphaSlider.value) + on_ChangingColorValueChanged: { + colorValue = _changingColorValue + } + + signal colorChanged(color changedColor) + + implicitWidth: picker.implicitWidth + implicitHeight: picker.implicitHeight + color: "#00000000" + clip: true + + + RowLayout { + id: picker + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: colorHandleRadius + anchors.bottom: parent.bottom + spacing: 0 + + + SBPicker { + id: sbPicker + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumWidth: 200 + Layout.minimumHeight: 200 + hueColor: { + var v = 1.0-hueSlider.value + if(0.0 <= v && v < 0.16) { + return Qt.rgba(1.0, 0.0, v/0.16, 1.0) + } else if(0.16 <= v && v < 0.33) { + return Qt.rgba(1.0 - (v-0.16)/0.17, 0.0, 1.0, 1.0) + } else if(0.33 <= v && v < 0.5) { + return Qt.rgba(0.0, ((v-0.33)/0.17), 1.0, 1.0) + } else if(0.5 <= v && v < 0.76) { + return Qt.rgba(0.0, 1.0, 1.0 - (v-0.5)/0.26, 1.0) + } else if(0.76 <= v && v < 0.85) { + return Qt.rgba((v-0.76)/0.09, 1.0, 0.0, 1.0) + } else if(0.85 <= v && v <= 1.0) { + return Qt.rgba(1.0, 1.0 - (v-0.85)/0.15, 0.0, 1.0) + } else { + return "red" + } + } + } + + Item { + id: huePicker + width: 12 + Layout.fillHeight: true + Layout.topMargin: colorHandleRadius + Layout.bottomMargin: colorHandleRadius + + Rectangle { + anchors.fill: parent + id: colorBar + gradient: Gradient { + GradientStop { position: 1.0; color: "#FF0000" } + GradientStop { position: 0.85; color: "#FFFF00" } + GradientStop { position: 0.76; color: "#00FF00" } + GradientStop { position: 0.5; color: "#00FFFF" } + GradientStop { position: 0.33; color: "#0000FF" } + GradientStop { position: 0.16; color: "#FF00FF" } + GradientStop { position: 0.0; color: "#FF0000" } + } + } + ColorSlider { + id: hueSlider; anchors.fill: parent + } + } + + Item { + id: alphaPicker + visible: enableAlphaChannel + width: 12 + Layout.leftMargin: 4 + Layout.fillHeight: true + Layout.topMargin: colorHandleRadius + Layout.bottomMargin: colorHandleRadius + Checkerboard { cellSide: 4 } + Rectangle { + anchors.fill: parent + gradient: Gradient { + GradientStop { position: 0.0; color: "#FF000000" } + GradientStop { position: 1.0; color: "#00000000" } + } + } + ColorSlider { + id: alphaSlider; anchors.fill: parent + } + } + + Column { + id: detailColumn + Layout.leftMargin: 4 + Layout.fillHeight: true + Layout.topMargin: colorHandleRadius + Layout.bottomMargin: colorHandleRadius + Layout.alignment: Qt.AlignRight + visible: enableDetails + + PanelBorder { + width: parent.width + height: 30 + visible: enableAlphaChannel + Checkerboard { cellSide: 5 } + Rectangle { + width: parent.width; height: 30 + border.width: 1; border.color: "black" + color: colorPicker.colorValue + } + } + + Item { + width: parent.width + height: 1 + } + + + PanelBorder { + id: colorEditBox + height: 15; width: parent.width + TextInput { + anchors.fill: parent + color: "#AAAAAA" + selectionColor: "#FF7777AA" + font.pixelSize: 11 + maximumLength: 9 + focus: false + text: _fullColorString(colorPicker.colorValue, alphaSlider.value) + selectByMouse: true + } + } + + Item { + width: parent.width + height: 8 + } + + Column { + width: parent.width + spacing: 1 + NumberBox { caption: "H:"; value: hueSlider.value.toFixed(2) } + NumberBox { caption: "S:"; value: sbPicker.saturation.toFixed(2) } + NumberBox { caption: "B:"; value: sbPicker.brightness.toFixed(2) } + } + + Item { + width: parent.width + height: 8 + } + + Column { + width: parent.width + spacing: 1 + NumberBox { + caption: "R:" + value: _getChannelStr(colorPicker.colorValue, 0) + min: 0; max: 255 + } + NumberBox { + caption: "G:" + value: _getChannelStr(colorPicker.colorValue, 1) + min: 0; max: 255 + } + NumberBox { + caption: "B:" + value: _getChannelStr(colorPicker.colorValue, 2) + min: 0; max: 255 + } + } + + Item{ + width: parent.width + height: 1 + } + + NumberBox { + visible: enableAlphaChannel + caption: "A:"; value: Math.ceil(alphaSlider.value*255) + min: 0; max: 255 + } + } + } + + function _hsla(h, s, b, a) { + var lightness = (2 - s)*b + var satHSL = s*b/((lightness <= 1) ? lightness : 2 - lightness) + lightness /= 2 + + var c = Qt.hsla(h, satHSL, lightness, a) + + colorChanged(c) + + return c + } + + function _rgb(rgb, a) { + + var c = Qt.rgba(rgb.r, rgb.g, rgb.b, a) + + colorChanged(c) + + return c + } + + function _fullColorString(clr, a) { + return "#" + ((Math.ceil(a*255) + 256).toString(16).substr(1, 2) + clr.toString().substr(1, 6)).toUpperCase() + } + + function _getChannelStr(clr, channelIdx) { + return parseInt(clr.toString().substr(channelIdx*2 + 1, 2), 16) + } + + function setColor(color) { + var c = Qt.tint(color, "transparent") + alphaSlider.setValue(c.a) + colorPicker.colorValue = c + } +} diff --git a/src/colorpicker/content/Checkerboard.qml b/src/colorpicker/content/Checkerboard.qml new file mode 100644 index 00000000..81f713a1 --- /dev/null +++ b/src/colorpicker/content/Checkerboard.qml @@ -0,0 +1,16 @@ +import QtQuick 2.15 +Grid { + id: root + property int cellSide: 5 + anchors.fill: parent + rows: height/cellSide; columns: width/cellSide + clip: true + Repeater { + model: root.columns*root.rows + Rectangle { + width: root.cellSide; height: root.cellSide + color: (index%2 == 0) ? "gray" : "white" + } + } +} + diff --git a/src/colorpicker/content/ColorSlider.qml b/src/colorpicker/content/ColorSlider.qml new file mode 100644 index 00000000..c7262f15 --- /dev/null +++ b/src/colorpicker/content/ColorSlider.qml @@ -0,0 +1,43 @@ +import QtQuick 2.15 + +Item { + property int cursorHeight: 7 + property real value: (1 - pickerCursor.y/height) + width: 15 + height: 300 + Item { + id: pickerCursor + width: parent.width + Rectangle { + x: -3; y: -height*0.5 + width: parent.width + 4; height: cursorHeight + border.color: "black"; border.width: 1 + color: "transparent" + Rectangle { + anchors.fill: parent; anchors.margins: 2 + border.color: "white"; border.width: 1 + color: "transparent" + } + } + } + MouseArea { + y: -Math.round(cursorHeight/2) + height: parent.height+cursorHeight + anchors.left: parent.left + anchors.right: parent.right + function handleMouse(mouse) { + if (mouse.buttons & Qt.LeftButton) { + pickerCursor.y = Math.max(0, Math.min(height, mouse.y)-cursorHeight) + } + } + onPositionChanged: { + handleMouse(mouse) + } + onPressed: handleMouse(mouse) + } + + function setValue(val) { + pickerCursor.y = height * (1 - val) + } +} + diff --git a/src/colorpicker/content/NumberBox.qml b/src/colorpicker/content/NumberBox.qml new file mode 100644 index 00000000..fc2205bd --- /dev/null +++ b/src/colorpicker/content/NumberBox.qml @@ -0,0 +1,39 @@ +import QtQuick 2.15 + +Row { + property alias caption: captionBox.text + property alias value: inputBox.text + property alias min: numValidator.bottom + property alias max: numValidator.top + property alias decimals: numValidator.decimals + + width: 80; + height: 15 + spacing: 4 + //anchors.margins: 2 + Text { + id: captionBox + width: 18; height: parent.height + color: "#AAAAAA" + font.pixelSize: 11; font.bold: true + } + PanelBorder { + height: parent.height + TextInput { + id: inputBox + color: "#AAAAAA"; selectionColor: "#FF7777AA" + font.pixelSize: 11 + maximumLength: 10 + focus: false + readOnly: true + selectByMouse: true + validator: DoubleValidator { + id: numValidator + bottom: 0; top: 1; decimals: 2 + notation: DoubleValidator.StandardNotation + } + } + } +} + + diff --git a/src/colorpicker/content/PanelBorder.qml b/src/colorpicker/content/PanelBorder.qml new file mode 100644 index 00000000..69dc4c53 --- /dev/null +++ b/src/colorpicker/content/PanelBorder.qml @@ -0,0 +1,18 @@ +import QtQuick 2.15 + +Rectangle { + width : 40; height : 15; radius: 2 + border.width: 1; border.color: "#FF101010" + color: "transparent" + anchors.leftMargin: 1; anchors.topMargin: 3 + clip: true + Rectangle { + anchors.fill: parent; radius: 2 + anchors.leftMargin: -1; anchors.topMargin: -1 + anchors.rightMargin: 0; anchors.bottomMargin: 0 + border.width: 1; border.color: "#FF525255" + color: "transparent" + } +} + + diff --git a/src/colorpicker/content/SBPicker.qml b/src/colorpicker/content/SBPicker.qml new file mode 100644 index 00000000..ab1b4c5a --- /dev/null +++ b/src/colorpicker/content/SBPicker.qml @@ -0,0 +1,69 @@ +import QtQuick 2.15 + +Item { + id: root + property color hueColor : "blue" + property real saturation : pickerCursor.x/(width-2*r) + property real brightness : 1 - pickerCursor.y/(height-2*r) + property int r : colorHandleRadius + + Rectangle { + x : r + y : r + parent.height - 2 * r + rotation: -90 + transformOrigin: Item.TopLeft + width: parent.height - 2 * r + height: parent.width - 2 * r + gradient: Gradient { + GradientStop { position: 0.0; color: "#FFFFFF" } + GradientStop { position: 1.0; color: root.hueColor } + } + } + Rectangle { + x: r + y: r + width: parent.width - 2 * r + height: parent.height - 2 * r + gradient: Gradient { + GradientStop { position: 1.0; color: "#FF000000" } + GradientStop { position: 0.0; color: "#00000000" } + } + } + Item { + id: pickerCursor + Rectangle { + width: r*2; height: r*2 + radius: r + border.color: "black"; border.width: 2 + color: "transparent" + Rectangle { + anchors.fill: parent; anchors.margins: 2; + border.color: "white"; border.width: 2 + radius: width/2 + color: "transparent" + } + } + } + MouseArea { + anchors.fill: parent + x: r + y: r + function handleMouse(mouse) { + if (mouse.buttons & Qt.LeftButton) { + + pickerCursor.x = Math.max(0,Math.min(mouse.x - r,width-2*r)); + pickerCursor.y = Math.max(0,Math.min(mouse.y - r,height-2*r)); + + +// pickerCursor.x = Math.max(-r,Math.min(mouse.x - r,width+r)); +// pickerCursor.y = Math.max(-r,Math.min(mouse.y - r,height+r)); + +// pickerCursor.x = Math.max(0, Math.min(width, mouse.x) - 2 * r); +// pickerCursor.y = Math.max(0, Math.min(height, mouse.y) - 2 * r); + } + } + onPositionChanged: handleMouse(mouse) + onPressed: handleMouse(mouse) + } +} + diff --git a/src/controls/FluCalendarView.qml b/src/controls/FluCalendarView.qml index cb7fc325..d7b5129d 100644 --- a/src/controls/FluCalendarView.qml +++ b/src/controls/FluCalendarView.qml @@ -44,7 +44,6 @@ Item { return {type:3,date:date,name:"",isDecade:isDecade} } - function updateDecade(date){ list_model.clear() var year = date.getFullYear() diff --git a/src/controls/FluColorPicker.qml b/src/controls/FluColorPicker.qml new file mode 100644 index 00000000..2cb25070 --- /dev/null +++ b/src/controls/FluColorPicker.qml @@ -0,0 +1,52 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Window 2.15 +import FluentUI 1.0 + + +Button{ + id:control + width: 36 + height: 36 + implicitWidth: width + implicitHeight: height + background: + Rectangle{ + id:layout_color + radius: 5 + color: container.colorValue + border.color: { + if(hovered) + return FluTheme.primaryColor.light + return FluTheme.isDark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1) + } + border.width: 1 + } + contentItem: Item{} + onClicked: { + popup.showPopup() + } + Popup{ + id:popup + height: container.height + width: container.width + background: FluColorView{ + id:container + } + contentItem: Item{} + function showPopup() { + var pos = control.mapToItem(null, 0, 0) + if(window.height>pos.y+control.height+popup.height){ + popup.y = control.height + } else if(pos.y>popup.height){ + popup.y = -popup.height + } else { + popup.y = window.height-(pos.y+popup.height) + } + popup.x = -(popup.width-control.width)/2 + popup.open() + } + } + +} diff --git a/src/controls/FluColorView.qml b/src/controls/FluColorView.qml new file mode 100644 index 00000000..d74059c5 --- /dev/null +++ b/src/controls/FluColorView.qml @@ -0,0 +1,29 @@ +import QtQuick 2.15 +import "../colorpicker" + +Item { + + width: color_picker.width+10 + height: color_picker.height + + property alias colorValue: color_picker.colorValue + + FluArea{ + anchors.fill: parent + radius: 5 + + FluShadow{ + radius: 5 + } + + ColorPicker{ + id:color_picker + } + } + + function setColor(color) { + color_picker.setColor(color) + } + +} + diff --git a/src/res.qrc b/src/res.qrc index 27570524..17ceafb7 100644 --- a/src/res.qrc +++ b/src/res.qrc @@ -49,5 +49,13 @@ controls/FluFocusRectangle.qml controls/FluCarousel.qml controls/FluBadge.qml + controls/FluColorView.qml + controls/FluColorPicker.qml + colorpicker/ColorPicker.qml + colorpicker/content/Checkerboard.qml + colorpicker/content/ColorSlider.qml + colorpicker/content/NumberBox.qml + colorpicker/content/PanelBorder.qml + colorpicker/content/SBPicker.qml