import QtQuick import QtQuick.Controls import QtQuick.Layouts import Flora Dialog { id: root signal markdownReady(string md) title: qsTr("Таблица") modal: true width: 560 anchors.centerIn: parent closePolicy: Dialog.NoAutoClose standardButtons: Dialog.NoButton background: Rectangle { color: Theme.surface; border.color: Theme.borderStrong; radius: 10 } property int rows: 1 property int cols: 2 property var cells: [] readonly property int cellW: 120 readonly property int cellH: 34 function cell(r, c) { const i = r * cols + c; return i < cells.length ? cells[i] : "" } function setCell(r, c, v) { const i = r * cols + c; if (i < cells.length) cells[i] = v } function addRowAfter(r) { // Новая строка пустая, а не копия const ins = r + 1 const flat = [] for (let rr = 0; rr <= rows; ++rr) { if (rr === ins) for (let c = 0; c < cols; ++c) flat.push("") for (let c = 0; c < cols; ++c) if (rr < rows) flat.push(cell(rr, c)) } cells = flat; rows += 1 } function addColAfter(c) { const src = [] for (let r = 0; r < rows; ++r) src.push(cell(r, c)) const ins = c + 1 const flat = [] for (let rr = 0; rr < rows; ++rr) { for (let cc = 0; cc <= cols; ++cc) { if (cc === ins) flat.push("") if (cc < cols) flat.push(cell(rr, cc)) } } cells = flat; cols += 1 } function buildMarkdown() { let out = "" for (let c = 0; c < cols; ++c) out += (c === 0 ? "| " : " | ") + cell(0, c) out += " |\n" for (let c = 0; c < cols; ++c) out += (c === 0 ? "| " : " | ") + "---" out += " |\n" for (let r = 1; r < rows; ++r) { for (let c = 0; c < cols; ++c) out += (c === 0 ? "| " : " | ") + cell(r, c) out += " |\n" } return out } function resetDialog() { rows = 1; cols = 2; cells = [] for (let i = 0; i < rows * cols; ++i) cells.push("") } Component.onCompleted: resetDialog() ColumnLayout { anchors.fill: parent spacing: 10 Label { text: qsTr("Заполните ячейки таблицы"); color: Theme.textSecondary; font.pixelSize: 11 } Flickable { Layout.fillWidth: true Layout.fillHeight: true contentWidth: cellGrid.width + 30 contentHeight: cellGrid.height + 30 clip: true ScrollBar.horizontal: ScrollBar {} ScrollBar.vertical: ScrollBar {} Row { id: tableRow spacing: 0 Grid { id: cellGrid columns: root.cols columnSpacing: 3 rowSpacing: 3 Repeater { model: root.rows * root.cols delegate: TextField { width: root.cellW; height: root.cellH placeholderText: (Math.floor(model / root.cols)===0 ? qsTr("Заголовок") : qsTr("Ячейка")) placeholderTextColor: Theme.textFaint text: root.cell(Math.floor(model / root.cols), model % root.cols) color: Theme.text background: Rectangle { color: Theme.bgAlt; border.color: parent.activeFocus ? Theme.accent : Theme.border; radius: 6 } onTextEdited: root.setCell(Math.floor(model / root.cols), model % root.cols, text) } } } Grid { columns: 1; columnSpacing: 3; rowSpacing: 3 anchors.leftMargin: 4 Repeater { model: root.rows delegate: Button { width: 24; height: root.cellH text: "+" ToolTip.text: qsTr("Добавить строку") ToolTip.visible: hovered onClicked: root.addRowAfter(model) } } } } Row { anchors.top: tableRow.bottom; anchors.topMargin: 4 Grid { columns: root.cols; columnSpacing: 3; rowSpacing: 3 Repeater { model: root.cols delegate: Button { width: root.cellW; height: 24 text: "+" ToolTip.text: qsTr("Добавить столбец") ToolTip.visible: hovered onClicked: root.addColAfter(model) } } } } } RowLayout { Layout.fillWidth: true Item { Layout.fillWidth: true } Label { text: qsTr("Строк: %1 Столбцов: %2").arg(root.rows).arg(root.cols); color: Theme.textMuted; font.pixelSize: 11 } Button { text: qsTr("Отмена"); onClicked: root.reject() } Button { text: qsTr("Вставить"); highlighted: true; onClicked: { root.markdownReady(buildMarkdown()); root.accept() } } } } }