1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

settings: make settings and file browser normal windows

- add default floating rules for dankinstall
This commit is contained in:
bbedward
2025-11-23 01:23:06 -05:00
parent 61ec0c697a
commit 1c7201fb04
10 changed files with 924 additions and 983 deletions

View File

@@ -125,6 +125,10 @@ windowrulev2 = noborder, class:^(kitty)$
windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$ windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$
windowrulev2 = float, class:^(zoom)$ windowrulev2 = float, class:^(zoom)$
# DMS windows floating by default
windowrulev2 = float, class:^(org.quickshell)$, title:^(Settings)$
windowrulev2 = float, class:^(org.quickshell)$, title:^(File)$
windowrulev2 = opacity 0.9 0.9, floating:0, focus:0 windowrulev2 = opacity 0.9 0.9, floating:0, focus:0
layerrule = noanim, ^(quickshell)$ layerrule = noanim, ^(quickshell)$

View File

@@ -218,6 +218,18 @@ window-rule {
geometry-corner-radius 12 geometry-corner-radius 12
clip-to-geometry true clip-to-geometry true
} }
// Open dms settings as floating by default
window-rule {
match app-id=r#"org.quickshell$"#
match title="Settings"
open-floating true
}
// dms file browser
window-rule {
match app-id=r#"org.quickshell$"#
match title=r#"File$"#
open-floating true
}
binds { binds {
// === System & Overview === // === System & Overview ===
Mod+D { spawn "niri" "msg" "action" "toggle-overview"; } Mod+D { spawn "niri" "msg" "action" "toggle-overview"; }

View File

@@ -1236,7 +1236,6 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
const txt = settingsFile.text() const txt = settingsFile.text()
const obj = (txt && txt.trim()) ? JSON.parse(txt) : null const obj = (txt && txt.trim()) ? JSON.parse(txt) : null
Store.parse(root, obj) Store.parse(root, obj)
Store.migrate(root, obj)
} catch (e) { } catch (e) {
console.warn("SettingsData: Failed to reload settings:", e.message) console.warn("SettingsData: Failed to reload settings:", e.message)
} }

View File

