Loading editor/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ qt_add_qml_module(blogi-editor qml/WidgetToolbar.qml qml/DocumentTree.qml qml/PropertyPanel.qml qml/HtmlEditorField.qml qml/PreviewPane.qml qml/ConnectionPanel.qml qml/FileBrowserTab.qml Loading editor/qml/HtmlEditorField.qml 0 → 100644 +76 −0 Original line number Diff line number Diff line import QtQuick import QtQuick.Controls import QtQuick.Layouts ColumnLayout { id: root property string text: "" signal textChangedByUser(string newText) property bool rawMode: false RowLayout { Layout.fillWidth: true spacing: 2 ToolButton { text: "<b>B</b>"; onClicked: insertText("<b>", "</b>"); font.bold: true; visible: !rawMode } ToolButton { text: "<i>I</i>"; onClicked: insertText("<i>", "</i>"); font.italic: true; visible: !rawMode } ToolButton { text: "<u>U</u>"; onClicked: insertText("<u>", "</u>"); font.underline: true; visible: !rawMode } ToolSeparator { visible: !rawMode } ToolButton { text: "H1"; onClicked: insertText("<h1>", "</h1>"); visible: !rawMode } ToolButton { text: "H2"; onClicked: insertText("<h2>", "</h2>"); visible: !rawMode } ToolButton { text: "H3"; onClicked: insertText("<h3>", "</h3>"); visible: !rawMode } ToolSeparator { visible: !rawMode } ToolButton { text: "p"; onClicked: insertText("<p>", "</p>"); visible: !rawMode } ToolButton { text: "br"; onClicked: insertText("<br/>", ""); visible: !rawMode } ToolButton { text: "Link"; onClicked: insertText("<a href=\"#\">", "</a>"); visible: !rawMode } Item { Layout.fillWidth: true } Switch { id: modeSwitch text: qsTr("Raw HTML") checked: root.rawMode onCheckedChanged: root.rawMode = checked } } ScrollView { Layout.fillWidth: true Layout.minimumHeight: 150 Layout.preferredHeight: 200 TextArea { id: editorArea text: root.text textFormat: root.rawMode ? TextEdit.PlainText : TextEdit.RichText font.pixelSize: 13 color: Theme.textPrimary wrapMode: TextEdit.Wrap background: Rectangle { color: Theme.bgTertiary; border.color: Theme.border; radius: Theme.radius } onTextChanged: { if (editorArea.activeFocus) { root.textChangedByUser(editorArea.text) } } } } function insertText(prefix, suffix) { var start = editorArea.selectionStart; var end = editorArea.selectionEnd; var currentText = editorArea.getText(0, editorArea.length); var selectedText = currentText.substring(start, end); var newText = currentText.substring(0, start) + prefix + selectedText + suffix + currentText.substring(end); editorArea.text = newText; editorArea.cursorPosition = start + prefix.length + selectedText.length; root.textChangedByUser(editorArea.text); } } editor/qml/PropertyPanel.qml +12 −2 Original line number Diff line number Diff line Loading @@ -381,9 +381,9 @@ ColumnLayout { } } /* textarea / richtext */ /* textarea */ ScrollView { visible: field.type === "textarea" || field.type === "richtext" visible: field.type === "textarea" Layout.fillWidth: true Layout.preferredHeight: 100 Loading @@ -396,6 +396,16 @@ ColumnLayout { } } /* richtext */ HtmlEditorField { visible: field.type === "richtext" Layout.fillWidth: true text: val onTextChangedByUser: (newText) => { propPanel.debounceSave(eKey, newText) } } /* color_var — three modes: none / theme variable / custom RGBA */ ColumnLayout { visible: field.type === "color_var" Loading Loading
editor/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ qt_add_qml_module(blogi-editor qml/WidgetToolbar.qml qml/DocumentTree.qml qml/PropertyPanel.qml qml/HtmlEditorField.qml qml/PreviewPane.qml qml/ConnectionPanel.qml qml/FileBrowserTab.qml Loading
editor/qml/HtmlEditorField.qml 0 → 100644 +76 −0 Original line number Diff line number Diff line import QtQuick import QtQuick.Controls import QtQuick.Layouts ColumnLayout { id: root property string text: "" signal textChangedByUser(string newText) property bool rawMode: false RowLayout { Layout.fillWidth: true spacing: 2 ToolButton { text: "<b>B</b>"; onClicked: insertText("<b>", "</b>"); font.bold: true; visible: !rawMode } ToolButton { text: "<i>I</i>"; onClicked: insertText("<i>", "</i>"); font.italic: true; visible: !rawMode } ToolButton { text: "<u>U</u>"; onClicked: insertText("<u>", "</u>"); font.underline: true; visible: !rawMode } ToolSeparator { visible: !rawMode } ToolButton { text: "H1"; onClicked: insertText("<h1>", "</h1>"); visible: !rawMode } ToolButton { text: "H2"; onClicked: insertText("<h2>", "</h2>"); visible: !rawMode } ToolButton { text: "H3"; onClicked: insertText("<h3>", "</h3>"); visible: !rawMode } ToolSeparator { visible: !rawMode } ToolButton { text: "p"; onClicked: insertText("<p>", "</p>"); visible: !rawMode } ToolButton { text: "br"; onClicked: insertText("<br/>", ""); visible: !rawMode } ToolButton { text: "Link"; onClicked: insertText("<a href=\"#\">", "</a>"); visible: !rawMode } Item { Layout.fillWidth: true } Switch { id: modeSwitch text: qsTr("Raw HTML") checked: root.rawMode onCheckedChanged: root.rawMode = checked } } ScrollView { Layout.fillWidth: true Layout.minimumHeight: 150 Layout.preferredHeight: 200 TextArea { id: editorArea text: root.text textFormat: root.rawMode ? TextEdit.PlainText : TextEdit.RichText font.pixelSize: 13 color: Theme.textPrimary wrapMode: TextEdit.Wrap background: Rectangle { color: Theme.bgTertiary; border.color: Theme.border; radius: Theme.radius } onTextChanged: { if (editorArea.activeFocus) { root.textChangedByUser(editorArea.text) } } } } function insertText(prefix, suffix) { var start = editorArea.selectionStart; var end = editorArea.selectionEnd; var currentText = editorArea.getText(0, editorArea.length); var selectedText = currentText.substring(start, end); var newText = currentText.substring(0, start) + prefix + selectedText + suffix + currentText.substring(end); editorArea.text = newText; editorArea.cursorPosition = start + prefix.length + selectedText.length; root.textChangedByUser(editorArea.text); } }
editor/qml/PropertyPanel.qml +12 −2 Original line number Diff line number Diff line Loading @@ -381,9 +381,9 @@ ColumnLayout { } } /* textarea / richtext */ /* textarea */ ScrollView { visible: field.type === "textarea" || field.type === "richtext" visible: field.type === "textarea" Layout.fillWidth: true Layout.preferredHeight: 100 Loading @@ -396,6 +396,16 @@ ColumnLayout { } } /* richtext */ HtmlEditorField { visible: field.type === "richtext" Layout.fillWidth: true text: val onTextChangedByUser: (newText) => { propPanel.debounceSave(eKey, newText) } } /* color_var — three modes: none / theme variable / custom RGBA */ ColumnLayout { visible: field.type === "color_var" Loading