Files
Flora/qml/MainWindow.qml
T
grendervill 277a87a53b UX: handles 12px хит-зона по всей высоте, календарь шире, Enter починен
- SplitView handle: implicit 12 (было 1/6), Rectangle fill parent + hit 16, cursor SplitH/VCursor, для файлового дерева/живого просмотра/графа-календаря — теперь легко попасть по всей высоте
- MonthCalendar: header 28x28 radius 14, month Label fill 70-110 + year 42/38, font 11, elide, spacing 4 — 'сентябрь 2026' не налезает, кнопки 28 круглые opaque (surface/hover/selected)
- RightPanel: удалён дубль 'Сегодня' ☷ из calBox header (оставлен только ⌂ в MonthCalendar)
- EditorPanel: Enter через Keys.onReturnPressed/onEnterPressed handleEnter() (сохранение indent), убран дубль в onPressed — теперь новая строка работает
2026-09-01 22:37:40 +03:00

306 lines
10 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, пока просто игнорируем
}
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()
}
}
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: 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()
}
}
}
}
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)
}
}
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 {
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
}
}
}
}