@@ -508,6 +508,7 @@ Item {
hyprKeybindsModalLoader: hyprKeybindsModalLoader hyprKeybindsModalLoader: hyprKeybindsModalLoader
dankBarRepeater: dankBarRepeater dankBarRepeater: dankBarRepeater
hyprlandOverviewLoader: hyprlandOverviewLoader hyprlandOverviewLoader: hyprlandOverviewLoader
settingsModal: settingsModal
} }
Variants { Variants {

View File

@@ -15,6 +15,7 @@ Item {
required property var hyprKeybindsModalLoader required property var hyprKeybindsModalLoader
required property var dankBarRepeater required property var dankBarRepeater
required property var hyprlandOverviewLoader required property var hyprlandOverviewLoader
required property var settingsModal
function getFirstBar() { function getFirstBar() {
if (!root.dankBarRepeater || root.dankBarRepeater.count === 0) if (!root.dankBarRepeater || root.dankBarRepeater.count === 0)
@@ -529,4 +530,37 @@ Item {
target: "bar" target: "bar"
} }
IpcHandler {
function open(): string {
root.settingsModal.show();
return "SETTINGS_OPEN_SUCCESS";
}
function close(): string {
root.settingsModal.hide();
return "SETTINGS_CLOSE_SUCCESS";
}
function toggle(): string {
root.settingsModal.toggle();
return "SETTINGS_TOGGLE_SUCCESS";
}
target: "settings"
}
IpcHandler {
function browse(type: string) {
if (type === "wallpaper") {
root.settingsModal.wallpaperBrowser.allowStacking = false;
root.settingsModal.wallpaperBrowser.open();
} else if (type === "profile") {
root.settingsModal.profileBrowser.allowStacking = false;
root.settingsModal.profileBrowser.open();
}
}
target: "file"
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,124 +1,52 @@
import QtQuick import QtQuick
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import qs.Common import qs.Common
import qs.Modals.Common
import qs.Modals.FileBrowser import qs.Modals.FileBrowser
import qs.Modules.Settings
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
DankModal { FloatingWindow {
id: settingsModal id: settingsModal
layerNamespace: "dms:settings"
HyprlandFocusGrab {
windows: [settingsModal]
active: CompositorService.isHyprland && settingsModal.shouldHaveFocus
}
property Component settingsContent
property alias profileBrowser: profileBrowser property alias profileBrowser: profileBrowser
property alias wallpaperBrowser: wallpaperBrowser
property int currentTabIndex: 0 property int currentTabIndex: 0
property bool shouldHaveFocus: visible
property bool allowFocusOverride: false
property alias shouldBeVisible: settingsModal.visible
signal closingModal() signal closingModal
function show() { function show() {
open(); visible = true;
} }
function hide() { function hide() {
close(); visible = false;
} }
function toggle() { function toggle() {
if (shouldBeVisible) { visible = !visible;
hide();
} else {
show();
}
} }
objectName: "settingsModal" objectName: "settingsModal"
width: Math.min(800, screenWidth * 0.9) title: "Settings"
height: Math.min(800, screenHeight * 0.85) implicitWidth: 800
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) implicitHeight: 800
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
visible: false visible: false
onBackgroundClicked: () => {
return hide();
}
content: settingsContent
onOpened: () => {
Qt.callLater(() => {
modalFocusScope.forceActiveFocus()
if (contentLoader.item) {
contentLoader.item.forceActiveFocus()
}
})
}
onVisibleChanged: { onVisibleChanged: {
if (visible && shouldBeVisible) { if (!visible) {
closingModal();
} else {
Qt.callLater(() => { Qt.callLater(() => {
modalFocusScope.forceActiveFocus() if (contentFocusScope) {
if (contentLoader.item) { contentFocusScope.forceActiveFocus();
contentLoader.item.forceActiveFocus()
} }
}) });
} }
} }
modalFocusScope.Keys.onPressed: event => {
const tabCount = 11
if (event.key === Qt.Key_Down) {
currentTabIndex = (currentTabIndex + 1) % tabCount
event.accepted = true
} else if (event.key === Qt.Key_Up) {
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount
event.accepted = true
} else if (event.key === Qt.Key_Tab && !event.modifiers) {
currentTabIndex = (currentTabIndex + 1) % tabCount
event.accepted = true
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && event.modifiers & Qt.ShiftModifier)) {
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount
event.accepted = true
}
}
IpcHandler {
function open(): string {
settingsModal.show();
return "SETTINGS_OPEN_SUCCESS";
}
function close(): string {
settingsModal.hide();
return "SETTINGS_CLOSE_SUCCESS";
}
function toggle(): string {
settingsModal.toggle();
return "SETTINGS_TOGGLE_SUCCESS";
}
target: "settings"
}
IpcHandler {
function browse(type: string) {
if (type === "wallpaper") {
wallpaperBrowser.allowStacking = false;
wallpaperBrowser.open();
} else if (type === "profile") {
profileBrowser.allowStacking = false;
profileBrowser.open();
}
}
target: "file"
}
FileBrowserModal { FileBrowserModal {
id: profileBrowser id: profileBrowser
@@ -130,20 +58,12 @@ DankModal {
browserType: "profile" browserType: "profile"
showHiddenFiles: true showHiddenFiles: true
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"] fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
onFileSelected: (path) => { onFileSelected: path => {
PortalService.setProfileImage(path); PortalService.setProfileImage(path);
close(); close();
} }
onDialogClosed: () => { onDialogClosed: () => {
allowStacking = true; allowStacking = true;
if (settingsModal.shouldBeVisible) {
Qt.callLater(() => {
settingsModal.modalFocusScope.forceActiveFocus()
if (settingsModal.contentLoader.item) {
settingsModal.contentLoader.item.forceActiveFocus()
}
})
}
} }
} }
@@ -157,111 +77,120 @@ DankModal {
browserType: "wallpaper" browserType: "wallpaper"
showHiddenFiles: true showHiddenFiles: true
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"] fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
onFileSelected: (path) => { onFileSelected: path => {
SessionData.setWallpaper(path); SessionData.setWallpaper(path);
close(); close();
} }
onDialogClosed: () => { onDialogClosed: () => {
allowStacking = true; allowStacking = true;
if (settingsModal.shouldBeVisible) {
Qt.callLater(() => {
settingsModal.modalFocusScope.forceActiveFocus()
if (settingsModal.contentLoader.item) {
settingsModal.contentLoader.item.forceActiveFocus()
}
})
}
} }
} }
settingsContent: Component { FocusScope {
Item { id: contentFocusScope
id: rootScope
anchors.fill: parent
Keys.onEscapePressed: event => { anchors.fill: parent
settingsModal.hide() focus: true
event.accepted = true
Keys.onPressed: event => {
const tabCount = 11;
if (event.key === Qt.Key_Escape) {
hide();
event.accepted = true;
return;
} }
if (event.key === Qt.Key_Down) {
currentTabIndex = (currentTabIndex + 1) % tabCount;
event.accepted = true;
return;
}
if (event.key === Qt.Key_Up) {
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount;
event.accepted = true;
return;
}
if (event.key === Qt.Key_Tab && !event.modifiers) {
currentTabIndex = (currentTabIndex + 1) % tabCount;
event.accepted = true;
return;
}
if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && event.modifiers & Qt.ShiftModifier)) {
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount;
event.accepted = true;
return;
}
}
Column { Column {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingL anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL anchors.rightMargin: Theme.spacingL
anchors.topMargin: Theme.spacingM anchors.topMargin: Theme.spacingM
anchors.bottomMargin: Theme.spacingL anchors.bottomMargin: Theme.spacingL
spacing: 0 spacing: 0
Item { Item {
width: parent.width width: parent.width
height: 35 height: 35
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "settings"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Settings")
font.pixelSize: Theme.fontSizeXLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
DankActionButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
circular: false
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
onClicked: () => {
return settingsModal.hide();
}
}
}
Row { Row {
width: parent.width anchors.left: parent.left
height: parent.height - 35 anchors.verticalCenter: parent.verticalCenter
spacing: 0 spacing: Theme.spacingM
SettingsSidebar { DankIcon {
id: sidebar name: "settings"
size: Theme.iconSize
parentModal: settingsModal color: Theme.primary
currentIndex: settingsModal.currentTabIndex anchors.verticalCenter: parent.verticalCenter
onCurrentIndexChanged: {
settingsModal.currentTabIndex = currentIndex
}
} }
SettingsContent { StyledText {
id: content text: I18n.tr("Settings")
font.pixelSize: Theme.fontSizeXLarge
width: parent.width - sidebar.width color: Theme.surfaceText
height: parent.height font.weight: Font.Medium
parentModal: settingsModal anchors.verticalCenter: parent.verticalCenter
currentIndex: settingsModal.currentTabIndex
} }
} }
DankActionButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
circular: false
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
onClicked: () => {
settingsModal.hide();
}
}
} }
Row {
width: parent.width
height: parent.height - 35
spacing: 0
SettingsSidebar {
id: sidebar
parentModal: settingsModal
currentIndex: settingsModal.currentTabIndex
onCurrentIndexChanged: {
settingsModal.currentTabIndex = currentIndex;
}
}
SettingsContent {
id: content
width: parent.width - sidebar.width
height: parent.height
parentModal: settingsModal
currentIndex: settingsModal.currentTabIndex
}
}
} }
} }
} }

