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 {
@@ -14,10 +10,9 @@ DankModal {
property string confirmButtonText: "Confirm"
property string cancelButtonText: "Cancel"
property color confirmButtonColor: Theme.primary
property var onConfirm: function() {}
property var onCancel: function() {}
property int selectedButton: -1 // -1 = none, 0 = Cancel, 1 = Confirm
property var onConfirm: function () {}
property var onCancel: function () {}
property int selectedButton: -1
property bool keyboardNavigation: false
function show(title, message, onConfirmCallback, onCancelCallback) {
@@ -26,33 +21,36 @@ DankModal {
confirmButtonText = "Confirm"
cancelButtonText = "Cancel"
confirmButtonColor = Theme.primary
onConfirm = onConfirmCallback || function() {}
onCancel = onCancelCallback || function() {}
onConfirm = onConfirmCallback || (() => {})
onCancel = onCancelCallback || (() => {})
selectedButton = -1
keyboardNavigation = false
open()
}
function showWithOptions(options) {
confirmTitle = options.title || ""
confirmMessage = options.message || ""
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,18 +62,22 @@ DankModal {
shouldHaveFocus: true
onBackgroundClicked: {
close()
if (onCancel) onCancel()
if (onCancel) {
onCancel()
}
}
onOpened: {
modalFocusScope.forceActiveFocus()
modalFocusScope.focus = true
shouldHaveFocus = true
}
modalFocusScope.Keys.onPressed: function(event) {
modalFocusScope.Keys.onPressed: function (event) {
switch (event.key) {
case Qt.Key_Escape:
close()
if (onCancel) onCancel()
if (onCancel) {
onCancel()
}
event.accepted = true
break
case Qt.Key_Left:
@@ -148,12 +150,13 @@ 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,13 +187,14 @@ 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,10 +51,11 @@ PanelWindow {
}
function toggle() {
if (shouldBeVisible)
if (shouldBeVisible) {
close()
else
} else {
open()
}
}
visible: shouldBeVisible
@@ -84,6 +75,16 @@ PanelWindow {
}
}
Connections {
function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
close()
}
}
target: ModalManager
}
Timer {
id: closeTimer
@@ -112,13 +113,10 @@ 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)
root.backgroundClicked()
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()
}
}
}
@@ -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
@@ -25,39 +21,29 @@ DankModal {
property int selectedIndex: -1
property bool keyboardNavigationActive: false
property bool backButtonFocused: false
property bool saveMode: false // Enable save functionality
property string defaultFileName: "" // Default filename for save mode
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,86 +54,122 @@ DankModal {
}
}
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) {
modalFocusScope.Keys.onPressed: function (event) {
keyboardController.handleKey(event)
}
onVisibleChanged: {
if (visible) {
var startPath = getLastPath()
currentPath = startPath
currentPath = getLastPath()
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
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) {
close()
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,207 +192,124 @@ DankModal {
}
return
}
switch (event.key) {
case Qt.Key_Tab:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
} else if (selectedIndex < totalItems - 1) {
selectedIndex++
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
case Qt.Key_Tab:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
} else if (selectedIndex < totalItems - 1) {
selectedIndex++
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
} else {
selectedIndex = 0
}
event.accepted = true
break
case Qt.Key_Backtab:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = totalItems - 1
} else if (selectedIndex > 0) {
selectedIndex--
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
} else {
selectedIndex = totalItems - 1
}
event.accepted = true
break
case Qt.Key_Left:
if (backButtonFocused)
return
if (selectedIndex > 0) {
selectedIndex--
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
}
event.accepted = true
break
case Qt.Key_Right:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
} else if (selectedIndex < totalItems - 1) {
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)
} else if (selectedIndex >= gridColumns) {
// Move up one row
selectedIndex -= gridColumns
} else if (currentPath !== homeDir) {
// At top row, go to back button
backButtonFocused = true
selectedIndex = -1
}
event.accepted = true
break
case Qt.Key_Down:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
} else {
// Move down one row if possible
var newIndex = selectedIndex + gridColumns
if (newIndex < totalItems) {
selectedIndex = newIndex
} else {
selectedIndex = 0
}
event.accepted = true
break
case Qt.Key_Backtab:
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = totalItems - 1
} else if (selectedIndex > 0) {
selectedIndex--
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
} else {
selectedIndex = totalItems - 1
}
event.accepted = true
break
case Qt.Key_Left:
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
// 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
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
selectedIndex = -1
}
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
var col = selectedIndex % gridColumns
var targetIndex = lastRowStart + col
if (targetIndex < totalItems && targetIndex > selectedIndex) {
selectedIndex = targetIndex
updateFileInfoForIndex(selectedIndex)
}
var targetIndex = lastRowStart + col
if (targetIndex < totalItems && targetIndex > selectedIndex) {
selectedIndex = targetIndex
}
}
event.accepted = true
break
case Qt.Key_Return:
case Qt.Key_Enter:
case Qt.Key_Space:
if (backButtonFocused) {
navigateUp()
} 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)
}
event.accepted = true
break
case Qt.Key_Return:
case Qt.Key_Enter:
case Qt.Key_Space:
if (backButtonFocused)
navigateUp()
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
}
}
}
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
executeKeyboardSelection(targetIndex)
}
}
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
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
@@ -405,7 +343,7 @@ DankModal {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankActionButton {
circular: false
iconName: "help"
@@ -414,7 +352,7 @@ DankModal {
hoverColor: Theme.surfacePressed
onClicked: fileBrowserModal.showKeyboardHints = !fileBrowserModal.showKeyboardHints
}
DankActionButton {
circular: false
iconName: "close"
@@ -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) {
return Theme.surfacePressed
}
return mouseArea.containsMouse ? Theme.surfaceVariant : "transparent"
}
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,35 +515,30 @@ 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 {
fileSelected(delegateRoot.filePath)
fileBrowserModal.close() // Close modal after file selection
fileBrowserModal.close() // Close modal after file selection
}
}
}
// 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,89 +547,91 @@ DankModal {
}
}
}
target: fileBrowserModal
}
}
}
}
// Save functionality - positioned at bottom in save mode
Row {
id: saveRow
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: Theme.spacingL
height: saveMode ? 40 : 0
visible: saveMode
spacing: Theme.spacingM
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
radius: Theme.cornerRadius
StyledText {
anchors.centerIn: parent
text: "Save"
color: fileNameInput.text.trim() !== "" ? Theme.primaryText : Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeMedium
}
StateLayer {
stateColor: Theme.primary
cornerRadius: Theme.cornerRadius
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
anchors.margins: Theme.spacingL
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
}
@@ -37,64 +34,24 @@ Rectangle {
property string selectedFilePath: ""
property var fileStats: null
running: false
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]
}
}
}
}
}
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"
}
onExited: function (exitCode) {}
}
property string currentFileName: ""
@@ -103,7 +60,7 @@ Rectangle {
onCurrentFileNameChanged: {
if (showFileInfo && currentFileName && currentPath) {
var fullPath = currentPath + "/" + currentFileName
const fullPath = currentPath + "/" + currentFileName
if (fullPath !== fileStatProcess.selectedFilePath) {
fileStatProcess.selectedFilePath = fullPath
fileStatProcess.running = true
@@ -116,16 +73,16 @@ Rectangle {
fileStatProcess.selectedFilePath = filePath
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()
}
}
currentFileExtension = ext
if (showFileInfo) {
fileStatProcess.running = true
}
@@ -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]
}
@@ -273,4 +234,4 @@ Rectangle {
easing.type: Theme.standardEasing
}
}
}
}

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
@@ -48,4 +47,4 @@ Rectangle {
easing.type: Theme.standardEasing
}
}
}
}

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