1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-08 06:25:37 -05:00

systematic cleanups and qmlfmt file browser modal

This commit is contained in:
bbedward
2025-09-03 00:00:25 -04:00
parent 93da303967
commit ae6a1b77c2
13 changed files with 354 additions and 475 deletions

View File

@@ -34,8 +34,7 @@ Item {
clearConfirmDialog.show("Clear All History?", "This will permanently delete all clipboard history.", function () {
modal.clearAll()
modal.hide()
}, function () {} // No action on cancel
)
}, function () {})
}
onCloseClicked: modal.hide()
}

View File

@@ -7,7 +7,7 @@ import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modals.Clipboard
import qs.Modals
DankModal {
id: clipboardHistoryModal
@@ -170,6 +170,7 @@ DankModal {
property alias filteredClipboardModel: filteredClipboardModel
property alias clipboardModel: clipboardModel
property var confirmDialog: clearConfirmDialog
ListModel {
id: clipboardModel
@@ -209,7 +210,7 @@ DankModal {
ClipboardContent {
modal: clipboardHistoryModal
filteredModel: filteredClipboardModel
clearConfirmDialog: clipboardHistoryModal.clearConfirmDialog
clearConfirmDialog: clipboardHistoryModal.confirmDialog
}
}
}

View File

@@ -1,9 +1,5 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
DankModal {
@@ -16,8 +12,7 @@ DankModal {
property color confirmButtonColor: Theme.primary
property var onConfirm: function () {}
property var onCancel: function () {}
property int selectedButton: -1 // -1 = none, 0 = Cancel, 1 = Confirm
property int selectedButton: -1
property bool keyboardNavigation: false
function show(title, message, onConfirmCallback, onCancelCallback) {
@@ -26,8 +21,8 @@ DankModal {
confirmButtonText = "Confirm"
cancelButtonText = "Cancel"
confirmButtonColor = Theme.primary
onConfirm = onConfirmCallback || function() {}
onCancel = onCancelCallback || function() {}
onConfirm = onConfirmCallback || (() => {})
onCancel = onCancelCallback || (() => {})
selectedButton = -1
keyboardNavigation = false
open()
@@ -39,20 +34,23 @@ DankModal {
confirmButtonText = options.confirmText || "Confirm"
cancelButtonText = options.cancelText || "Cancel"
confirmButtonColor = options.confirmColor || Theme.primary
onConfirm = options.onConfirm || function() {}
onCancel = options.onCancel || function() {}
onConfirm = options.onConfirm || (() => {})
onCancel = options.onCancel || (() => {})
selectedButton = -1
keyboardNavigation = false
open()
}
function selectButton() {
close()
if (selectedButton === 0) {
close()
if (onCancel) onCancel()
if (onCancel) {
onCancel()
}
} else {
close()
if (onConfirm) onConfirm()
if (onConfirm) {
onConfirm()
}
}
}
@@ -64,7 +62,9 @@ DankModal {
shouldHaveFocus: true
onBackgroundClicked: {
close()
if (onCancel) onCancel()
if (onCancel) {
onCancel()
}
}
onOpened: {
modalFocusScope.forceActiveFocus()
@@ -75,7 +75,9 @@ DankModal {
switch (event.key) {
case Qt.Key_Escape:
close()
if (onCancel) onCancel()
if (onCancel) {
onCancel()
}
event.accepted = true
break
case Qt.Key_Left:
@@ -148,13 +150,14 @@ DankModal {
height: 40
radius: Theme.cornerRadius
color: {
if (keyboardNavigation && selectedButton === 0)
if (keyboardNavigation && selectedButton === 0) {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
else if (cancelButton.containsMouse)
} else if (cancelButton.containsMouse) {
return Theme.surfacePressed
else
} else {
return Theme.surfaceVariantAlpha
}
}
border.color: (keyboardNavigation && selectedButton === 0) ? Theme.primary : "transparent"
border.width: (keyboardNavigation && selectedButton === 0) ? 1 : 0
@@ -177,7 +180,6 @@ DankModal {
selectButton()
}
}
}
Rectangle {
@@ -185,14 +187,15 @@ DankModal {
height: 40
radius: Theme.cornerRadius
color: {
let baseColor = confirmButtonColor
if (keyboardNavigation && selectedButton === 1)
const baseColor = confirmButtonColor
if (keyboardNavigation && selectedButton === 1) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 1)
else if (confirmButton.containsMouse)
} else if (confirmButton.containsMouse) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9)
else
} else {
return baseColor
}
}
border.color: (keyboardNavigation && selectedButton === 1) ? "white" : "transparent"
border.width: (keyboardNavigation && selectedButton === 1) ? 1 : 0
@@ -215,15 +218,9 @@ DankModal {
selectButton()
}
}
}
}
}
}
}
}