View File

@@ -1,8 +1,5 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls
import QtCore
import Quickshell
import Quickshell.Wayland
import Quickshell.Io import Quickshell.Io
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
@@ -10,8 +7,6 @@ import qs.Modals.FileBrowser
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
pragma ComponentBehavior: Bound
Item { Item {
id: root id: root
@@ -26,119 +21,119 @@ Item {
property bool showSettingsMenu: false property bool showSettingsMenu: false
property string pendingSaveContent: "" property string pendingSaveContent: ""
signal hideRequested() signal hideRequested
Ref { Ref {
service: NotepadStorageService service: NotepadStorageService
} }
function hasUnsavedChanges() { function hasUnsavedChanges() {
return textEditor.hasUnsavedChanges() return textEditor.hasUnsavedChanges();
} }
function hasUnsavedTemporaryContent() { function hasUnsavedTemporaryContent() {
return hasUnsavedChanges() return hasUnsavedChanges();
} }
function createNewTab() { function createNewTab() {
performCreateNewTab() performCreateNewTab();
} }
function performCreateNewTab() { function performCreateNewTab() {
NotepadStorageService.createNewTab() NotepadStorageService.createNewTab();
textEditor.text = "" textEditor.text = "";
textEditor.lastSavedContent = "" textEditor.lastSavedContent = "";
textEditor.contentLoaded = true textEditor.contentLoaded = true;
textEditor.textArea.forceActiveFocus() textEditor.textArea.forceActiveFocus();
} }
function closeTab(tabIndex) { function closeTab(tabIndex) {
if (tabIndex === NotepadStorageService.currentTabIndex && hasUnsavedChanges()) { if (tabIndex === NotepadStorageService.currentTabIndex && hasUnsavedChanges()) {
root.pendingAction = "close_tab_" + tabIndex root.pendingAction = "close_tab_" + tabIndex;
root.confirmationDialogOpen = true root.confirmationDialogOpen = true;
confirmationDialog.open() confirmationDialog.open();
} else { } else {
performCloseTab(tabIndex) performCloseTab(tabIndex);
} }
} }
function performCloseTab(tabIndex) { function performCloseTab(tabIndex) {
NotepadStorageService.closeTab(tabIndex) NotepadStorageService.closeTab(tabIndex);
Qt.callLater(() => { Qt.callLater(() => {
textEditor.loadCurrentTabContent() textEditor.loadCurrentTabContent();
}) });
} }
function switchToTab(tabIndex) { function switchToTab(tabIndex) {
if (tabIndex < 0 || tabIndex >= NotepadStorageService.tabs.length) return if (tabIndex < 0 || tabIndex >= NotepadStorageService.tabs.length)
return;
if (textEditor.contentLoaded) { if (textEditor.contentLoaded) {
textEditor.autoSaveToSession() textEditor.autoSaveToSession();
} }
NotepadStorageService.switchToTab(tabIndex) NotepadStorageService.switchToTab(tabIndex);
Qt.callLater(() => { Qt.callLater(() => {
textEditor.loadCurrentTabContent() textEditor.loadCurrentTabContent();
if (currentTab) { if (currentTab) {
root.currentFileName = currentTab.fileName || "" root.currentFileName = currentTab.fileName || "";
root.currentFileUrl = currentTab.fileUrl || "" root.currentFileUrl = currentTab.fileUrl || "";
} }
}) });
} }
function saveToFile(fileUrl) { function saveToFile(fileUrl) {
if (!currentTab) return if (!currentTab)
return;
var content = textEditor.text;
var filePath = fileUrl.toString().replace(/^file:\/\//, '');
var content = textEditor.text saveFileView.path = "";
var filePath = fileUrl.toString().replace(/^file:\/\//, '') pendingSaveContent = content;
saveFileView.path = filePath;
saveFileView.path = ""
pendingSaveContent = content
saveFileView.path = filePath
Qt.callLater(() => { Qt.callLater(() => {
saveFileView.setText(pendingSaveContent) saveFileView.setText(pendingSaveContent);
}) });
} }
function loadFromFile(fileUrl) { function loadFromFile(fileUrl) {
if (hasUnsavedTemporaryContent()) { if (hasUnsavedTemporaryContent()) {
root.pendingFileUrl = fileUrl root.pendingFileUrl = fileUrl;
root.pendingAction = "load_file" root.pendingAction = "load_file";
root.confirmationDialogOpen = true root.confirmationDialogOpen = true;
confirmationDialog.open() confirmationDialog.open();
} else { } else {
performLoadFromFile(fileUrl) performLoadFromFile(fileUrl);
} }
} }
function performLoadFromFile(fileUrl) { function performLoadFromFile(fileUrl) {
const filePath = fileUrl.toString().replace(/^file:\/\//, '') const filePath = fileUrl.toString().replace(/^file:\/\//, '');
const fileName = filePath.split('/').pop() const fileName = filePath.split('/').pop();
loadFileView.path = "" loadFileView.path = "";
loadFileView.path = filePath loadFileView.path = filePath;
if (loadFileView.waitForJob()) { if (loadFileView.waitForJob()) {
Qt.callLater(() => { Qt.callLater(() => {
var content = loadFileView.text() var content = loadFileView.text();
if (currentTab && content !== undefined && content !== null) { if (currentTab && content !== undefined && content !== null) {
textEditor.text = content textEditor.text = content;
textEditor.lastSavedContent = content textEditor.lastSavedContent = content;
textEditor.contentLoaded = true textEditor.contentLoaded = true;
root.lastSavedFileContent = content root.lastSavedFileContent = content;
NotepadStorageService.updateTabMetadata(NotepadStorageService.currentTabIndex, { NotepadStorageService.updateTabMetadata(NotepadStorageService.currentTabIndex, {
title: fileName, title: fileName,
filePath: filePath, filePath: filePath,
isTemporary: false isTemporary: false
}) });
root.currentFileName = fileName root.currentFileName = fileName;
root.currentFileUrl = fileUrl root.currentFileUrl = fileUrl;
textEditor.saveCurrentTabContent() textEditor.saveCurrentTabContent();
} }
}) });
} }
} }
@@ -151,16 +146,16 @@ Item {
width: parent.width width: parent.width
contentLoaded: textEditor.contentLoaded contentLoaded: textEditor.contentLoaded
onTabSwitched: (tabIndex) => { onTabSwitched: tabIndex => {
switchToTab(tabIndex) switchToTab(tabIndex);
} }
onTabClosed: (tabIndex) => { onTabClosed: tabIndex => {
closeTab(tabIndex) closeTab(tabIndex);
} }
onNewTabRequested: { onNewTabRequested: {
createNewTab() createNewTab();
} }
} }
@@ -171,41 +166,41 @@ Item {
onSaveRequested: { onSaveRequested: {
if (currentTab && !currentTab.isTemporary && currentTab.filePath) { if (currentTab && !currentTab.isTemporary && currentTab.filePath) {
var fileUrl = "file://" + currentTab.filePath var fileUrl = "file://" + currentTab.filePath;
saveToFile(fileUrl) saveToFile(fileUrl);
} else { } else {
root.fileDialogOpen = true root.fileDialogOpen = true;
saveBrowser.open() saveBrowser.open();
} }
} }
onOpenRequested: { onOpenRequested: {
if (hasUnsavedChanges()) { if (hasUnsavedChanges()) {
root.pendingAction = "open" root.pendingAction = "open";
root.confirmationDialogOpen = true root.confirmationDialogOpen = true;
confirmationDialog.open() confirmationDialog.open();
} else { } else {
root.fileDialogOpen = true root.fileDialogOpen = true;
loadBrowser.open() loadBrowser.open();
} }
} }
onNewRequested: { onNewRequested: {
if (hasUnsavedChanges()) { if (hasUnsavedChanges()) {
root.pendingAction = "new" root.pendingAction = "new";
root.confirmationDialogOpen = true root.confirmationDialogOpen = true;
confirmationDialog.open() confirmationDialog.open();
} else { } else {
createNewTab() createNewTab();
} }
} }
onEscapePressed: { onEscapePressed: {
root.hideRequested() root.hideRequested();
} }
onSettingsRequested: { onSettingsRequested: {
showSettingsMenu = !showSettingsMenu showSettingsMenu = !showSettingsMenu;
} }
} }
} }
@@ -216,8 +211,8 @@ Item {
isVisible: showSettingsMenu isVisible: showSettingsMenu
onSettingsRequested: showSettingsMenu = !showSettingsMenu onSettingsRequested: showSettingsMenu = !showSettingsMenu
onFindRequested: { onFindRequested: {
showSettingsMenu = false showSettingsMenu = false;
textEditor.showSearch() textEditor.showSearch();
} }
} }
@@ -233,14 +228,14 @@ Item {
NotepadStorageService.updateTabMetadata(NotepadStorageService.currentTabIndex, { NotepadStorageService.updateTabMetadata(NotepadStorageService.currentTabIndex, {
hasUnsavedChanges: false, hasUnsavedChanges: false,
lastSavedContent: pendingSaveContent lastSavedContent: pendingSaveContent
}) });
root.lastSavedFileContent = pendingSaveContent root.lastSavedFileContent = pendingSaveContent;
pendingSaveContent = "" pendingSaveContent = "";
} }
} }
onSaveFailed: (error) => { onSaveFailed: error => {
pendingSaveContent = "" pendingSaveContent = "";
} }
} }
@@ -251,8 +246,7 @@ Item {
atomicWrites: true atomicWrites: true
printErrors: true printErrors: true
onLoadFailed: (error) => { onLoadFailed: error => {}
}
} }
FileBrowserModal { FileBrowserModal {
@@ -266,56 +260,51 @@ Item {
saveMode: true saveMode: true
defaultFileName: { defaultFileName: {
if (currentTab && currentTab.title && currentTab.title !== "Untitled") { if (currentTab && currentTab.title && currentTab.title !== "Untitled") {
return currentTab.title return currentTab.title;
} else if (currentTab && !currentTab.isTemporary && currentTab.filePath) { } else if (currentTab && !currentTab.isTemporary && currentTab.filePath) {
return currentTab.filePath.split('/').pop() return currentTab.filePath.split('/').pop();
} else { } else {
return "note.txt" return "note.txt";
} }
} }
WlrLayershell.layer: WlrLayershell.Overlay onFileSelected: path => {
root.fileDialogOpen = false;
const cleanPath = path.toString().replace(/^file:\/\//, '');
const fileName = cleanPath.split('/').pop();
const fileUrl = "file://" + cleanPath;
onFileSelected: (path) => { root.currentFileName = fileName;
root.fileDialogOpen = false root.currentFileUrl = fileUrl;
const cleanPath = path.toString().replace(/^file:\/\//, '')
const fileName = cleanPath.split('/').pop()
const fileUrl = "file://" + cleanPath
root.currentFileName = fileName
root.currentFileUrl = fileUrl
if (currentTab) { if (currentTab) {
NotepadStorageService.saveTabAs( NotepadStorageService.saveTabAs(NotepadStorageService.currentTabIndex, cleanPath);
NotepadStorageService.currentTabIndex,
cleanPath
)
} }
saveToFile(fileUrl) saveToFile(fileUrl);
if (root.pendingAction === "new") { if (root.pendingAction === "new") {
Qt.callLater(() => { Qt.callLater(() => {
createNewTab() createNewTab();
}) });
} else if (root.pendingAction === "open") { } else if (root.pendingAction === "open") {
Qt.callLater(() => { Qt.callLater(() => {
root.fileDialogOpen = true root.fileDialogOpen = true;
loadBrowser.open() loadBrowser.open();
}) });
} else if (root.pendingAction.startsWith("close_tab_")) { } else if (root.pendingAction.startsWith("close_tab_")) {
Qt.callLater(() => { Qt.callLater(() => {
var tabIndex = parseInt(root.pendingAction.split("_")[2]) var tabIndex = parseInt(root.pendingAction.split("_")[2]);
performCloseTab(tabIndex) performCloseTab(tabIndex);
}) });
} }
root.pendingAction = "" root.pendingAction = "";
close() close();
} }
onDialogClosed: { onDialogClosed: {
root.fileDialogOpen = false root.fileDialogOpen = false;
} }
} }
@@ -328,23 +317,21 @@ Item {
fileExtensions: ["*.txt", "*.md", "*.*"] fileExtensions: ["*.txt", "*.md", "*.*"]
allowStacking: true allowStacking: true
WlrLayershell.layer: WlrLayershell.Overlay onFileSelected: path => {
root.fileDialogOpen = false;
const cleanPath = path.toString().replace(/^file:\/\//, '');
const fileName = cleanPath.split('/').pop();
const fileUrl = "file://" + cleanPath;
onFileSelected: (path) => { root.currentFileName = fileName;
root.fileDialogOpen = false root.currentFileUrl = fileUrl;
const cleanPath = path.toString().replace(/^file:\/\//, '')
const fileName = cleanPath.split('/').pop()
const fileUrl = "file://" + cleanPath
root.currentFileName = fileName loadFromFile(fileUrl);
root.currentFileUrl = fileUrl close();
loadFromFile(fileUrl)
close()
} }
onDialogClosed: { onDialogClosed: {
root.fileDialogOpen = false root.fileDialogOpen = false;
} }
} }
@@ -357,8 +344,8 @@ Item {
allowStacking: true allowStacking: true
onBackgroundClicked: { onBackgroundClicked: {
close() close();
root.confirmationDialogOpen = false root.confirmationDialogOpen = false;
} }
content: Component { content: Component {
@@ -367,9 +354,9 @@ Item {
focus: true focus: true
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
confirmationDialog.close() confirmationDialog.close();
root.confirmationDialogOpen = false root.confirmationDialogOpen = false;
event.accepted = true event.accepted = true;
} }
Column { Column {
@@ -392,13 +379,7 @@ Item {
} }
StyledText { StyledText {
text: root.pendingAction === "new" ? text: root.pendingAction === "new" ? I18n.tr("You have unsaved changes. Save before creating a new file?") : root.pendingAction.startsWith("close_tab_") ? I18n.tr("You have unsaved changes. Save before closing this tab?") : root.pendingAction === "load_file" || root.pendingAction === "open" ? I18n.tr("You have unsaved changes. Save before opening a file?") : I18n.tr("You have unsaved changes. Save before continuing?")
I18n.tr("You have unsaved changes. Save before creating a new file?") :
root.pendingAction.startsWith("close_tab_") ?
I18n.tr("You have unsaved changes. Save before closing this tab?") :
root.pendingAction === "load_file" || root.pendingAction === "open" ?
I18n.tr("You have unsaved changes. Save before opening a file?") :
I18n.tr("You have unsaved changes. Save before continuing?")
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium color: Theme.surfaceTextMedium
width: parent.width width: parent.width
@@ -411,8 +392,8 @@ Item {
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: { onClicked: {
confirmationDialog.close() confirmationDialog.close();
root.confirmationDialogOpen = false root.confirmationDialogOpen = false;
} }
} }
} }
@@ -449,21 +430,21 @@ Item {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
confirmationDialog.close() confirmationDialog.close();
root.confirmationDialogOpen = false root.confirmationDialogOpen = false;
if (root.pendingAction === "new") { if (root.pendingAction === "new") {
createNewTab() createNewTab();
} else if (root.pendingAction === "open") { } else if (root.pendingAction === "open") {
root.fileDialogOpen = true root.fileDialogOpen = true;
loadBrowser.open() loadBrowser.open();
} else if (root.pendingAction === "load_file") { } else if (root.pendingAction === "load_file") {
performLoadFromFile(root.pendingFileUrl) performLoadFromFile(root.pendingFileUrl);
} else if (root.pendingAction.startsWith("close_tab_")) { } else if (root.pendingAction.startsWith("close_tab_")) {
var tabIndex = parseInt(root.pendingAction.split("_")[2]) var tabIndex = parseInt(root.pendingAction.split("_")[2]);
performCloseTab(tabIndex) performCloseTab(tabIndex);
} }
root.pendingAction = "" root.pendingAction = "";
root.pendingFileUrl = "" root.pendingFileUrl = "";
} }
} }
} }
@@ -489,10 +470,10 @@ Item {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
confirmationDialog.close() confirmationDialog.close();
root.confirmationDialogOpen = false root.confirmationDialogOpen = false;
root.fileDialogOpen = true root.fileDialogOpen = true;
saveBrowser.open() saveBrowser.open();
} }
} }
@@ -503,11 +484,10 @@ Item {
} }
} }
} }
} }
} }
} }
} }
} }
} }
} }

