Loading debian/changelog +7 −0 Original line number Diff line number Diff line blogi (20260416+4) unstable; urgency=medium * Improve TextBox editor with rich text/raw HTML switch and replace color dialog with custom RGBA picker -- Jan Koester <jan.koester@tuxist.de> Thu, 16 Apr 2026 10:55:35 +0200 blogi (20260416+3) unstable; urgency=medium * Unify AI rules and use htmlimport.h API for KI import Loading editor/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ qt_add_qml_module(blogi-editor qml/PropertyPanel.qml qml/HtmlEditorField.qml qml/PreviewPane.qml qml/RgbaColorPicker.qml qml/ConnectionPanel.qml qml/FileBrowserTab.qml qml/MediaBrowser.qml Loading editor/qml/PropertyPanel.qml +5 −18 Original line number Diff line number Diff line Loading @@ -639,26 +639,13 @@ ColumnLayout { property string fieldKey: "" property string initialColor: "#ffffff" sourceComponent: ColorDialog { id: colorDlg title: qsTr("Pick a color") options: ColorDialog.ShowAlphaChannel selectedColor: colorDialogLoader.initialColor onAccepted: { var c = selectedColor /* Format as rgba() if alpha < 1, otherwise hex */ var colorStr if (c.a < 1.0) { colorStr = "rgba(" + Math.round(c.r * 255) + "," + Math.round(c.g * 255) + "," + Math.round(c.b * 255) + "," + c.a.toFixed(2) + ")" } else { colorStr = c.toString() } sourceComponent: RgbaColorPicker { initialColor: colorDialogLoader.initialColor onColorSelected: function(colorStr) { propPanel.saveProperty(colorDialogLoader.fieldKey, colorStr) propPanel.loadProperties(propPanel.currentUuid) colorDialogLoader.active = false } onRejected: colorDialogLoader.active = false } Loading editor/qml/RgbaColorPicker.qml 0 → 100644 +79 −0 Original line number Diff line number Diff line import QtQuick import QtQuick.Controls import QtQuick.Layouts Dialog { id: rgbaDialog width: 300 title: qsTr("Pick RGBA Color") standardButtons: Dialog.Ok | Dialog.Cancel property string initialColor: "#ffffff" property color currentColor: initialColor signal colorSelected(string colorStr) onOpened: { var c = Qt.color(initialColor) rSlider.value = c.r * 255 gSlider.value = c.g * 255 bSlider.value = c.b * 255 aSlider.value = c.a updateColor() } onAccepted: { var r = Math.round(rSlider.value) var g = Math.round(gSlider.value) var b = Math.round(bSlider.value) var a = aSlider.value.toFixed(2) colorSelected("rgba(" + r + "," + g + "," + b + "," + a + ")") } function updateColor() { currentColor = Qt.rgba(rSlider.value/255, gSlider.value/255, bSlider.value/255, aSlider.value) } ColumnLayout { anchors.fill: parent spacing: 12 Rectangle { Layout.fillWidth: true height: 40 color: rgbaDialog.currentColor border.color: Theme.border radius: Theme.radius // Checkered background pattern to show transparency properly Image { anchors.fill: parent z: -1 fillMode: Image.Tile source: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><rect width='8' height='8' fill='%23ccc'/><rect x='8' y='8' width='8' height='8' fill='%23ccc'/><rect x='8' width='8' height='8' fill='%23fff'/><rect y='8' width='8' height='8' fill='%23fff'/></svg>" opacity: 0.5 } } RowLayout { Label { text: "R"; color: Theme.textPrimary; width: 15 } Slider { id: rSlider; from: 0; to: 255; stepSize: 1; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: Math.round(rSlider.value); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } RowLayout { Label { text: "G"; color: Theme.textPrimary; width: 15 } Slider { id: gSlider; from: 0; to: 255; stepSize: 1; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: Math.round(gSlider.value); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } RowLayout { Label { text: "B"; color: Theme.textPrimary; width: 15 } Slider { id: bSlider; from: 0; to: 255; stepSize: 1; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: Math.round(bSlider.value); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } RowLayout { Label { text: "A"; color: Theme.textPrimary; width: 15 } Slider { id: aSlider; from: 0.0; to: 1.0; stepSize: 0.01; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: aSlider.value.toFixed(2); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } } } Loading
debian/changelog +7 −0 Original line number Diff line number Diff line blogi (20260416+4) unstable; urgency=medium * Improve TextBox editor with rich text/raw HTML switch and replace color dialog with custom RGBA picker -- Jan Koester <jan.koester@tuxist.de> Thu, 16 Apr 2026 10:55:35 +0200 blogi (20260416+3) unstable; urgency=medium * Unify AI rules and use htmlimport.h API for KI import Loading
editor/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ qt_add_qml_module(blogi-editor qml/PropertyPanel.qml qml/HtmlEditorField.qml qml/PreviewPane.qml qml/RgbaColorPicker.qml qml/ConnectionPanel.qml qml/FileBrowserTab.qml qml/MediaBrowser.qml Loading
editor/qml/PropertyPanel.qml +5 −18 Original line number Diff line number Diff line Loading @@ -639,26 +639,13 @@ ColumnLayout { property string fieldKey: "" property string initialColor: "#ffffff" sourceComponent: ColorDialog { id: colorDlg title: qsTr("Pick a color") options: ColorDialog.ShowAlphaChannel selectedColor: colorDialogLoader.initialColor onAccepted: { var c = selectedColor /* Format as rgba() if alpha < 1, otherwise hex */ var colorStr if (c.a < 1.0) { colorStr = "rgba(" + Math.round(c.r * 255) + "," + Math.round(c.g * 255) + "," + Math.round(c.b * 255) + "," + c.a.toFixed(2) + ")" } else { colorStr = c.toString() } sourceComponent: RgbaColorPicker { initialColor: colorDialogLoader.initialColor onColorSelected: function(colorStr) { propPanel.saveProperty(colorDialogLoader.fieldKey, colorStr) propPanel.loadProperties(propPanel.currentUuid) colorDialogLoader.active = false } onRejected: colorDialogLoader.active = false } Loading
editor/qml/RgbaColorPicker.qml 0 → 100644 +79 −0 Original line number Diff line number Diff line import QtQuick import QtQuick.Controls import QtQuick.Layouts Dialog { id: rgbaDialog width: 300 title: qsTr("Pick RGBA Color") standardButtons: Dialog.Ok | Dialog.Cancel property string initialColor: "#ffffff" property color currentColor: initialColor signal colorSelected(string colorStr) onOpened: { var c = Qt.color(initialColor) rSlider.value = c.r * 255 gSlider.value = c.g * 255 bSlider.value = c.b * 255 aSlider.value = c.a updateColor() } onAccepted: { var r = Math.round(rSlider.value) var g = Math.round(gSlider.value) var b = Math.round(bSlider.value) var a = aSlider.value.toFixed(2) colorSelected("rgba(" + r + "," + g + "," + b + "," + a + ")") } function updateColor() { currentColor = Qt.rgba(rSlider.value/255, gSlider.value/255, bSlider.value/255, aSlider.value) } ColumnLayout { anchors.fill: parent spacing: 12 Rectangle { Layout.fillWidth: true height: 40 color: rgbaDialog.currentColor border.color: Theme.border radius: Theme.radius // Checkered background pattern to show transparency properly Image { anchors.fill: parent z: -1 fillMode: Image.Tile source: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><rect width='8' height='8' fill='%23ccc'/><rect x='8' y='8' width='8' height='8' fill='%23ccc'/><rect x='8' width='8' height='8' fill='%23fff'/><rect y='8' width='8' height='8' fill='%23fff'/></svg>" opacity: 0.5 } } RowLayout { Label { text: "R"; color: Theme.textPrimary; width: 15 } Slider { id: rSlider; from: 0; to: 255; stepSize: 1; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: Math.round(rSlider.value); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } RowLayout { Label { text: "G"; color: Theme.textPrimary; width: 15 } Slider { id: gSlider; from: 0; to: 255; stepSize: 1; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: Math.round(gSlider.value); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } RowLayout { Label { text: "B"; color: Theme.textPrimary; width: 15 } Slider { id: bSlider; from: 0; to: 255; stepSize: 1; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: Math.round(bSlider.value); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } RowLayout { Label { text: "A"; color: Theme.textPrimary; width: 15 } Slider { id: aSlider; from: 0.0; to: 1.0; stepSize: 0.01; Layout.fillWidth: true; onValueChanged: updateColor() } Label { text: aSlider.value.toFixed(2); color: Theme.textPrimary; width: 30; horizontalAlignment: Text.AlignRight } } } }