From 419cd42b12879620cee26d5a0cf003a6f224d573 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 19 Oct 2025 13:06:15 -0400 Subject: [PATCH 1/7] Add a clear button, and optional icon to NTextInput --- Assets/Translations/de.json | 3 + Assets/Translations/en.json | 4 +- Assets/Translations/es.json | 3 + Assets/Translations/fr.json | 3 + Assets/Translations/pt.json | 3 + Assets/Translations/zh-CN.json | 3 + Widgets/NTextInput.qml | 153 ++++++++++++++++++++++----------- 7 files changed, 120 insertions(+), 52 deletions(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index a3318920..907757a3 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -927,6 +927,9 @@ }, "cancel": "Abbrechen", "apply": "Anwenden" + }, + "text-input": { + "clear": "Löschen" } }, "bar": { diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index cbad3dd8..000f36bc 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -910,6 +910,9 @@ }, "cancel": "Cancel", "apply": "Apply" + }, + "text-input": { + "clear": "Clear" } }, "bar": { @@ -1233,7 +1236,6 @@ "scan-again": "Scan again" } }, - "tooltips": { "refresh": "Refresh", "close": "Close", diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 2db6da8f..c95ed9da 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -903,6 +903,9 @@ }, "cancel": "Cancelar", "apply": "Aplicar" + }, + "text-input": { + "clear": "Borrar" } }, "bar": { diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 8131592a..b27af887 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -903,6 +903,9 @@ }, "cancel": "Annuler", "apply": "Appliquer" + }, + "text-input": { + "clear": "Effacer" } }, "bar": { diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index dd3fe7f0..52bab731 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -903,6 +903,9 @@ }, "cancel": "Cancelar", "apply": "Aplicar" + }, + "text-input": { + "clear": "Limpar" } }, "bar": { diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 4738030b..c6d34fcd 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -903,6 +903,9 @@ }, "cancel": "取消", "apply": "应用" + }, + "text-input": { + "clear": "清除" } }, "bar": { diff --git a/Widgets/NTextInput.qml b/Widgets/NTextInput.qml index adb83cd3..784bd199 100644 --- a/Widgets/NTextInput.qml +++ b/Widgets/NTextInput.qml @@ -9,6 +9,7 @@ ColumnLayout { property string label: "" property string description: "" + property string inputIconName: "" property bool readOnly: false property bool enabled: true property color labelColor: Color.mOnSurface @@ -106,74 +107,124 @@ ColumnLayout { id: inputContainer anchors.fill: parent anchors.leftMargin: Style.marginM - anchors.rightMargin: Style.marginM + // anchors.rightMargin: Style.marginM + clip: true z: 1 - TextField { - id: input - + RowLayout { anchors.fill: parent - verticalAlignment: TextInput.AlignVCenter - echoMode: TextInput.Normal - readOnly: root.readOnly - enabled: root.enabled - color: Color.mOnSurface - placeholderTextColor: Qt.alpha(Color.mOnSurfaceVariant, 0.6) + NIcon { + id: inputIcon + icon: root.inputIconName - selectByMouse: true + visible: root.inputIconName !== "" + enabled: false - topPadding: 0 - bottomPadding: 0 - leftPadding: 0 - rightPadding: 0 + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.topMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.rightMargin: 0 + } - background: null + TextField { + id: input - font.family: root.fontFamily - font.pointSize: root.fontSize * Style.uiScaleRatio - font.weight: root.fontWeight + anchors.left: inputIcon.visible ? inputIcon.right : parent.left + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: clearButton.left + anchors.leftMargin: inputIcon.visible ? Style.marginS : 0 - onEditingFinished: root.editingFinished() + verticalAlignment: TextInput.AlignVCenter - // Override mouse handling to prevent propagation - MouseArea { - id: textFieldMouse - anchors.fill: parent - acceptedButtons: Qt.AllButtons - preventStealing: true - propagateComposedEvents: false - cursorShape: Qt.IBeamCursor + echoMode: TextInput.Normal + readOnly: root.readOnly + enabled: root.enabled + color: Color.mOnSurface + placeholderTextColor: Qt.alpha(Color.mOnSurfaceVariant, 0.6) - property int selectionStart: 0 + selectByMouse: true - onPressed: mouse => { - mouse.accepted = true - input.forceActiveFocus() - var pos = input.positionAt(mouse.x, mouse.y) - input.cursorPosition = pos - selectionStart = pos - } + topPadding: 0 + bottomPadding: 0 + leftPadding: 0 + rightPadding: 0 - onPositionChanged: mouse => { - if (mouse.buttons & Qt.LeftButton) { - mouse.accepted = true - var pos = input.positionAt(mouse.x, mouse.y) - input.select(selectionStart, pos) - } - } + background: null - onDoubleClicked: mouse => { - mouse.accepted = true - input.selectAll() - } + font.family: root.fontFamily + font.pointSize: root.fontSize * Style.uiScaleRatio + font.weight: root.fontWeight - onReleased: mouse => { + onEditingFinished: root.editingFinished() + + // Override mouse handling to prevent propagation + MouseArea { + id: textFieldMouse + anchors.fill: parent + acceptedButtons: Qt.AllButtons + preventStealing: true + propagateComposedEvents: false + cursorShape: Qt.IBeamCursor + + property int selectionStart: 0 + + onPressed: mouse => { mouse.accepted = true + input.forceActiveFocus() + var pos = input.positionAt(mouse.x, mouse.y) + input.cursorPosition = pos + selectionStart = pos } - onWheel: wheel => { - wheel.accepted = true - } + + onPositionChanged: mouse => { + if (mouse.buttons & Qt.LeftButton) { + mouse.accepted = true + var pos = input.positionAt(mouse.x, mouse.y) + input.select(selectionStart, pos) + } + } + + onDoubleClicked: mouse => { + mouse.accepted = true + input.selectAll() + } + + onReleased: mouse => { + mouse.accepted = true + } + onWheel: wheel => { + wheel.accepted = true + } + } + } + NIconButton { + id: clearButton + icon: "x" + tooltipText: I18n.tr("widgets.text-input.clear") + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.topMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.rightMargin: 0 + border.width: 0 + + colorBg: Color.transparent + colorBgHover: Color.transparent + colorFgHover: Color.mTertiary + + visible: input.text.length > 0 && !root.readOnly + enabled: input.text.length > 0 && !root.readOnly + + onClicked: { + input.clear() + input.forceActiveFocus() + } } } } From 75417c1fa595a46a0131fb978309a1e595b341ae Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 19 Oct 2025 13:16:49 -0400 Subject: [PATCH 2/7] Fixed warnings because I was using Anchors and Layout at the same time --- Widgets/NTextInput.qml | 102 +++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/Widgets/NTextInput.qml b/Widgets/NTextInput.qml index 784bd199..7d81ecc0 100644 --- a/Widgets/NTextInput.qml +++ b/Widgets/NTextInput.qml @@ -75,31 +75,31 @@ ColumnLayout { propagateComposedEvents: false onPressed: mouse => { - mouse.accepted = true - // Focus the input and position cursor - input.forceActiveFocus() - var inputPos = mapToItem(inputContainer, mouse.x, mouse.y) - if (inputPos.x >= 0 && inputPos.x <= inputContainer.width) { - var textPos = inputPos.x - Style.marginM - if (textPos >= 0 && textPos <= input.width) { - input.cursorPosition = input.positionAt(textPos, input.height / 2) - } - } - } + mouse.accepted = true + // Focus the input and position cursor + input.forceActiveFocus() + var inputPos = mapToItem(inputContainer, mouse.x, mouse.y) + if (inputPos.x >= 0 && inputPos.x <= inputContainer.width) { + var textPos = inputPos.x - Style.marginM + if (textPos >= 0 && textPos <= input.width) { + input.cursorPosition = input.positionAt(textPos, input.height / 2) + } + } + } onReleased: mouse => { - mouse.accepted = true - } + mouse.accepted = true + } onDoubleClicked: mouse => { - mouse.accepted = true - input.selectAll() - } + mouse.accepted = true + input.selectAll() + } onPositionChanged: mouse => { - mouse.accepted = true - } + mouse.accepted = true + } onWheel: wheel => { - wheel.accepted = true - } + wheel.accepted = true + } } // Container for the actual text field @@ -113,6 +113,7 @@ ColumnLayout { RowLayout { anchors.fill: parent + spacing: 0 NIcon { id: inputIcon @@ -121,22 +122,15 @@ ColumnLayout { visible: root.inputIconName !== "" enabled: false - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.topMargin: 0 - anchors.bottomMargin: 0 - anchors.leftMargin: 0 - anchors.rightMargin: 0 + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: visible ? Style.marginS : 0 } TextField { id: input - anchors.left: inputIcon.visible ? inputIcon.right : parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: clearButton.left - anchors.leftMargin: inputIcon.visible ? Style.marginS : 0 + Layout.fillWidth: true + Layout.fillHeight: true verticalAlignment: TextInput.AlignVCenter @@ -173,32 +167,32 @@ ColumnLayout { property int selectionStart: 0 onPressed: mouse => { - mouse.accepted = true - input.forceActiveFocus() - var pos = input.positionAt(mouse.x, mouse.y) - input.cursorPosition = pos - selectionStart = pos - } + mouse.accepted = true + input.forceActiveFocus() + var pos = input.positionAt(mouse.x, mouse.y) + input.cursorPosition = pos + selectionStart = pos + } onPositionChanged: mouse => { - if (mouse.buttons & Qt.LeftButton) { - mouse.accepted = true - var pos = input.positionAt(mouse.x, mouse.y) - input.select(selectionStart, pos) - } - } + if (mouse.buttons & Qt.LeftButton) { + mouse.accepted = true + var pos = input.positionAt(mouse.x, mouse.y) + input.select(selectionStart, pos) + } + } onDoubleClicked: mouse => { - mouse.accepted = true - input.selectAll() - } + mouse.accepted = true + input.selectAll() + } onReleased: mouse => { - mouse.accepted = true - } + mouse.accepted = true + } onWheel: wheel => { - wheel.accepted = true - } + wheel.accepted = true + } } } NIconButton { @@ -206,12 +200,8 @@ ColumnLayout { icon: "x" tooltipText: I18n.tr("widgets.text-input.clear") - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.topMargin: 0 - anchors.bottomMargin: 0 - anchors.leftMargin: 0 - anchors.rightMargin: 0 + Layout.alignment: Qt.AlignVCenter + border.width: 0 colorBg: Color.transparent From 87af2e86cc739fb310d11db6fc31c57ab4145004 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 19 Oct 2025 13:27:49 -0400 Subject: [PATCH 3/7] Add magnifying glass to NSearchableComboBox --- Widgets/NSearchableComboBox.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/Widgets/NSearchableComboBox.qml b/Widgets/NSearchableComboBox.qml index a9a578f4..7d662278 100644 --- a/Widgets/NSearchableComboBox.qml +++ b/Widgets/NSearchableComboBox.qml @@ -173,6 +173,7 @@ RowLayout { // Search input NTextInput { id: searchInput + inputIconName: "search" Layout.fillWidth: true placeholderText: root.searchPlaceholder text: root.searchText From 6bfd93f7cc7fa59ae39ecfec5f3ed8de59fff07a Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 19 Oct 2025 20:20:45 -0400 Subject: [PATCH 4/7] combine searchInput and LocationInput elegantly in NFilePicker --- Widgets/NFilePicker.qml | 295 ++++++++++++++++++---------------------- 1 file changed, 134 insertions(+), 161 deletions(-) diff --git a/Widgets/NFilePicker.qml b/Widgets/NFilePicker.qml index cc318f02..261f31be 100644 --- a/Widgets/NFilePicker.qml +++ b/Widgets/NFilePicker.qml @@ -39,8 +39,8 @@ Popup { function openFilePicker() { if (!root.currentPath) root.currentPath = root.initialPath - shouldResetSelection = true - open() + shouldResetSelection = true + open() } function getFileIcon(fileName) { @@ -92,18 +92,18 @@ Popup { function formatFileSize(bytes) { if (bytes === 0) return "0 B" - const k = 1024, sizes = ["B", "KB", "MB", "GB", "TB"] - const i = Math.floor(Math.log(bytes) / Math.log(k)) - return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i] + const k = 1024, sizes = ["B", "KB", "MB", "GB", "TB"] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i] } function confirmSelection() { if (filePickerPanel.currentSelection.length === 0) return - root.selectedPaths = filePickerPanel.currentSelection - root.accepted(filePickerPanel.currentSelection) - root.close() + root.selectedPaths = filePickerPanel.currentSelection + root.accepted(filePickerPanel.currentSelection) + root.close() } function updateFilteredModel() { @@ -126,14 +126,14 @@ Popup { if (root.selectionMode === "folders" && !fileIsDir) continue - if (searchText === "" || fileName.toLowerCase().includes(searchText)) { - filteredModel.append({ - "fileName": fileName, - "filePath": filePath, - "fileIsDir": fileIsDir, - "fileSize": fileSize - }) - } + if (searchText === "" || fileName.toLowerCase().includes(searchText)) { + filteredModel.append({ + "fileName": fileName, + "filePath": filePath, + "fileIsDir": fileIsDir, + "fileSize": fileSize + }) + } } } @@ -165,19 +165,19 @@ Popup { focus: true Keys.onPressed: event => { - if (event.modifiers & Qt.ControlModifier && event.key === Qt.Key_F) { - filePickerPanel.showSearchBar = !filePickerPanel.showSearchBar - if (filePickerPanel.showSearchBar) - Qt.callLater(() => searchInput.forceActiveFocus()) - event.accepted = true - } else if (event.key === Qt.Key_Escape && filePickerPanel.showSearchBar) { - filePickerPanel.showSearchBar = false - filePickerPanel.searchText = "" - filePickerPanel.filterText = "" - root.updateFilteredModel() - event.accepted = true - } - } + if (event.modifiers & Qt.ControlModifier && event.key === Qt.Key_F) { + filePickerPanel.showSearchBar = !filePickerPanel.showSearchBar + if (filePickerPanel.showSearchBar) + Qt.callLater(() => searchInput.forceActiveFocus()) + event.accepted = true + } else if (event.key === Qt.Key_Escape && filePickerPanel.showSearchBar) { + filePickerPanel.showSearchBar = false + filePickerPanel.searchText = "" + filePickerPanel.filterText = "" + root.updateFilteredModel() + event.accepted = true + } + } ColumnLayout { anchors.fill: parent @@ -296,6 +296,10 @@ Popup { text: root.currentPath placeholderText: "Enter path..." Layout.fillWidth: true + + visible: !filePickerPanel.showSearchBar + enabled: !filePickerPanel.showSearchBar + onEditingFinished: { const newPath = text.trim() if (newPath !== "" && newPath !== root.currentPath) { @@ -314,6 +318,30 @@ Popup { } } + // Search bar + NTextInput { + id: searchInput + inputIconName: "search" + placeholderText: I18n.tr("widget.file-picker.search-placeholder") + Layout.fillWidth: true + + visible: filePickerPanel.showSearchBar + enabled: filePickerPanel.showSearchBar + + text: filePickerPanel.searchText + onTextChanged: { + filePickerPanel.searchText = text + filePickerPanel.filterText = text + root.updateFilteredModel() + } + Keys.onEscapePressed: { + filePickerPanel.showSearchBar = false + filePickerPanel.searchText = "" + filePickerPanel.filterText = "" + root.updateFilteredModel() + } + } + NIconButton { icon: filePickerPanel.viewMode ? "filepicker-list" : "filepicker-layout-grid" tooltipText: filePickerPanel.viewMode ? "List View" : "Grid View" @@ -336,61 +364,6 @@ Popup { } } - // Search bar - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 45 - color: Color.mSurfaceVariant - radius: Style.radiusS - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS) - visible: filePickerPanel.showSearchBar - - RowLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Style.marginS - anchors.rightMargin: Style.marginS - spacing: Style.marginS - - NIcon { - icon: "filepicker-search" - color: Color.mOnSurfaceVariant - pointSize: Style.fontSizeS - } - NTextInput { - id: searchInput - placeholderText: I18n.tr("widget.file-picker.search-placeholder") - Layout.fillWidth: true - text: filePickerPanel.searchText - onTextChanged: { - filePickerPanel.searchText = text - filePickerPanel.filterText = text - root.updateFilteredModel() - } - Keys.onEscapePressed: { - filePickerPanel.showSearchBar = false - filePickerPanel.searchText = "" - filePickerPanel.filterText = "" - root.updateFilteredModel() - } - } - NIconButton { - icon: "filepicker-x" - tooltipText: I18n.tr("tooltips.clear") - baseSize: Style.baseWidgetSize * 0.6 - visible: filePickerPanel.searchText.length > 0 - onClicked: { - searchInput.text = "" - filePickerPanel.searchText = "" - filePickerPanel.filterText = "" - root.updateFilteredModel() - } - } - } - } - // File list area Rectangle { Layout.fillWidth: true @@ -500,11 +473,11 @@ Popup { bottomMargin: Style.marginS ScrollBar.vertical: scrollBarComponent.createObject(gridView, { - "parent": gridView, - "x": gridView.mirrored ? 0 : gridView.width - width, - "y": 0, - "height": gridView.height - }) + "parent": gridView, + "x": gridView.mirrored ? 0 : gridView.width - width, + "y": 0, + "height": gridView.height + }) delegate: Rectangle { id: gridItem @@ -560,8 +533,8 @@ Popup { property bool isImage: { if (model.fileIsDir) return false - const ext = model.fileName.split('.').pop().toLowerCase() - return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'].includes(ext) + const ext = model.fileName.split('.').pop().toLowerCase() + return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'].includes(ext) } Image { @@ -601,10 +574,10 @@ Popup { color: { if (isSelected) return Color.mSecondary - else if (mouseArea.containsMouse) - return model.fileIsDir ? Color.mOnTertiary : Color.mOnTertiary - else - return model.fileIsDir ? Color.mPrimary : Color.mOnSurfaceVariant + else if (mouseArea.containsMouse) + return model.fileIsDir ? Color.mOnTertiary : Color.mOnTertiary + else + return model.fileIsDir ? Color.mPrimary : Color.mOnSurfaceVariant } anchors.centerIn: parent visible: !iconContainer.isImage || thumbnail.status !== Image.Ready @@ -635,10 +608,10 @@ Popup { color: { if (isSelected) return Color.mSecondary - else if (mouseArea.containsMouse) - return Color.mOnTertiary - else - return Color.mOnSurfaceVariant + else if (mouseArea.containsMouse) + return Color.mOnTertiary + else + return Color.mOnSurfaceVariant } pointSize: Style.fontSizeS font.weight: isSelected ? Style.fontWeightBold : Style.fontWeightRegular @@ -657,37 +630,37 @@ Popup { acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: mouse => { - if (mouse.button === Qt.LeftButton) { - if (model.fileIsDir) { - // In folder mode, single click selects the folder - if (root.selectionMode === "folders") { - filePickerPanel.currentSelection = [model.filePath] - } - // In file mode, single click on folder does nothing (must double-click to enter) - } else { - // Single click on file selects it (only in file mode) - if (root.selectionMode === "files") { - filePickerPanel.currentSelection = [model.filePath] - } - } - } - } + if (mouse.button === Qt.LeftButton) { + if (model.fileIsDir) { + // In folder mode, single click selects the folder + if (root.selectionMode === "folders") { + filePickerPanel.currentSelection = [model.filePath] + } + // In file mode, single click on folder does nothing (must double-click to enter) + } else { + // Single click on file selects it (only in file mode) + if (root.selectionMode === "files") { + filePickerPanel.currentSelection = [model.filePath] + } + } + } + } onDoubleClicked: mouse => { - if (mouse.button === Qt.LeftButton) { - if (model.fileIsDir) { - // Double-click on folder always navigates into it - folderModel.folder = "file://" + model.filePath - root.currentPath = model.filePath - } else { - // Double-click on file selects and confirms (only in file mode) - if (root.selectionMode === "files") { - filePickerPanel.currentSelection = [model.filePath] - root.confirmSelection() - } - } - } - } + if (mouse.button === Qt.LeftButton) { + if (model.fileIsDir) { + // Double-click on folder always navigates into it + folderModel.folder = "file://" + model.filePath + root.currentPath = model.filePath + } else { + // Double-click on file selects and confirms (only in file mode) + if (root.selectionMode === "files") { + filePickerPanel.currentSelection = [model.filePath] + root.confirmSelection() + } + } + } + } } } } @@ -707,9 +680,9 @@ Popup { color: { if (filePickerPanel.currentSelection.includes(model.filePath)) return Color.mSecondary - if (mouseArea.containsMouse) - return Color.mTertiary - return Color.transparent + if (mouseArea.containsMouse) + return Color.mTertiary + return Color.transparent } radius: Style.radiusS Behavior on color { @@ -755,37 +728,37 @@ Popup { acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: mouse => { - if (mouse.button === Qt.LeftButton) { - if (model.fileIsDir) { - // In folder mode, single click selects the folder - if (root.selectionMode === "folders") { - filePickerPanel.currentSelection = [model.filePath] - } - // In file mode, single click on folder does nothing (must double-click to enter) - } else { - // Single click on file selects it (only in file mode) - if (root.selectionMode === "files") { - filePickerPanel.currentSelection = [model.filePath] - } - } - } - } + if (mouse.button === Qt.LeftButton) { + if (model.fileIsDir) { + // In folder mode, single click selects the folder + if (root.selectionMode === "folders") { + filePickerPanel.currentSelection = [model.filePath] + } + // In file mode, single click on folder does nothing (must double-click to enter) + } else { + // Single click on file selects it (only in file mode) + if (root.selectionMode === "files") { + filePickerPanel.currentSelection = [model.filePath] + } + } + } + } onDoubleClicked: mouse => { - if (mouse.button === Qt.LeftButton) { - if (model.fileIsDir) { - // Double-click on folder always navigates into it - folderModel.folder = "file://" + model.filePath - root.currentPath = model.filePath - } else { - // Double-click on file selects and confirms (only in file mode) - if (root.selectionMode === "files") { - filePickerPanel.currentSelection = [model.filePath] - root.confirmSelection() - } - } - } - } + if (mouse.button === Qt.LeftButton) { + if (model.fileIsDir) { + // Double-click on folder always navigates into it + folderModel.folder = "file://" + model.filePath + root.currentPath = model.filePath + } else { + // Double-click on file selects and confirms (only in file mode) + if (root.selectionMode === "files") { + filePickerPanel.currentSelection = [model.filePath] + root.confirmSelection() + } + } + } + } } } } @@ -843,7 +816,7 @@ Popup { Component.onCompleted: { if (!root.currentPath) root.currentPath = root.initialPath - folderModel.folder = "file://" + root.currentPath + folderModel.folder = "file://" + root.currentPath } } } From 25c26a63340c16a289a3541264783dcbc979efee Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 19 Oct 2025 23:46:21 -0400 Subject: [PATCH 5/7] Small color tweak to make clear button less intrusive --- Widgets/NTextInput.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/Widgets/NTextInput.qml b/Widgets/NTextInput.qml index 7d81ecc0..c3fe1c9d 100644 --- a/Widgets/NTextInput.qml +++ b/Widgets/NTextInput.qml @@ -206,6 +206,7 @@ ColumnLayout { colorBg: Color.transparent colorBgHover: Color.transparent + colorFg: Color.mOnSurface colorFgHover: Color.mTertiary visible: input.text.length > 0 && !root.readOnly From 53a8706f603fbb24663d80d7362ea523584ac0bf Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sun, 19 Oct 2025 23:52:27 -0400 Subject: [PATCH 6/7] Another small color tweak --- Widgets/NTextInput.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Widgets/NTextInput.qml b/Widgets/NTextInput.qml index c3fe1c9d..6043a51d 100644 --- a/Widgets/NTextInput.qml +++ b/Widgets/NTextInput.qml @@ -207,7 +207,7 @@ ColumnLayout { colorBg: Color.transparent colorBgHover: Color.transparent colorFg: Color.mOnSurface - colorFgHover: Color.mTertiary + colorFgHover: Color.mError visible: input.text.length > 0 && !root.readOnly enabled: input.text.length > 0 && !root.readOnly From d6958aca9ea4864348626221a994a3f0c0ed9e79 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Mon, 20 Oct 2025 00:16:05 -0400 Subject: [PATCH 7/7] Used Il8n Search placeholder for search box in nFilePicker --- Widgets/NFilePicker.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Widgets/NFilePicker.qml b/Widgets/NFilePicker.qml index 261f31be..b9bd7863 100644 --- a/Widgets/NFilePicker.qml +++ b/Widgets/NFilePicker.qml @@ -322,7 +322,7 @@ Popup { NTextInput { id: searchInput inputIconName: "search" - placeholderText: I18n.tr("widget.file-picker.search-placeholder") + placeholderText: I18n.tr("placeholders.search") Layout.fillWidth: true visible: filePickerPanel.showSearchBar