View File

@@ -1,10 +1,7 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Services
import qs.Widgets
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Widgets
Item { Item {
id: root id: root
@@ -14,60 +11,54 @@ Item {
property var cachedMonoFamilies: [] property var cachedMonoFamilies: []
property bool fontsEnumerated: false property bool fontsEnumerated: false
signal settingsRequested() signal settingsRequested
signal findRequested() signal findRequested
function enumerateFonts() { function enumerateFonts() {
var fonts = ["Default"] var fonts = ["Default"];
var availableFonts = Qt.fontFamilies() var availableFonts = Qt.fontFamilies();
var rootFamilies = [] var rootFamilies = [];
var seenFamilies = new Set() var seenFamilies = new Set();
for (var i = 0; i < availableFonts.length; i++) { for (var i = 0; i < availableFonts.length; i++) {
var fontName = availableFonts[i] var fontName = availableFonts[i];
if (fontName.startsWith(".")) if (fontName.startsWith("."))
continue continue;
if (fontName === SettingsData.defaultFontFamily) if (fontName === SettingsData.defaultFontFamily)
continue 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) {
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, return match;
"").replace(/ (UI|Display|Text|Mono|Sans|Serif)$/i, function (match, suffix) { }).trim();
return match
}).trim()
if (!seenFamilies.has(rootName) && rootName !== "") { if (!seenFamilies.has(rootName) && rootName !== "") {
seenFamilies.add(rootName) seenFamilies.add(rootName);
rootFamilies.push(rootName) rootFamilies.push(rootName);
} }
} }
cachedFontFamilies = fonts.concat(rootFamilies.sort()) cachedFontFamilies = fonts.concat(rootFamilies.sort());
var monoFonts = ["Default"] var monoFonts = ["Default"];
var monoFamilies = [] var monoFamilies = [];
var seenMonoFamilies = new Set() var seenMonoFamilies = new Set();
for (var j = 0; j < availableFonts.length; j++) { for (var j = 0; j < availableFonts.length; j++) {
var fontName2 = availableFonts[j] var fontName2 = availableFonts[j];
if (fontName2.startsWith(".")) if (fontName2.startsWith("."))
continue continue;
if (fontName2 === SettingsData.defaultMonoFontFamily) if (fontName2 === SettingsData.defaultMonoFontFamily)
continue continue;
var lowerName = fontName2.toLowerCase();
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")) {
if (lowerName.includes("mono") || lowerName.includes("code") || lowerName.includes("console") || lowerName.includes("terminal") || lowerName.includes("courier") || lowerName.includes("dejavu sans mono") || lowerName.includes( 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();
"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 !== "") { if (!seenMonoFamilies.has(rootName2) && rootName2 !== "") {
seenMonoFamilies.add(rootName2) seenMonoFamilies.add(rootName2);
monoFamilies.push(rootName2) monoFamilies.push(rootName2);
} }
} }
} }
cachedMonoFamilies = monoFonts.concat(monoFamilies.sort()) cachedMonoFamilies = monoFonts.concat(monoFamilies.sort());
fontsEnumerated = true fontsEnumerated = true;
} }
Component.onCompleted: { Component.onCompleted: {
if (!fontsEnumerated) { if (!fontsEnumerated) {
enumerateFonts() enumerateFonts();
} }
} }
@@ -140,7 +131,7 @@ Item {
description: "Toggle fonts" description: "Toggle fonts"
checked: SettingsData.notepadUseMonospace checked: SettingsData.notepadUseMonospace
onToggled: checked => { onToggled: checked => {
SettingsData.notepadUseMonospace = checked SettingsData.notepadUseMonospace = checked;
} }
} }
@@ -152,7 +143,7 @@ Item {
description: "Display line numbers in editor" description: "Display line numbers in editor"
checked: SettingsData.notepadShowLineNumbers checked: SettingsData.notepadShowLineNumbers
onToggled: checked => { onToggled: checked => {
SettingsData.notepadShowLineNumbers = checked SettingsData.notepadShowLineNumbers = checked;
} }
} }
@@ -214,23 +205,23 @@ Item {
DankDropdown { DankDropdown {
id: fontDropdown id: fontDropdown
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: -Theme.spacingM anchors.leftMargin: -Theme.spacingM
width: parent.width + Theme.spacingM width: parent.width + Theme.spacingM
text: I18n.tr("Font Family") text: I18n.tr("Font Family")
options: cachedFontFamilies options: cachedFontFamilies
currentValue: { currentValue: {
if (!SettingsData.notepadFontFamily || SettingsData.notepadFontFamily === "") if (!SettingsData.notepadFontFamily || SettingsData.notepadFontFamily === "")
return "Default (Global)" return "Default (Global)";
else else
return SettingsData.notepadFontFamily return SettingsData.notepadFontFamily;
} }
enableFuzzySearch: true enableFuzzySearch: true
onValueChanged: value => { onValueChanged: value => {
if (value && (value.startsWith("Default") || value === "Default (Global)")) { if (value && (value.startsWith("Default") || value === "Default (Global)")) {
SettingsData.notepadFontFamily = "" SettingsData.notepadFontFamily = "";
} else { } else {
SettingsData.notepadFontFamily = value SettingsData.notepadFontFamily = value;
} }
} }
} }
@@ -278,8 +269,8 @@ Item {
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: { onClicked: {
var newSize = Math.max(8, SettingsData.notepadFontSize - 1) var newSize = Math.max(8, SettingsData.notepadFontSize - 1);
SettingsData.notepadFontSize = newSize SettingsData.notepadFontSize = newSize;
} }
} }
@@ -308,8 +299,8 @@ Item {
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: { onClicked: {
var newSize = Math.min(48, SettingsData.notepadFontSize + 1) var newSize = Math.min(48, SettingsData.notepadFontSize + 1);
SettingsData.notepadFontSize = newSize SettingsData.notepadFontSize = newSize;
} }
} }
} }
@@ -335,9 +326,9 @@ Item {
checked: SettingsData.notepadTransparencyOverride >= 0 checked: SettingsData.notepadTransparencyOverride >= 0
onToggled: checked => { onToggled: checked => {
if (checked) { if (checked) {
SettingsData.notepadTransparencyOverride = SettingsData.notepadLastCustomTransparency SettingsData.notepadTransparencyOverride = SettingsData.notepadLastCustomTransparency;
} else { } else {
SettingsData.notepadTransparencyOverride = -1 SettingsData.notepadTransparencyOverride = -1;
} }
} }
} }
@@ -356,7 +347,7 @@ Item {
wheelEnabled: false wheelEnabled: false
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
if (SettingsData.notepadTransparencyOverride >= 0) { if (SettingsData.notepadTransparencyOverride >= 0) {
SettingsData.notepadTransparencyOverride = newValue / 100 SettingsData.notepadTransparencyOverride = newValue / 100;
} }
} }
} }
@@ -365,9 +356,7 @@ Item {
StyledText { StyledText {
width: parent.width width: parent.width
text: SettingsData.notepadUseMonospace ? text: SettingsData.notepadUseMonospace ? "Using global monospace font from Settings → Personalization" : "Global fonts can be configured in Settings → Personalization"
"Using global monospace font from Settings → Personalization" :
"Global fonts can be configured in Settings → Personalization"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium color: Theme.surfaceTextMedium
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
@@ -375,4 +364,4 @@ Item {
} }
} }
} }
} }