View File

@@ -27,7 +27,6 @@ PanelWindow {
property real borderWidth: 1
property real cornerRadius: Theme.cornerRadius
property bool enableShadow: false
// Expose the focusScope for external access
property alias modalFocusScope: focusScope
property bool shouldBeVisible: false
property bool shouldHaveFocus: shouldBeVisible
@@ -38,15 +37,6 @@ PanelWindow {
signal dialogClosed
signal backgroundClicked
Connections {
target: ModalManager
function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
close()
}
}
}
function open() {
ModalManager.openModal(root)
closeTimer.stop()
@@ -61,11 +51,12 @@ PanelWindow {
}
function toggle() {
if (shouldBeVisible)
if (shouldBeVisible) {
close()
else
} else {
open()
}
}
visible: shouldBeVisible
color: "transparent"
@@ -84,6 +75,16 @@ PanelWindow {
}
}
Connections {
function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
close()
}
}
target: ModalManager
}
Timer {
id: closeTimer
@@ -112,15 +113,12 @@ PanelWindow {
anchors.fill: parent
enabled: root.closeOnBackgroundClick
onClicked: mouse => {
var localPos = mapToItem(contentContainer,
mouse.x, mouse.y)
if (localPos.x < 0
|| localPos.x > contentContainer.width
|| localPos.y < 0
|| localPos.y > contentContainer.height)
const localPos = mapToItem(contentContainer, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) {
root.backgroundClicked()
}
}
}
Behavior on opacity {
NumberAnimation {
@@ -137,19 +135,20 @@ PanelWindow {
height: root.height
anchors.centerIn: positioning === "center" ? parent : undefined
x: {
if (positioning === "top-right")
return Math.max(Theme.spacingL,
root.screenWidth - width - Theme.spacingL)
else if (positioning === "custom")
if (positioning === "top-right") {
return Math.max(Theme.spacingL, root.screenWidth - width - Theme.spacingL)
} else if (positioning === "custom") {
return root.customPosition.x
return 0 // Will be overridden by anchors.centerIn when positioning === "center"
}
return 0
}
y: {
if (positioning === "top-right")
if (positioning === "top-right") {
return Theme.barHeight + Theme.spacingXS
else if (positioning === "custom")
} else if (positioning === "custom") {
return root.customPosition.y
return 0 // Will be overridden by anchors.centerIn when positioning === "center"
}
return 0
}
color: root.backgroundColor
radius: root.cornerRadius
@@ -157,12 +156,7 @@ PanelWindow {
border.width: root.borderWidth
layer.enabled: root.enableShadow
opacity: root.shouldBeVisible ? 1 : 0
scale: {
if (root.animationType === "scale")
return root.shouldBeVisible ? 1 : 0.9
return 1
}
scale: root.animationType === "scale" ? (root.shouldBeVisible ? 1 : 0.9) : 1
transform: root.animationType === "slide" ? slideTransform : null
Translate {
@@ -214,28 +208,25 @@ PanelWindow {
visible: root.visible // Only active when the modal is visible
focus: root.visible
Keys.onEscapePressed: event => {
if (root.closeOnEscapeKey
&& shouldHaveFocus) {
if (root.closeOnEscapeKey && shouldHaveFocus) {
root.close()
event.accepted = true
}
}
onVisibleChanged: {
if (visible && shouldHaveFocus)
Qt.callLater(function () {
focusScope.forceActiveFocus()
})
if (visible && shouldHaveFocus) {
Qt.callLater(() => focusScope.forceActiveFocus())
}
}
Connections {
target: root
function onShouldHaveFocusChanged() {
if (shouldHaveFocus && visible) {
Qt.callLater(function () {
focusScope.forceActiveFocus()
})
}
}
Qt.callLater(() => focusScope.forceActiveFocus())
}
}
target: root
}
}
}

View File

@@ -1,20 +1,16 @@
import Qt.labs.folderlistmodel
import QtCore
import QtQuick
import QtQuick.Controls
import QtCore
import Qt.labs.folderlistmodel
import Quickshell.Io
import qs.Common
import qs.Modals
import qs.Widgets
DankModal {
id: fileBrowserModal
objectName: "fileBrowserModal"
allowStacking: true
signal fileSelected(string path)
property string homeDir: StandardPaths.writableLocation(
StandardPaths.HomeLocation)
property string homeDir: StandardPaths.writableLocation(StandardPaths.HomeLocation)
property string currentPath: ""
property var fileExtensions: ["*.*"]
property alias filterExtensions: fileBrowserModal.fileExtensions
@@ -27,37 +23,27 @@ DankModal {
property bool backButtonFocused: false
property bool saveMode: false // Enable save functionality
property string defaultFileName: "" // Default filename for save mode
property int keyboardSelectionIndex: -1
property bool keyboardSelectionRequested: false
property bool showKeyboardHints: false
property bool showFileInfo: false
property string selectedFilePath: ""
property string selectedFileName: ""
property bool selectedFileIsDir: false
FolderListModel {
id: folderModel
showDirsFirst: true
showDotAndDotDot: false
showHidden: fileBrowserModal.showHiddenFiles
nameFilters: fileExtensions
showFiles: true
showDirs: true
folder: currentPath ? "file://" + currentPath : "file://" + homeDir
}
signal fileSelected(string path)
function isImageFile(fileName) {
if (!fileName)
if (!fileName) {
return false
var ext = fileName.toLowerCase().split('.').pop()
}
const ext = fileName.toLowerCase().split('.').pop()
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(ext)
}
function getLastPath() {
var lastPath = ""
if (browserType === "wallpaper") {
lastPath = SessionData.wallpaperLastPath
} else if (browserType === "profile") {
lastPath = SessionData.profileLastPath
}
if (lastPath && lastPath !== "") {
return lastPath
}
return homeDir
const lastPath = browserType === "wallpaper" ? SessionData.wallpaperLastPath : browserType === "profile" ? SessionData.profileLastPath : ""
return (lastPath && lastPath !== "") ? lastPath : homeDir
}
function saveLastPath(path) {
@@ -68,64 +54,103 @@ DankModal {
}
}
Component.onCompleted: {
currentPath = getLastPath()
}
width: 800
height: 600
enableShadow: true
visible: false
onBackgroundClicked: close()
onOpened: {
modalFocusScope.forceActiveFocus()
}
modalFocusScope.Keys.onPressed: function(event) {
keyboardController.handleKey(event)
}
onVisibleChanged: {
if (visible) {
var startPath = getLastPath()
currentPath = startPath
selectedIndex = -1
keyboardNavigationActive = false
backButtonFocused = false
}
}
onCurrentPathChanged: {
selectedFilePath = ""
selectedFileName = ""
selectedFileIsDir = false
}
onSelectedIndexChanged: {
// Update selected file data when index changes
if (selectedIndex >= 0 && folderModel && selectedIndex < folderModel.count) {
// We need to get the file data from the model for this index
// This is a bit tricky with FolderListModel, so we'll use a different approach
selectedFilePath = ""
selectedFileName = ""
selectedFileIsDir = false
}
}
// Function to update file data from delegates
function setSelectedFileData(path, name, isDir) {
selectedFilePath = path
selectedFileName = name
selectedFileIsDir = isDir
}
function navigateUp() {
const path = currentPath
if (path === homeDir)
return
const lastSlash = path.lastIndexOf('/')
if (lastSlash > 0) {
const newPath = path.substring(0, lastSlash)
if (newPath.length < homeDir.length) {
currentPath = homeDir
saveLastPath(homeDir)
} else {
currentPath = newPath
saveLastPath(newPath)
}
}
}
function navigateTo(path) {
currentPath = path
saveLastPath(path)
selectedIndex = -1
backButtonFocused = false
}
function keyboardFileSelection(index) {
if (index >= 0) {
keyboardSelectionTimer.targetIndex = index
keyboardSelectionTimer.start()
}
}
function executeKeyboardSelection(index) {
keyboardSelectionIndex = index
keyboardSelectionRequested = true
}
objectName: "fileBrowserModal"
allowStacking: true
Component.onCompleted: {
currentPath = getLastPath()
}
width: 800
height: 600
enableShadow: true
visible: false
onBackgroundClicked: close()
onOpened: {
modalFocusScope.forceActiveFocus()
}
modalFocusScope.Keys.onPressed: function (event) {
keyboardController.handleKey(event)
}
onVisibleChanged: {
if (visible) {
currentPath = getLastPath()
selectedIndex = -1
keyboardNavigationActive = false
backButtonFocused = false
}
}
onCurrentPathChanged: {
selectedFilePath = ""
selectedFileName = ""
selectedFileIsDir = false
}
onSelectedIndexChanged: {
if (selectedIndex >= 0 && folderModel && selectedIndex < folderModel.count) {
selectedFilePath = ""
selectedFileName = ""
selectedFileIsDir = false
}
}
FolderListModel {
id: folderModel
showDirsFirst: true
showDotAndDotDot: false
showHidden: fileBrowserModal.showHiddenFiles
nameFilters: fileExtensions
showFiles: true
showDirs: true
folder: currentPath ? "file://" + currentPath : "file://" + homeDir
}
QtObject {
id: keyboardController
property int totalItems: folderModel.count
property int gridColumns: 5 // Fixed number of columns for the grid (matches actual display)
property int gridColumns: 5
function handleKey(event) {
if (event.key === Qt.Key_Escape) {
@@ -133,21 +158,18 @@ DankModal {
event.accepted = true
return
}
// F10 toggles keyboard hints
if (event.key === Qt.Key_F10) {
showKeyboardHints = !showKeyboardHints
event.accepted = true
return
}
// F1 or I key for file information
if (event.key === Qt.Key_F1 || event.key === Qt.Key_I) {
showFileInfo = !showFileInfo
event.accepted = true
return
}
// Alt+Left or Backspace to go back
if ((event.modifiers & Qt.AltModifier && event.key === Qt.Key_Left) || event.key === Qt.Key_Backspace) {
if (currentPath !== homeDir) {
@@ -156,7 +178,6 @@ DankModal {
}
return
}
if (!keyboardNavigationActive) {
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Down || event.key === Qt.Key_Right) {
keyboardNavigationActive = true
@@ -171,7 +192,6 @@ DankModal {
}
return
}
switch (event.key) {
case Qt.Key_Tab:
if (backButtonFocused) {
@@ -187,7 +207,6 @@ DankModal {
}
event.accepted = true
break
case Qt.Key_Backtab:
if (backButtonFocused) {
backButtonFocused = false
@@ -202,45 +221,36 @@ DankModal {
}
event.accepted = true
break
case Qt.Key_Left:
if (backButtonFocused) {
if (backButtonFocused)
return
}
if (selectedIndex > 0) {
selectedIndex--
// Update file info for navigation
updateFileInfoForIndex(selectedIndex)
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
}
event.accepted = true
break
case Qt.Key_Right:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
updateFileInfoForIndex(selectedIndex)
} else if (selectedIndex < totalItems - 1) {
selectedIndex++
updateFileInfoForIndex(selectedIndex)
}
event.accepted = true
break
case Qt.Key_Up:
if (backButtonFocused) {
backButtonFocused = false
// Go to first row, appropriate column
var col = selectedIndex % gridColumns
selectedIndex = Math.min(col, totalItems - 1)
updateFileInfoForIndex(selectedIndex)
} else if (selectedIndex >= gridColumns) {
// Move up one row
selectedIndex -= gridColumns
updateFileInfoForIndex(selectedIndex)
} else if (currentPath !== homeDir) {
// At top row, go to back button
backButtonFocused = true
@@ -248,18 +258,15 @@ DankModal {
}
event.accepted = true
break
case Qt.Key_Down:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
updateFileInfoForIndex(selectedIndex)
} else {
// Move down one row if possible
var newIndex = selectedIndex + gridColumns
if (newIndex < totalItems) {
selectedIndex = newIndex
updateFileInfoForIndex(selectedIndex)
} else {
// If can't go down a full row, go to last item in the column if exists
var lastRowStart = Math.floor((totalItems - 1) / gridColumns) * gridColumns
@@ -267,83 +274,31 @@ DankModal {
var targetIndex = lastRowStart + col
if (targetIndex < totalItems && targetIndex > selectedIndex) {
selectedIndex = targetIndex
updateFileInfoForIndex(selectedIndex)
}
}
}
event.accepted = true
break
case Qt.Key_Return:
case Qt.Key_Enter:
case Qt.Key_Space:
if (backButtonFocused) {
if (backButtonFocused)
navigateUp()
} else if (selectedIndex >= 0 && selectedIndex < totalItems) {
else if (selectedIndex >= 0 && selectedIndex < totalItems)
// Trigger selection by setting the grid's current index and using signal
fileBrowserModal.keyboardFileSelection(selectedIndex)
}
event.accepted = true
break
}
// Scroll handling is done in the grid's onCurrentIndexChanged
}
}
function navigateUp() {
var path = currentPath
if (path === homeDir) {
return
}
var lastSlash = path.lastIndexOf('/')
if (lastSlash > 0) {
var newPath = path.substring(0, lastSlash)
if (newPath.length < homeDir.length) {
currentPath = homeDir
saveLastPath(homeDir)
} else {
currentPath = newPath
saveLastPath(newPath)
}
}
}
function navigateTo(path) {
currentPath = path
saveLastPath(path) // Save the path when navigating
selectedIndex = -1
backButtonFocused = false
}
function keyboardFileSelection(index) {
if (index >= 0) {
keyboardSelectionTimer.targetIndex = index
keyboardSelectionTimer.start()
}
}
function updateSelectedFileInfo(index) {
// This will be called when we need to update file info for the selected index
// The delegate will handle the actual file info updates
}
function updateFileInfoForIndex(index) {
// We can't directly access FolderListModel data by index from here
// Instead, we'll rely on the delegate's Component.onCompleted and mouse clicks
// to call setSelectedFileData() with the proper file information
// For keyboard navigation, we need a different approach
// The selectedIndex change will trigger delegate updates
}
Timer {
id: keyboardSelectionTimer
interval: 1
property int targetIndex: -1
interval: 1
onTriggered: {
// Access the currently selected item through model role names
// This will work because QML models expose role data
@@ -351,23 +306,6 @@ DankModal {
}
}
function executeKeyboardSelection(index) {
// This is a simplified version that just needs to work
// We'll handle this in the mouse area of each delegate
// For now, signal that keyboard selection was requested
keyboardSelectionIndex = index
keyboardSelectionRequested = true
}
property int keyboardSelectionIndex: -1
property bool keyboardSelectionRequested: false
property bool showKeyboardHints: false
property bool showFileInfo: false
property string selectedFilePath: ""
property string selectedFileName: ""
property bool selectedFileIsDir: false
content: Component {
Item {
anchors.fill: parent
@@ -434,9 +372,8 @@ DankModal {
width: 32
height: 32
radius: Theme.cornerRadius
color: (backButtonMouseArea.containsMouse || (backButtonFocused && keyboardNavigationActive))
&& currentPath !== homeDir ? Theme.surfaceVariant : "transparent"
opacity: currentPath !== homeDir ? 1.0 : 0.0
color: (backButtonMouseArea.containsMouse || (backButtonFocused && keyboardNavigationActive)) && currentPath !== homeDir ? Theme.surfaceVariant : "transparent"
opacity: currentPath !== homeDir ? 1 : 0
DankIcon {
anchors.centerIn: parent
@@ -447,10 +384,10 @@ DankModal {
MouseArea {
id: backButtonMouseArea
anchors.fill: parent
hoverEnabled: currentPath !== homeDir
cursorShape: currentPath
!== homeDir ? Qt.PointingHandCursor : Qt.ArrowCursor
cursorShape: currentPath !== homeDir ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: currentPath !== homeDir
onClicked: navigateUp()
}
@@ -478,19 +415,17 @@ DankModal {
cellWidth: 150
cellHeight: 130
cacheBuffer: 260
model: folderModel
currentIndex: selectedIndex
onCurrentIndexChanged: {
if (keyboardNavigationActive && currentIndex >= 0) {
if (keyboardNavigationActive && currentIndex >= 0)
positionViewAtIndex(currentIndex, GridView.Contain)
}
}
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
}
ScrollBar.horizontal: ScrollBar {
policy: ScrollBar.AlwaysOff
}
@@ -508,29 +443,27 @@ DankModal {
height: 120
radius: Theme.cornerRadius
color: {
if (keyboardNavigationActive && delegateRoot.index === selectedIndex) {
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
return Theme.surfacePressed
}
return mouseArea.containsMouse ? Theme.surfaceVariant : "transparent"
}
border.color: keyboardNavigationActive && delegateRoot.index === selectedIndex ? Theme.primary : Theme.outline
border.width: (mouseArea.containsMouse || (keyboardNavigationActive && delegateRoot.index === selectedIndex)) ? 1 : 0
// Update file info when this item gets selected via keyboard or initially
Component.onCompleted: {
if (keyboardNavigationActive && delegateRoot.index === selectedIndex) {
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
}
}
// Watch for selectedIndex changes to update file info during keyboard navigation
Connections {
target: fileBrowserModal
function onSelectedIndexChanged() {
if (keyboardNavigationActive && selectedIndex === delegateRoot.index) {
if (keyboardNavigationActive && selectedIndex === delegateRoot.index)
setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
}
}
target: fileBrowserModal
}
Column {
@@ -555,8 +488,7 @@ DankModal {
name: "description"
size: Theme.iconSizeLarge
color: Theme.primary
visible: !delegateRoot.fileIsDir
&& !isImageFile(delegateRoot.fileName)
visible: !delegateRoot.fileIsDir && !isImageFile(delegateRoot.fileName)
}
DankIcon {
@@ -583,15 +515,14 @@ DankModal {
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
// Update selected file info and index first
selectedIndex = delegateRoot.index
setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
if (delegateRoot.fileIsDir) {
navigateTo(delegateRoot.filePath)
} else {
@@ -603,15 +534,11 @@ DankModal {
// Handle keyboard selection
Connections {
target: fileBrowserModal
function onKeyboardSelectionRequestedChanged() {
if (fileBrowserModal.keyboardSelectionRequested && fileBrowserModal.keyboardSelectionIndex === delegateRoot.index) {
// Reset the flag first
fileBrowserModal.keyboardSelectionRequested = false
// Update selected file info and index first
selectedIndex = delegateRoot.index
setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
// Trigger the same action as mouse click
if (delegateRoot.fileIsDir) {
navigateTo(delegateRoot.filePath)
} else {
@@ -620,15 +547,16 @@ DankModal {
}
}
}
}
target: fileBrowserModal
}
}
}
}
// Save functionality - positioned at bottom in save mode
Row {
id: saveRow
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -639,32 +567,31 @@ DankModal {
DankTextField {
id: fileNameInput
width: parent.width - saveButton.width - Theme.spacingM
height: 36
text: defaultFileName
placeholderText: "Enter filename..."
ignoreLeftRightKeys: false // Allow arrow key navigation
focus: saveMode // Auto-focus when in save mode
ignoreLeftRightKeys: false
focus: saveMode
Component.onCompleted: {
if (saveMode) {
if (saveMode)
Qt.callLater(() => {
forceActiveFocus();
});
forceActiveFocus()
})
}
}
onAccepted: {
if (text.trim() !== "") {
var fullPath = currentPath + "/" + text.trim();
fileSelected(fullPath);
fileBrowserModal.close();
var fullPath = currentPath + "/" + text.trim()
fileSelected(fullPath)
fileBrowserModal.close()
}
}
}
StyledRect {
id: saveButton
width: 80
height: 36
color: fileNameInput.text.trim() !== "" ? Theme.primary : Theme.surfaceVariant
@@ -683,17 +610,18 @@ DankModal {
enabled: fileNameInput.text.trim() !== ""
onClicked: {
if (fileNameInput.text.trim() !== "") {
var fullPath = currentPath + "/" + fileNameInput.text.trim();
fileSelected(fullPath);
fileBrowserModal.close();
var fullPath = currentPath + "/" + fileNameInput.text.trim()
fileSelected(fullPath)
fileBrowserModal.close()
}
}
}
}
}
FileBrowserKeyboardHints {
KeyboardHints {
id: keyboardHints
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@@ -701,8 +629,9 @@ DankModal {
showHints: fileBrowserModal.showKeyboardHints
}
FileBrowserFileInfo {
FileInfo {
id: fileInfo
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: Theme.spacingL
@@ -711,16 +640,15 @@ DankModal {
selectedIndex: fileBrowserModal.selectedIndex
sourceFolderModel: folderModel
currentPath: fileBrowserModal.currentPath
// Bind directly to the modal's selected file properties
currentFileName: fileBrowserModal.selectedFileName
currentFileIsDir: fileBrowserModal.selectedFileIsDir
currentFileExtension: {
if (fileBrowserModal.selectedFileIsDir || !fileBrowserModal.selectedFileName) return ""
if (fileBrowserModal.selectedFileIsDir || !fileBrowserModal.selectedFileName)
return ""
var lastDot = fileBrowserModal.selectedFileName.lastIndexOf('.')
return lastDot > 0 ? fileBrowserModal.selectedFileName.substring(lastDot + 1).toLowerCase() : ""
}
}
}
}

View File

@@ -1,7 +1,5 @@
import QtQuick
import QtQuick.Controls
import QtCore
import Qt.labs.folderlistmodel
import Quickshell.Io
import qs.Common
import qs.Widgets
@@ -16,8 +14,7 @@ Rectangle {
height: 200
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g,
Theme.surfaceContainer.b, 0.95)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
border.color: Theme.secondary
border.width: 2
opacity: showFileInfo ? 1 : 0
@@ -25,7 +22,7 @@ Rectangle {
onShowFileInfoChanged: {
if (showFileInfo && currentFileName && currentPath) {
var fullPath = currentPath + "/" + currentFileName
const fullPath = currentPath + "/" + currentFileName
fileStatProcess.selectedFilePath = fullPath
fileStatProcess.running = true
}
@@ -41,13 +38,13 @@ Rectangle {
stdout: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
var parts = text.trim().split('|')
const parts = text.trim().split('|')
if (parts.length >= 4) {
fileStatProcess.fileStats = {
modifiedTime: parts[0],
permissions: parts[1],
size: parseInt(parts[2]) || 0,
fullPath: parts[3]
"modifiedTime": parts[0],
"permissions": parts[1],
"size": parseInt(parts[2]) || 0,
"fullPath": parts[3]
}
}
}
@@ -57,53 +54,13 @@ Rectangle {
onExited: function (exitCode) {}
}
readonly property var currentFileData: {
if (!sourceFolderModel || selectedIndex < 0 || selectedIndex >= sourceFolderModel.count) {
return {
exists: false,
name: "No selection",
type: "",
size: "",
modified: "",
permissions: "",
extension: "",
position: selectedIndex >= 0 ? (selectedIndex + 1) + " of " + (sourceFolderModel ? sourceFolderModel.count : 0) : "N/A"
}
}
var path = currentPath
if (path && selectedIndex >= 0) {
return {
exists: true,
name: "Loading...",
type: "file",
size: "Calculating...",
modified: "Loading...",
permissions: "Loading...",
extension: "",
position: (selectedIndex + 1) + " of " + sourceFolderModel.count
}
}
return {
exists: false,
name: "No selection",
type: "",
size: "",
modified: "",
permissions: "",
extension: "",
position: "N/A"
}
}
property string currentFileName: ""
property bool currentFileIsDir: false
property string currentFileExtension: ""
onCurrentFileNameChanged: {
if (showFileInfo && currentFileName && currentPath) {
var fullPath = currentPath + "/" + currentFileName
const fullPath = currentPath + "/" + currentFileName
if (fullPath !== fileStatProcess.selectedFilePath) {
fileStatProcess.selectedFilePath = fullPath
fileStatProcess.running = true
@@ -117,9 +74,9 @@ Rectangle {
currentFileName = fileName || ""
currentFileIsDir = isDirectory || false
var ext = ""
let ext = ""
if (!isDirectory && fileName) {
var lastDot = fileName.lastIndexOf('.')
const lastDot = fileName.lastIndexOf('.')
if (lastDot > 0) {
ext = fileName.substring(lastDot + 1).toLowerCase()
}
@@ -135,27 +92,27 @@ Rectangle {
readonly property var currentFileDisplayData: {
if (selectedIndex < 0 || !sourceFolderModel) {
return {
exists: false,
name: "No selection",
type: "",
size: "",
modified: "",
permissions: "",
extension: "",
position: "N/A"
"exists": false,
"name": "No selection",
"type": "",
"size": "",
"modified": "",
"permissions": "",
"extension": "",
"position": "N/A"
}
}
var hasValidFile = currentFileName !== ""
const hasValidFile = currentFileName !== ""
return {
exists: hasValidFile,
name: hasValidFile ? currentFileName : "Loading...",
type: currentFileIsDir ? "Directory" : "File",
size: fileStatProcess.fileStats ? formatFileSize(fileStatProcess.fileStats.size) : "Calculating...",
modified: fileStatProcess.fileStats ? formatDateTime(fileStatProcess.fileStats.modifiedTime) : "Loading...",
permissions: fileStatProcess.fileStats ? fileStatProcess.fileStats.permissions : "Loading...",
extension: currentFileExtension,
position: sourceFolderModel ? ((selectedIndex + 1) + " of " + sourceFolderModel.count) : "N/A"
"exists": hasValidFile,
"name": hasValidFile ? currentFileName : "Loading...",
"type": currentFileIsDir ? "Directory" : "File",
"size": fileStatProcess.fileStats ? formatFileSize(fileStatProcess.fileStats.size) : "Calculating...",
"modified": fileStatProcess.fileStats ? formatDateTime(fileStatProcess.fileStats.modifiedTime) : "Loading...",
"permissions": fileStatProcess.fileStats ? fileStatProcess.fileStats.permissions : "Loading...",
"extension": currentFileExtension,
"position": sourceFolderModel ? ((selectedIndex + 1) + " of " + sourceFolderModel.count) : "N/A"
}
}
@@ -251,16 +208,20 @@ Rectangle {
}
function formatFileSize(bytes) {
if (bytes === 0 || !bytes) return "0 B"
var k = 1024
var sizes = ['B', 'KB', 'MB', 'GB', 'TB']
var i = Math.floor(Math.log(bytes) / Math.log(k))
if (bytes === 0 || !bytes) {
return "0 B"
}
const k = 1024
const 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 formatDateTime(dateTimeString) {
if (!dateTimeString) return "Unknown"
var parts = dateTimeString.split(' ')
if (!dateTimeString) {
return "Unknown"
}
const parts = dateTimeString.split(' ')
if (parts.length >= 2) {
return parts[0] + " " + parts[1].split('.')[0]
}

View File

@@ -9,8 +9,7 @@ Rectangle {
height: 80
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g,
Theme.surfaceContainer.b, 0.95)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
border.color: Theme.primary
border.width: 2
opacity: showHints ? 1 : 0

View File

@@ -6,6 +6,7 @@ import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modals.FileBrowser
pragma ComponentBehavior: Bound

View File

@@ -4,6 +4,7 @@ import QtQuick.Effects
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Modals.FileBrowser
import qs.Modules.Settings
import qs.Services
import qs.Widgets

View File

@@ -4,6 +4,7 @@ import QtQuick.Effects
import Quickshell
import qs.Common
import qs.Modals
import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets

View File

@@ -4,6 +4,7 @@ import Quickshell
import Quickshell.Io
import qs.Common
import qs.Modals
import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets

View File

@@ -13,7 +13,7 @@ StyledText {
property int weight: filled ? 500 : 400
font.family: "Material Symbols Rounded"
font.pixelSize: Appearance.fontSize.normal
font.pixelSize: Theme.fontSizeMedium
font.weight: weight
color: Theme.surfaceText
verticalAlignment: Text.AlignVCenter
@@ -27,17 +27,15 @@ StyledText {
Behavior on fill {
NumberAnimation {
duration: Appearance.anim.durations.quick
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on weight {
NumberAnimation {
duration: Appearance.anim.durations.quick
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}

View File

@@ -5,6 +5,7 @@ import Quickshell.Io
import Quickshell.Widgets
import qs.Common
import qs.Modals
import qs.Modals.Clipboard
import qs.Modules
import qs.Modules.AppDrawer
import qs.Modules.CentcomCenter