Files
Flora/qml/MainWindow.qml
T
grendervill 8e8cf3ce70 Графический браузер базы данных (DatabaseBrowser)
- Диалог: готовые выборки (файлы/теги/свойства/связи), свой SQL,
  результат таблицей, пути кликабельны, ошибки красным
- Ctrl+Enter для выполнения, автопересчёт при изменении базы
- Меню Файл -> База данных (Ctrl+D)
2026-09-07 16:39:32 +03:00

379 lines
13 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Flora
ApplicationWindow {
id: window
width: 1280
height: 800
minimumWidth: 900
minimumHeight: 600
visible: true
title: "Flora"
color: Theme.bg
palette.window: Theme.bg
palette.windowText: Theme.text
palette.base: Theme.surface
palette.text: Theme.text
palette.highlight: Theme.accent
palette.highlightedText: "white"
property string windowVault: ""
readonly property string activeVault: {
if (fileStore.ready) return fileStore.vault
if (windowVault.length > 0) return windowVault
return vaultManager.currentVault
}
WelcomeWizard {
id: welcomeWizard
firstRun: vaultManager.vaults.length === 0
onFinished: {
if (fileStore.ready) {
windowController.openWindow(welcomeWizard.vaultPath)
} else {
fileStore.openVault(welcomeWizard.vaultPath)
root_status("Хранилище открыто")
}
}
}
function openWizard() {
welcomeWizard.firstRun = false
welcomeWizard.open()
}
function root_status(msg, ok) {
// статус можно показать в UI, пока просто игнорируем
}
function applyAutosavePreset() {
if (settingsStore.autosaveEnabled) {
documentManager.setAutosaveIntervalMs(settingsStore.autosaveDiskMs)
documentManager.setAutosaveEnabled(true)
} else {
documentManager.setAutosaveEnabled(false)
}
}
Component.onCompleted: {
if (windowVault.length > 0) {
fileStore.openVault(windowVault)
} else if (vaultManager.currentVault.length > 0) {
fileStore.openVault(vaultManager.currentVault)
}
if (vaultManager.vaults.length === 0) {
welcomeWizard.open()
}
applyAutosavePreset()
}
Connections {
target: settingsStore
function onAutosavePresetChanged() { applyAutosavePreset() }
}
menuBar: MenuBar {
// Переопределяем цвета под тему (Basic стиль не красит сам)
background: Rectangle { color: Theme.bgAlt; border.color: Theme.divider }
Menu {
title: qsTr("&Файл")
Action { text: qsTr("Добавить хранилище…"); onTriggered: window.openWizard() }
Menu {
title: qsTr("Экспорт")
Action { text: qsTr("В HTML…"); enabled: false }
Action { text: qsTr("В PDF…"); enabled: false }
}
MenuSeparator { }
Action { text: qsTr("Настройки…"); onTriggered: settingsDialog.open() }
Action {
text: qsTr("Настройки хранилища…")
enabled: fileStore.ready
onTriggered: vaultSettingsDialog.open()
}
Action {
text: qsTr("База данных…")
shortcut: "Ctrl+D"
enabled: fileStore.ready
onTriggered: dbBrowser.open()
}
Action { text: qsTr("Выход"); onTriggered: window.close() }
}
Menu {
title: qsTr("&Правка")
Action { text: qsTr("Отменить"); shortcut: "Ctrl+Z"; enabled: false }
Action { text: qsTr("Повторить"); shortcut: "Ctrl+Shift+Z"; enabled: false }
MenuSeparator { }
Action {
text: qsTr("История файла…")
shortcut: "Ctrl+H"
enabled: documentManager.activeIndex >= 0 && fileStore.ready
onTriggered: {
const p = documentManager.pathAt(documentManager.activeIndex)
if (p.length>0) {
historyDialog.vaultPath = fileStore.vault
historyDialog.filePath = p
historyDialog.open()
}
}
}
Action {
text: qsTr("Вернуться к предыдущей версии…")
enabled: documentManager.activeIndex >= 0 && fileStore.ready
onTriggered: {
const p = documentManager.pathAt(documentManager.activeIndex)
if (p.length>0) {
historyDialog.vaultPath = fileStore.vault
historyDialog.filePath = p
historyDialog.open()
}
}
}
MenuSeparator { }
Action {
text: qsTr("Свойства заметки…")
enabled: documentManager.activeIndex >= 0 && fileStore.ready
onTriggered: {
const p = documentManager.pathAt(documentManager.activeIndex)
if (p.length>0) {
filePropsDialog.filePath = p
filePropsDialog.open()
}
}
}
}
Menu {
title: qsTr("&Вид")
ActionGroup { id: themeGroup }
Action {
text: qsTr("Светлая тема")
checkable: true
checked: settingsStore.theme !== "dark"
ActionGroup.group: themeGroup
onTriggered: settingsStore.theme = "light"
}
Action {
text: qsTr("Тёмная тема")
checkable: true
checked: settingsStore.theme === "dark"
ActionGroup.group: themeGroup
onTriggered: settingsStore.theme = "dark"
}
MenuSeparator { }
Action {
text: qsTr("Сбросить ширины панелей")
onTriggered: {
settingsStore.leftPanelWidth = 220
settingsStore.rightPanelWidth = 220
leftPanel.width = 220
rightPanel.width = 220
}
}
}
Menu {
title: qsTr("&Справка")
Action {
text: qsTr("О программе")
onTriggered: aboutDialog.open()
}
}
}
Dialog {
id: aboutDialog
title: qsTr("О программе")
standardButtons: Dialog.Ok
modal: true
anchors.centerIn: parent
background: Rectangle { color: Theme.surface; border.color: Theme.border; radius: 8 }
Label {
color: Theme.text
text: "Flora\nМинималистичный markdown-заметочник (GPL-3.0)\n"
+ "Версия " + Qt.application.version
}
}
HistoryDialog {
id: historyDialog
onRestored: {
// после восстановления обновляем граф и статус
if (fileStore.ready) vaultIndex.rebuild(fileStore.vault)
}
}
SettingsDialog {
id: settingsDialog
}
VaultSettingsDialog {
id: vaultSettingsDialog
}
FilePropertiesDialog {
id: filePropsDialog
}
DatabaseBrowser {
id: dbBrowser
}
// Fallback для пути визарда: хранилище открыли прямо в этом окне
// (не через WindowController) — открываем настройки и базу здесь.
// Обычный путь уже подключён в C++, повторный open() безвреден (guard ниже).
function ensureVaultDb() {
if (!fileStore.ready)
return
if (vaultDatabase.isOpen && vaultDatabase.vault === fileStore.vault)
return
vaultSettings.open(fileStore.vault)
if (vaultDatabase.open(fileStore.vault))
mdParser.setDatabaseForVault(fileStore.vault, vaultDatabase)
}
Connections {
target: fileStore
function onVaultChanged() { window.ensureVaultDb() }
function onReadyChanged() { if (fileStore.ready) window.ensureVaultDb() }
}
Popup {
id: vaultSwitcher
x: 12
y: window.height - height - 12
width: 340
modal: false
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
background: Rectangle { color: Theme.surface; border.color: Theme.borderStrong; radius: 8 }
ColumnLayout {
width: parent.width
spacing: 8
Label {
text: qsTr("Хранилища")
font.bold: true
color: Theme.text
}
ListView {
id: vaultList
Layout.preferredHeight: Math.min(200, contentHeight)
Layout.fillWidth: true
model: vaultManager.vaults
clip: true
ScrollBar.vertical: ScrollBar {}
delegate: ItemDelegate {
width: ListView.view.width
text: modelData
highlighted: modelData === window.activeVault
palette.highlight: Theme.accent
onClicked: {
if (modelData === window.activeVault) {
vaultSwitcher.close()
} else {
vaultManager.selectVault(modelData)
windowController.openWindow(modelData)
vaultSwitcher.close()
}
}
}
}
Label {
visible: vaultList.count === 0
text: qsTr("Хранилищ пока нет")
color: Theme.textMuted
}
Label {
text: qsTr("Текущее: %1").arg(window.activeVault.length > 0
? window.activeVault : "—")
color: Theme.textSecondary
elide: Text.ElideMiddle
Layout.fillWidth: true
font.pixelSize: 11
}
Button {
Layout.fillWidth: true
text: qsTr("Добавить новое хранилище…")
onClicked: { vaultSwitcher.close(); window.openWizard() }
}
}
}
SplitView {
id: mainSplit
anchors.fill: parent
orientation: Qt.Horizontal
// Увеличенная зона захвата — работает по всей высоте
handle: Rectangle {
implicitWidth: 12
color: "transparent"
Rectangle {
anchors.centerIn: parent
width: 1; height: parent.height
color: Theme.divider
}
Rectangle {
anchors.fill: parent
color: "transparent"
MouseArea {
anchors.fill: parent
cursorShape: Qt.SplitHCursor
hoverEnabled: true
onPressed: mouse.accepted = false
}
}
}
FileTreePanel {
id: leftPanel
SplitView.preferredWidth: settingsStore.leftPanelWidth
SplitView.minimumWidth: 180
SplitView.maximumWidth: 500
onWidthChanged: {
if (width >= 180 && width <= 500)
settingsStore.leftPanelWidth = width
}
onOpenVaultRequested: vaultSwitcher.open()
onOpenFileRequested: (path) => {
if (documentManager.isTextForEditor(path)) {
documentManager.openDocument(path)
} else {
leftPanel.showStatus(qsTr("Предпросмотр HTML/PDF/изображений появится в Фазе 6-10"), false)
}
}
onHistoryRequested: (path) => {
historyDialog.vaultPath = fileStore.vault
historyDialog.filePath = path
historyDialog.open()
}
}
EditorPanel {
id: centerPanel
SplitView.fillWidth: true
SplitView.minimumWidth: 300
height: window.height - (menuBar ? menuBar.height : 0)
onHistoryRequested: (path) => {
historyDialog.vaultPath = fileStore.vault
historyDialog.filePath = path
historyDialog.open()
}
}
RightPanel {
id: rightPanel
SplitView.preferredWidth: settingsStore.rightPanelWidth
SplitView.minimumWidth: 200
SplitView.maximumWidth: 500
onWidthChanged: {
if (width >= 200 && width <= 500)
settingsStore.rightPanelWidth = width
}
}
}
}