View File

@@ -1,11 +1,10 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import qs.Common import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
pragma ComponentBehavior: Bound
Column { Column {
id: root id: root
@@ -14,15 +13,16 @@ Column {
signal tabSwitched(int tabIndex) signal tabSwitched(int tabIndex)
signal tabClosed(int tabIndex) signal tabClosed(int tabIndex)
signal newTabRequested() signal newTabRequested
function hasUnsavedChangesForTab(tab) { function hasUnsavedChangesForTab(tab) {
if (!tab) return false if (!tab)
return false;
if (tab.id === currentTab?.id) { if (tab.id === currentTab?.id) {
return root.parent?.hasUnsavedChanges ? root.parent.hasUnsavedChanges() : false return root.parent?.hasUnsavedChanges ? root.parent.hasUnsavedChanges() : false;
} }
return false return false;
} }
spacing: Theme.spacingXS spacing: Theme.spacingXS
@@ -53,11 +53,11 @@ Column {
readonly property bool isActive: NotepadStorageService.currentTabIndex === index readonly property bool isActive: NotepadStorageService.currentTabIndex === index
readonly property bool isHovered: tabMouseArea.containsMouse && !closeMouseArea.containsMouse readonly property bool isHovered: tabMouseArea.containsMouse && !closeMouseArea.containsMouse
readonly property real calculatedWidth: { readonly property real calculatedWidth: {
const textWidth = tabText.paintedWidth || 100 const textWidth = tabText.paintedWidth || 100;
const closeButtonWidth = NotepadStorageService.tabs.length > 1 ? 20 : 0 const closeButtonWidth = NotepadStorageService.tabs.length > 1 ? 20 : 0;
const spacing = Theme.spacingXS const spacing = Theme.spacingXS;
const padding = Theme.spacingM * 2 const padding = Theme.spacingM * 2;
return Math.max(120, Math.min(200, textWidth + closeButtonWidth + spacing + padding)) return Math.max(120, Math.min(200, textWidth + closeButtonWidth + spacing + padding));
} }
width: calculatedWidth width: calculatedWidth
@@ -85,11 +85,11 @@ Column {
StyledText { StyledText {
id: tabText id: tabText
text: { text: {
var prefix = "" var prefix = "";
if (hasUnsavedChangesForTab(modelData)) { if (hasUnsavedChangesForTab(modelData)) {
prefix = "● " prefix = "● ";
} }
return prefix + (modelData.title || "Untitled") return prefix + (modelData.title || "Untitled");
} }
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: isActive ? Theme.primary : Theme.surfaceText color: isActive ? Theme.primary : Theme.surfaceText
@@ -123,7 +123,7 @@ Column {
z: 100 z: 100
onClicked: { onClicked: {
root.tabClosed(index) root.tabClosed(index);
} }
} }
} }
@@ -150,4 +150,4 @@ Column {
onClicked: root.newTabRequested() onClicked: root.newTabRequested()
} }
} }
} }