diff --git a/Common/SettingsData.qml b/Common/SettingsData.qml index 6828d6e7..3524df82 100644 --- a/Common/SettingsData.qml +++ b/Common/SettingsData.qml @@ -83,6 +83,12 @@ Singleton { property string monoFontFamily: "Fira Code" property int fontWeight: Font.Normal property real fontScale: 1.0 + property bool notepadUseMonospace: true + property string notepadFontFamily: "" + property real notepadFontSize: 14 + onNotepadUseMonospaceChanged: saveSettings() + onNotepadFontFamilyChanged: saveSettings() + onNotepadFontSizeChanged: saveSettings() property bool gtkThemingEnabled: false property bool qtThemingEnabled: false property bool showDock: false @@ -248,6 +254,9 @@ Singleton { monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : defaultMonoFontFamily fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal fontScale = settings.fontScale !== undefined ? settings.fontScale : 1.0 + notepadUseMonospace = settings.notepadUseMonospace !== undefined ? settings.notepadUseMonospace : true + notepadFontFamily = settings.notepadFontFamily !== undefined ? settings.notepadFontFamily : "" + notepadFontSize = settings.notepadFontSize !== undefined ? settings.notepadFontSize : 14 gtkThemingEnabled = settings.gtkThemingEnabled !== undefined ? settings.gtkThemingEnabled : false qtThemingEnabled = settings.qtThemingEnabled !== undefined ? settings.qtThemingEnabled : false showDock = settings.showDock !== undefined ? settings.showDock : false @@ -346,6 +355,9 @@ Singleton { "monoFontFamily": monoFontFamily, "fontWeight": fontWeight, "fontScale": fontScale, + "notepadUseMonospace": notepadUseMonospace, + "notepadFontFamily": notepadFontFamily, + "notepadFontSize": notepadFontSize, "gtkThemingEnabled": gtkThemingEnabled, "qtThemingEnabled": qtThemingEnabled, "showDock": showDock, diff --git a/Modules/NotepadSlideout.qml b/Modules/NotepadSlideout.qml index f77a73b8..de381653 100644 --- a/Modules/NotepadSlideout.qml +++ b/Modules/NotepadSlideout.qml @@ -30,6 +30,10 @@ PanelWindow { property string currentContent: "" property string lastSavedContent: "" property bool contentLoaded: false + property bool showSettingsMenu: false + property var cachedFontFamilies: [] + property var cachedMonoFamilies: [] + property bool fontsEnumerated: false function hasUnsavedChanges() { if (!currentTab || !contentLoaded) { @@ -40,8 +44,6 @@ PanelWindow { if (currentTab.isTemporary) { return textArea.text.length > 0 } - - // For non-temporary files, show unsaved if content differs from last saved state return textArea.text !== lastSavedContent } @@ -59,6 +61,54 @@ PanelWindow { return false } + function enumerateFonts() { + var fonts = ["Default"] + var availableFonts = Qt.fontFamilies() + var rootFamilies = [] + var seenFamilies = new Set() + for (var i = 0; i < availableFonts.length; i++) { + var fontName = availableFonts[i] + if (fontName.startsWith(".")) + continue + + if (fontName === SettingsData.defaultFontFamily) + continue + + var rootName = fontName.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, + "").replace(/ (UI|Display|Text|Mono|Sans|Serif)$/i, function (match, suffix) { + return match + }).trim() + if (!seenFamilies.has(rootName) && rootName !== "") { + seenFamilies.add(rootName) + rootFamilies.push(rootName) + } + } + cachedFontFamilies = fonts.concat(rootFamilies.sort()) + var monoFonts = ["Default"] + var monoFamilies = [] + var seenMonoFamilies = new Set() + for (var j = 0; j < availableFonts.length; j++) { + var fontName2 = availableFonts[j] + if (fontName2.startsWith(".")) + continue + + if (fontName2 === SettingsData.defaultMonoFontFamily) + continue + + var lowerName = fontName2.toLowerCase() + if (lowerName.includes("mono") || lowerName.includes("code") || lowerName.includes("console") || lowerName.includes("terminal") || lowerName.includes("courier") || lowerName.includes("dejavu sans mono") || lowerName.includes( + "jetbrains") || lowerName.includes("fira") || lowerName.includes("hack") || lowerName.includes("source code") || lowerName.includes("ubuntu mono") || lowerName.includes("cascadia")) { + var rootName2 = fontName2.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").trim() + if (!seenMonoFamilies.has(rootName2) && rootName2 !== "") { + seenMonoFamilies.add(rootName2) + monoFamilies.push(rootName2) + } + } + } + cachedMonoFamilies = monoFonts.concat(monoFamilies.sort()) + fontsEnumerated = true + } + function loadCurrentTabContent() { if (!currentTab) return @@ -173,6 +223,12 @@ PanelWindow { WlrLayershell.exclusiveZone: 0 WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + Component.onCompleted: { + if (!fontsEnumerated) { + enumerateFonts() + } + } + StyledRect { id: contentRect @@ -417,8 +473,8 @@ PanelWindow { TextArea { id: textArea placeholderText: qsTr("Start typing your notes here...") - font.family: SettingsData.monoFontFamily - font.pixelSize: Theme.fontSizeMedium + font.family: SettingsData.notepadUseMonospace ? SettingsData.monoFontFamily : (SettingsData.notepadFontFamily || SettingsData.fontFamily) + font.pixelSize: SettingsData.notepadFontSize * SettingsData.fontScale color: Theme.surfaceText selectByMouse: true selectByKeyboard: true @@ -529,69 +585,85 @@ PanelWindow { width: parent.width spacing: Theme.spacingS - Row { + Item { width: parent.width - spacing: Theme.spacingL - + height: 32 + Row { - spacing: Theme.spacingS - DankActionButton { - iconName: "save" - iconSize: Theme.iconSize - 2 - iconColor: Theme.primary - enabled: currentTab && (hasUnsavedChanges() || textArea.text.length > 0) - onClicked: { - root.fileDialogOpen = true - saveBrowser.open() - } - } - StyledText { - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Save") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceTextMedium - } - } + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingL - Row { - spacing: Theme.spacingS - DankActionButton { - iconName: "folder_open" - iconSize: Theme.iconSize - 2 - iconColor: Theme.secondary - onClicked: { - if (hasUnsavedChanges()) { - root.pendingAction = "open" - root.confirmationDialogOpen = true - confirmationDialog.open() - } else { + Row { + spacing: Theme.spacingS + DankActionButton { + iconName: "save" + iconSize: Theme.iconSize - 2 + iconColor: Theme.primary + enabled: currentTab && (hasUnsavedChanges() || textArea.text.length > 0) + onClicked: { root.fileDialogOpen = true - loadBrowser.open() + saveBrowser.open() } } + StyledText { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Save") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceTextMedium + } } - StyledText { - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Open") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceTextMedium + + Row { + spacing: Theme.spacingS + DankActionButton { + iconName: "folder_open" + iconSize: Theme.iconSize - 2 + iconColor: Theme.secondary + onClicked: { + if (hasUnsavedChanges()) { + root.pendingAction = "open" + root.confirmationDialogOpen = true + confirmationDialog.open() + } else { + root.fileDialogOpen = true + loadBrowser.open() + } + } + } + StyledText { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Open") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceTextMedium + } + } + + Row { + spacing: Theme.spacingS + DankActionButton { + iconName: "note_add" + iconSize: Theme.iconSize - 2 + iconColor: Theme.surfaceText + onClicked: createNewTab() + } + StyledText { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("New") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceTextMedium + } } } - - Row { - spacing: Theme.spacingS - DankActionButton { - iconName: "note_add" - iconSize: Theme.iconSize - 2 - iconColor: Theme.surfaceText - onClicked: createNewTab() - } - StyledText { - anchors.verticalCenter: parent.verticalCenter - text: qsTr("New") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceTextMedium - } + + DankActionButton { + id: settingsButton + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + iconName: "more_horiz" + iconSize: Theme.iconSize - 2 + iconColor: Theme.surfaceText + onClicked: showSettingsMenu = !showSettingsMenu } } @@ -645,6 +717,203 @@ PanelWindow { } } } + + MouseArea { + anchors.fill: parent + visible: showSettingsMenu + onClicked: showSettingsMenu = false + z: 50 + } + + // Settings Menu Popup + Rectangle { + id: settingsMenu + visible: showSettingsMenu + anchors.horizontalCenter: parent.horizontalCenter + y: 423 + width: 360 + height: settingsColumn.implicitHeight + Theme.spacingXL * 2 + radius: Theme.cornerRadius + color: Theme.popupBackground() + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) + border.width: 1 + z: 100 + + Rectangle { + anchors.fill: parent + anchors.topMargin: 4 + anchors.leftMargin: 2 + anchors.rightMargin: -2 + anchors.bottomMargin: -4 + radius: parent.radius + color: Qt.rgba(0, 0, 0, 0.15) + z: parent.z - 1 + } + + Column { + id: settingsColumn + width: parent.width - Theme.spacingXL * 2 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: Theme.spacingXL + spacing: Theme.spacingS + + Rectangle { + width: parent.width + height: 36 + color: "transparent" + + StyledText { + anchors.left: parent.left + anchors.leftMargin: -Theme.spacingXS + anchors.verticalCenter: parent.verticalCenter + text: "Notepad Font Settings" + font.pixelSize: Theme.fontSizeMedium + font.weight: Font.Medium + color: Theme.surfaceText + } + } + + Rectangle { + width: parent.width + height: 1 + color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) + } + + DankToggle { + anchors.left: parent.left + anchors.leftMargin: -Theme.spacingM + width: parent.width + Theme.spacingM + text: "Use Monospace Font" + description: "Toggle fonts" + checked: SettingsData.notepadUseMonospace + onToggled: checked => { + SettingsData.notepadUseMonospace = checked + } + } + + Rectangle { + width: parent.width + height: visible ? (fontDropdown.height + Theme.spacingS) : 0 + color: "transparent" + visible: !SettingsData.notepadUseMonospace + + DankDropdown { + id: fontDropdown + anchors.left: parent.left + anchors.leftMargin: -Theme.spacingM + width: parent.width + Theme.spacingM + text: "Font Family" + options: cachedFontFamilies + currentValue: { + if (!SettingsData.notepadFontFamily || SettingsData.notepadFontFamily === "") + return "Default (Global)" + else + return SettingsData.notepadFontFamily + } + enableFuzzySearch: true + onValueChanged: value => { + if (value && (value.startsWith("Default") || value === "Default (Global)")) { + SettingsData.notepadFontFamily = "" + } else { + SettingsData.notepadFontFamily = value + } + } + } + } + + Rectangle { + width: parent.width + height: fontSizeRow.height + Theme.spacingS + color: "transparent" + + Row { + id: fontSizeRow + width: parent.width + spacing: Theme.spacingS + + Column { + width: parent.width - fontSizeControls.width - Theme.spacingM + spacing: Theme.spacingXS + + StyledText { + text: "Font Size" + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + } + + StyledText { + text: SettingsData.notepadFontSize + "px" + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + width: parent.width + } + } + + Row { + id: fontSizeControls + spacing: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + + DankActionButton { + buttonSize: 32 + iconName: "remove" + iconSize: Theme.iconSizeSmall + enabled: SettingsData.notepadFontSize > 8 + backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) + iconColor: Theme.surfaceText + onClicked: { + var newSize = Math.max(8, SettingsData.notepadFontSize - 1) + SettingsData.notepadFontSize = newSize + } + } + + Rectangle { + width: 60 + height: 32 + radius: Theme.cornerRadius + color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) + border.width: 1 + + StyledText { + anchors.centerIn: parent + text: SettingsData.notepadFontSize + "px" + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + } + } + + DankActionButton { + buttonSize: 32 + iconName: "add" + iconSize: Theme.iconSizeSmall + enabled: SettingsData.notepadFontSize < 48 + backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) + iconColor: Theme.surfaceText + onClicked: { + var newSize = Math.min(48, SettingsData.notepadFontSize + 1) + SettingsData.notepadFontSize = newSize + } + } + } + } + } + + StyledText { + width: parent.width + text: SettingsData.notepadUseMonospace ? + "Using global monospace font from Settings → Personalization" : + "Global fonts can be configured in Settings → Personalization" + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceTextMedium + wrapMode: Text.WordWrap + opacity: 0.8 + } + } + } } Timer { @@ -669,7 +938,6 @@ PanelWindow { pendingSaveContent = content saveFileView.path = filePath - // Use Qt.callLater to ensure path is set before calling setText Qt.callLater(() => { saveFileView.setText(pendingSaveContent) }) @@ -765,7 +1033,6 @@ PanelWindow { if (currentTab && currentTab.title && currentTab.title !== "Untitled") { return currentTab.title } else if (currentTab && !currentTab.isTemporary && currentTab.filePath) { - // Extract filename from path for non-temporary files return currentTab.filePath.split('/').pop() } else { return "note.txt"