mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
File Picker: Using platform's native picker - removed custom picker.
This commit is contained in:
@@ -30,7 +30,7 @@ ColumnLayout {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
}
|
||||
|
||||
NInputButton {
|
||||
NTextInputButton {
|
||||
label: `${Quickshell.env("USER") || "user"}'s profile picture`
|
||||
description: "Your profile picture that appears throughout the interface."
|
||||
text: Settings.data.general.avatarImage
|
||||
@@ -39,18 +39,20 @@ ColumnLayout {
|
||||
buttonTooltip: "Browse for avatar image"
|
||||
onInputEditingFinished: Settings.data.general.avatarImage = text
|
||||
onButtonClicked: {
|
||||
FilePickerService.open({
|
||||
"title": "Select Avatar Image",
|
||||
"initialPath": Settings.data.general.avatarImage || Quickshell.env("HOME"),
|
||||
"selectFiles": true,
|
||||
"scaling": scaling,
|
||||
"parent": root,
|
||||
"onSelected": path => Settings.data.general.avatarImage = path
|
||||
})
|
||||
filePicker.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NFilePicker {
|
||||
id: filePicker
|
||||
pickerType: "file"
|
||||
title: "Select avatar image"
|
||||
initialPath: Settings.data.general.avatarImage.substr(0, Settings.data.general.avatarImage.lastIndexOf("/")) || Quickshell.env("HOME")
|
||||
nameFilters: ["Image files (*.jpg *.jpeg *.png *.gif *.pnm *.bmp *.face)", "All files (*)"]
|
||||
onAccepted: paths => Settings.data.general.avatarImage = paths[0]
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -20,24 +21,15 @@ ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NInputButton {
|
||||
NTextInputButton {
|
||||
label: "Output folder"
|
||||
description: "Folder where screen recordings will be saved."
|
||||
placeholderText: "/home/xxx/Videos"
|
||||
placeholderText: Quickshell.env("HOME") + "/Videos"
|
||||
text: Settings.data.screenRecorder.directory
|
||||
buttonIcon: "folder-open"
|
||||
buttonTooltip: "Browse for output folder"
|
||||
onInputEditingFinished: Settings.data.screenRecorder.directory = text
|
||||
onButtonClicked: {
|
||||
FilePickerService.open({
|
||||
"title": "Select Output Folder",
|
||||
"initialPath": Settings.data.screenRecorder.directory || Quickshell.env("HOME") + "/Videos",
|
||||
"selectFiles": false,
|
||||
"scaling": scaling,
|
||||
"parent": root,
|
||||
"onSelected": path => Settings.data.screenRecorder.directory = path
|
||||
})
|
||||
}
|
||||
onButtonClicked: folderPicker.open()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -261,4 +253,12 @@ ColumnLayout {
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
NFilePicker {
|
||||
id: folderPicker
|
||||
pickerType: "folder"
|
||||
title: "Select output folder"
|
||||
initialPath: Settings.data.screenRecorder.directory || Quickshell.env("HOME") + "/Videos"
|
||||
onAccepted: paths => Settings.data.screenRecorder.directory = paths[0]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ ColumnLayout {
|
||||
id: root
|
||||
spacing: Style.marginL * scaling
|
||||
|
||||
property string specificFolderMonitorName: ""
|
||||
|
||||
NHeader {
|
||||
label: "Wallpaper settings"
|
||||
description: "Control how wallpapers are managed and displayed."
|
||||
@@ -18,7 +20,7 @@ ColumnLayout {
|
||||
|
||||
NToggle {
|
||||
label: "Enable wallpaper management"
|
||||
description: "Manage wallpapers with Noctalia. (Uncheck if you prefer using another application)."
|
||||
description: "Manage wallpapers with Noctalia. Uncheck if you prefer using another application."
|
||||
checked: Settings.data.wallpaper.enabled
|
||||
onToggled: checked => Settings.data.wallpaper.enabled = checked
|
||||
Layout.bottomMargin: Style.marginL * scaling
|
||||
@@ -29,7 +31,7 @@ ColumnLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NInputButton {
|
||||
NTextInputButton {
|
||||
id: wallpaperPathInput
|
||||
label: "Wallpaper folder"
|
||||
description: "Path to your main wallpaper folder."
|
||||
@@ -37,13 +39,8 @@ ColumnLayout {
|
||||
buttonIcon: "folder-open"
|
||||
buttonTooltip: "Browse for wallpaper folder"
|
||||
Layout.fillWidth: true
|
||||
|
||||
onInputEditingFinished: {
|
||||
Settings.data.wallpaper.directory = text
|
||||
}
|
||||
onButtonClicked: {
|
||||
openFileManager()
|
||||
}
|
||||
onInputEditingFinished: Settings.data.wallpaper.directory = text
|
||||
onButtonClicked: mainFolderPicker.open()
|
||||
}
|
||||
|
||||
// Monitor-specific directories
|
||||
@@ -83,17 +80,15 @@ ColumnLayout {
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
}
|
||||
|
||||
NInputButton {
|
||||
NTextInputButton {
|
||||
text: WallpaperService.getMonitorDirectory(modelData.name)
|
||||
buttonIcon: "folder-open"
|
||||
buttonTooltip: "Browse for wallpaper folder"
|
||||
Layout.fillWidth: true
|
||||
|
||||
onInputEditingFinished: {
|
||||
WallpaperService.setMonitorDirectory(modelData.name, text)
|
||||
}
|
||||
onInputEditingFinished: WallpaperService.setMonitorDirectory(modelData.name, text)
|
||||
onButtonClicked: {
|
||||
openMonitorFileManager(modelData.name)
|
||||
specificFolderMonitorName = modelData.name
|
||||
monitorFolderPicker.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,26 +338,17 @@ ColumnLayout {
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// File manager functions
|
||||
function openFileManager() {
|
||||
FilePickerService.open({
|
||||
"title": "Select Wallpaper Folder",
|
||||
"initialPath": Settings.data.wallpaper.directory || Quickshell.env("HOME"),
|
||||
"selectFiles": false,
|
||||
"scaling": scaling,
|
||||
"parent": root,
|
||||
"onSelected": path => Settings.data.wallpaper.directory = path
|
||||
})
|
||||
NFilePicker {
|
||||
id: mainFolderPicker
|
||||
pickerType: "folder"
|
||||
title: "Select wallpaper folder"
|
||||
onAccepted: paths => Settings.data.wallpaper.directory = paths[0]
|
||||
}
|
||||
|
||||
function openMonitorFileManager(monitorName) {
|
||||
FilePickerService.open({
|
||||
"title": "Select Monitor Wallpaper Folder",
|
||||
"initialPath": WallpaperService.getMonitorDirectory(monitorName),
|
||||
"selectFiles": false,
|
||||
"scaling": scaling,
|
||||
"parent": root,
|
||||
"onSelected": path => WallpaperService.setMonitorDirectory(monitorName, path)
|
||||
})
|
||||
NFilePicker {
|
||||
id: monitorFolderPicker
|
||||
pickerType: "folder"
|
||||
title: "Select monitor wallpaper folder"
|
||||
onAccepted: paths => WallpaperService.setMonitorDirectory(specificFolderMonitorName, paths[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
// Function to open a file manager dialog
|
||||
function open(options) {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl(Quickshell.shellDir + "/Widgets/NFilePicker.qml"))
|
||||
if (component.status === Component.Ready) {
|
||||
// Extract directory from file path if it's a file
|
||||
var initialPath = options.initialPath || Quickshell.env("HOME")
|
||||
if (options.selectFiles && initialPath !== Quickshell.env("HOME")) {
|
||||
// If selecting files and path is not home, extract directory
|
||||
var pathParts = initialPath.split('/')
|
||||
if (pathParts.length > 1) {
|
||||
pathParts.pop() // Remove filename
|
||||
initialPath = pathParts.join('/') || '/'
|
||||
}
|
||||
}
|
||||
|
||||
var dialog = component.createObject(options.parent || Overlay.overlay, {
|
||||
"title": options.title || "Select File/Folder",
|
||||
"initialPath": initialPath,
|
||||
"selectFiles": options.selectFiles || false,
|
||||
"selectFolders": !options.selectFiles || false,
|
||||
"scaling": options.scaling || 1.0
|
||||
})
|
||||
if (dialog) {
|
||||
if (options.onSelected) {
|
||||
if (options.selectFiles) {
|
||||
dialog.fileSelected.connect(options.onSelected)
|
||||
} else {
|
||||
dialog.folderSelected.connect(options.onSelected)
|
||||
}
|
||||
}
|
||||
dialog.open()
|
||||
return dialog
|
||||
}
|
||||
} else {
|
||||
console.error("Component error:", component.errorString())
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
+163
-782
@@ -1,816 +1,197 @@
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.folderlistmodel
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "../Helpers/FuzzySort.js" as FuzzySort
|
||||
|
||||
Popup {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// Properties
|
||||
property string title: "File Picker"
|
||||
property string initialPath: Quickshell.env("HOME") || "/home"
|
||||
property bool selectFiles: true
|
||||
property bool selectFolders: true
|
||||
property var nameFilters: ["*"]
|
||||
property bool showDirs: true
|
||||
property real scaling: 1.0
|
||||
// Public API Properties
|
||||
property string initialPath: ""
|
||||
property var selectedPaths: []
|
||||
property string currentPath: initialPath
|
||||
property bool shouldResetSelection: false
|
||||
property string selectedPath: ""
|
||||
property bool multipleSelection: false
|
||||
property string pickerType: "file" // "file" or "folder"
|
||||
property var nameFilters: ["All files (*)"] // e.g., ["Image files (*.png *.jpg)", "Text files (*.txt)"]
|
||||
property string title: pickerType === "folder" ? "Select Folder" : "Select File"
|
||||
property string acceptLabel: "Select"
|
||||
property string rejectLabel: "Cancel"
|
||||
|
||||
// Signals
|
||||
signal fileSelected(string path)
|
||||
signal filesSelected(var paths)
|
||||
signal folderSelected(string path)
|
||||
signal cancelled
|
||||
// State properties
|
||||
property bool isOpen: false
|
||||
|
||||
function openFileManager() {
|
||||
if (!root.currentPath)
|
||||
root.currentPath = root.initialPath
|
||||
shouldResetSelection = true
|
||||
open()
|
||||
}
|
||||
// Signals for external connections
|
||||
signal accepted(var paths)
|
||||
signal rejected
|
||||
signal pathSelected(string path)
|
||||
signal pathsSelected(var paths)
|
||||
signal beforeOpen
|
||||
signal afterClose
|
||||
|
||||
function getFileIcon(fileName) {
|
||||
const ext = fileName.split('.').pop().toLowerCase()
|
||||
const iconMap = {
|
||||
"txt": 'filepicker-file-text',
|
||||
"md": 'filepicker-file-text',
|
||||
"log": 'filepicker-file-text',
|
||||
"jpg": 'filepicker-photo',
|
||||
"jpeg": 'filepicker-photo',
|
||||
"png": 'filepicker-photo',
|
||||
"gif": 'filepicker-photo',
|
||||
"bmp": 'filepicker-photo',
|
||||
"svg": 'filepicker-photo',
|
||||
"mp4": 'filepicker-video',
|
||||
"avi": 'filepicker-video',
|
||||
"mkv": 'filepicker-video',
|
||||
"mov": 'filepicker-video',
|
||||
"mp3": 'filepicker-music',
|
||||
"wav": 'filepicker-music',
|
||||
"flac": 'filepicker-music',
|
||||
"ogg": 'filepicker-music',
|
||||
"zip": 'filepicker-archive',
|
||||
"tar": 'filepicker-archive',
|
||||
"gz": 'filepicker-archive',
|
||||
"rar": 'filepicker-archive',
|
||||
"7z": 'filepicker-archive',
|
||||
"pdf": 'filepicker-text',
|
||||
"doc": 'filepicker-text',
|
||||
"docx": 'filepicker-text',
|
||||
"xls": 'filepicker-table',
|
||||
"xlsx": 'filepicker-table',
|
||||
"ppt": 'filepicker-presentation',
|
||||
"pptx": 'filepicker-presentation',
|
||||
"html": 'filepicker-code',
|
||||
"htm": 'filepicker-code',
|
||||
"css": 'filepicker-code',
|
||||
"js": 'filepicker-code',
|
||||
"json": 'filepicker-code',
|
||||
"xml": 'filepicker-code',
|
||||
"exe": 'filepicker-settings',
|
||||
"app": 'filepicker-settings',
|
||||
"deb": 'filepicker-settings',
|
||||
"rpm": 'filepicker-settings'
|
||||
// Public functions
|
||||
function open() {
|
||||
beforeOpen()
|
||||
|
||||
if (PanelService.openedPanel !== null) {
|
||||
PanelService.openedPanel.isMasked = true
|
||||
}
|
||||
return iconMap[ext] || 'filepicker-file'
|
||||
|
||||
isOpen = true
|
||||
|
||||
// Small delay to ensure panel changes happen first
|
||||
Qt.callLater(function () {
|
||||
if (pickerType === "folder") {
|
||||
folderDialog.open()
|
||||
} else {
|
||||
fileDialog.open()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
function confirmSelection() {
|
||||
if (filePickerPanel.currentSelection.length === 0)
|
||||
return
|
||||
|
||||
root.selectedPaths = filePickerPanel.currentSelection
|
||||
const path = filePickerPanel.currentSelection[0]
|
||||
|
||||
if (filePickerPanel.currentSelection.length === 1) {
|
||||
const isDir = folderModel.get(folderModel.indexOf(path), "fileIsDir")
|
||||
if (root.selectFiles && !root.selectFolders)
|
||||
root.fileSelected(path)
|
||||
else if (root.selectFolders && !root.selectFiles)
|
||||
root.folderSelected(path)
|
||||
else
|
||||
isDir ? root.folderSelected(path) : root.fileSelected(path)
|
||||
function close() {
|
||||
if (pickerType === "folder") {
|
||||
folderDialog.close()
|
||||
} else {
|
||||
root.filesSelected(filePickerPanel.currentSelection)
|
||||
fileDialog.close()
|
||||
}
|
||||
root.close()
|
||||
|
||||
handleClose()
|
||||
}
|
||||
|
||||
function updateFilteredModel() {
|
||||
filteredModel.clear()
|
||||
const searchText = filePickerPanel.filterText.toLowerCase()
|
||||
function handleClose() {
|
||||
isOpen = false
|
||||
|
||||
for (var i = 0; i < folderModel.count; i++) {
|
||||
const fileName = folderModel.get(i, "fileName")
|
||||
const filePath = folderModel.get(i, "filePath")
|
||||
const fileIsDir = folderModel.get(i, "fileIsDir")
|
||||
const fileSize = folderModel.get(i, "fileSize")
|
||||
if (PanelService.openedPanel !== null) {
|
||||
PanelService.openedPanel.isMasked = false
|
||||
}
|
||||
|
||||
if (root.selectFolders && !root.selectFiles && !fileIsDir)
|
||||
continue
|
||||
if (searchText === "" || fileName.toLowerCase().includes(searchText)) {
|
||||
filteredModel.append({
|
||||
"fileName": fileName,
|
||||
"filePath": filePath,
|
||||
"fileIsDir": fileIsDir,
|
||||
"fileSize": fileSize
|
||||
})
|
||||
afterClose()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
selectedPaths = []
|
||||
selectedPath = ""
|
||||
}
|
||||
|
||||
// Helper function to set file extensions easily
|
||||
function setFileExtensions(extensions) {
|
||||
if (!extensions || extensions.length === 0) {
|
||||
nameFilters = ["All files (*)"]
|
||||
return
|
||||
}
|
||||
|
||||
var filters = []
|
||||
for (var i = 0; i < extensions.length; i++) {
|
||||
var ext = extensions[i]
|
||||
if (typeof ext === "string") {
|
||||
// Simple extension like "png"
|
||||
filters.push(ext.toUpperCase() + " files (*." + ext + ")")
|
||||
} else if (typeof ext === "object" && ext.label && ext.extensions) {
|
||||
// Complex filter like {label: "Images", extensions: ["png", "jpg", "jpeg"]}
|
||||
var filterStr = ext.label + " ("
|
||||
for (var j = 0; j < ext.extensions.length; j++) {
|
||||
filterStr += "*." + ext.extensions[j]
|
||||
if (j < ext.extensions.length - 1)
|
||||
filterStr += " "
|
||||
}
|
||||
filterStr += ")"
|
||||
filters.push(filterStr)
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.length > 0) {
|
||||
filters.push("All files (*)")
|
||||
nameFilters = filters
|
||||
}
|
||||
}
|
||||
|
||||
width: 900 * scaling
|
||||
height: 700 * scaling
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
anchors.centerIn: Overlay.overlay
|
||||
|
||||
background: Rectangle {
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusL * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
// Helper function to convert URL to local path
|
||||
function urlToPath(url) {
|
||||
var path = url.toString()
|
||||
// Remove file:// prefix (works for both Windows and Unix)
|
||||
path = path.replace(/^file:\/\/\//, "/") // Unix
|
||||
path = path.replace(/^file:\/\//, "") // Windows
|
||||
// Handle Windows drive letters
|
||||
if (Qt.platform.os === "windows") {
|
||||
path = path.replace(/^\/([A-Z]:)/, "$1")
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: filePickerPanel
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
color: Color.transparent
|
||||
|
||||
property string filterText: ""
|
||||
property var currentSelection: []
|
||||
property bool viewMode: true // true = grid, false = list
|
||||
property string searchText: ""
|
||||
property bool showSearchBar: false
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
icon: "filepicker-folder"
|
||||
color: Color.mPrimary
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
}
|
||||
NText {
|
||||
text: root.title
|
||||
font.pointSize: Style.fontSizeXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
NIconButton {
|
||||
icon: "filepicker-refresh"
|
||||
tooltipText: "Refresh"
|
||||
onClicked: folderModel.refresh()
|
||||
}
|
||||
NIconButton {
|
||||
icon: "filepicker-close"
|
||||
tooltipText: "Close"
|
||||
onClicked: {
|
||||
root.cancelled()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Navigation toolbar
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 45 * scaling
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusS * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NIconButton {
|
||||
icon: "filepicker-arrow-up"
|
||||
tooltipText: "Up"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
enabled: folderModel.folder.toString() !== "file:///"
|
||||
onClicked: {
|
||||
const parentPath = folderModel.parentFolder.toString().replace("file://", "")
|
||||
folderModel.folder = "file://" + parentPath
|
||||
root.currentPath = parentPath
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "filepicker-home"
|
||||
tooltipText: "Home"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: {
|
||||
const homePath = Quickshell.env("HOME") || "/home"
|
||||
folderModel.folder = "file://" + homePath
|
||||
root.currentPath = homePath
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: filePickerPanel.viewMode ? "filepicker-list" : "filepicker-layout-grid"
|
||||
tooltipText: filePickerPanel.viewMode ? "List View" : "Grid View"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: filePickerPanel.viewMode = !filePickerPanel.viewMode
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: filePickerPanel.showSearchBar ? "filepicker-x" : "filepicker-search"
|
||||
tooltipText: filePickerPanel.showSearchBar ? "Close Search" : "Search"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: {
|
||||
filePickerPanel.showSearchBar = !filePickerPanel.showSearchBar
|
||||
if (!filePickerPanel.showSearchBar) {
|
||||
filePickerPanel.searchText = ""
|
||||
filePickerPanel.filterText = ""
|
||||
root.updateFilteredModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NTextInput {
|
||||
id: locationInput
|
||||
text: root.currentPath
|
||||
placeholderText: "Enter path..."
|
||||
Layout.fillWidth: true
|
||||
onEditingFinished: {
|
||||
const newPath = text.trim()
|
||||
if (newPath !== "" && newPath !== root.currentPath) {
|
||||
folderModel.folder = "file://" + newPath
|
||||
root.currentPath = newPath
|
||||
} else {
|
||||
text = root.currentPath
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentPathChanged() {
|
||||
if (!locationInput.activeFocus)
|
||||
locationInput.text = root.currentPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search bar
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 45 * scaling
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusS * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
visible: filePickerPanel.showSearchBar
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NIcon {
|
||||
icon: "filepicker-search"
|
||||
color: Color.mOnSurfaceVariant
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
}
|
||||
NTextInput {
|
||||
id: searchInput
|
||||
placeholderText: "Search files and folders..."
|
||||
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: "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
|
||||
Layout.fillHeight: true
|
||||
color: Color.mSurface
|
||||
radius: Style.radiusM * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
clip: true
|
||||
|
||||
FolderListModel {
|
||||
id: folderModel
|
||||
folder: "file://" + root.currentPath
|
||||
nameFilters: root.nameFilters
|
||||
showDirs: root.showDirs
|
||||
showHidden: true
|
||||
sortField: FolderListModel.Name
|
||||
sortReversed: false
|
||||
onFolderChanged: {
|
||||
root.currentPath = folder.toString().replace("file://", "")
|
||||
filePickerPanel.currentSelection = []
|
||||
}
|
||||
onStatusChanged: {
|
||||
if (status === FolderListModel.Error) {
|
||||
if (root.currentPath !== Quickshell.env("HOME")) {
|
||||
folder = "file://" + Quickshell.env("HOME")
|
||||
root.currentPath = Quickshell.env("HOME")
|
||||
}
|
||||
} else if (status === FolderListModel.Ready) {
|
||||
root.updateFilteredModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: filteredModel
|
||||
}
|
||||
|
||||
// Common scroll bar component
|
||||
Component {
|
||||
id: scrollBarComponent
|
||||
ScrollBar {
|
||||
policy: ScrollBar.AsNeeded
|
||||
contentItem: Rectangle {
|
||||
implicitWidth: 6 * scaling
|
||||
implicitHeight: 100
|
||||
radius: Style.radiusM * scaling
|
||||
color: parent.pressed ? Qt.alpha(Color.mTertiary, 0.8) : parent.hovered ? Qt.alpha(Color.mTertiary, 0.8) : Qt.alpha(Color.mTertiary, 0.8)
|
||||
opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 1.0 : 0.0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
implicitWidth: 6 * scaling
|
||||
implicitHeight: 100
|
||||
color: Color.transparent
|
||||
opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 0.3 : 0.0
|
||||
radius: (Style.radiusM * scaling) / 2
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grid view
|
||||
GridView {
|
||||
id: gridView
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM * scaling
|
||||
model: filteredModel
|
||||
visible: filePickerPanel.viewMode
|
||||
clip: true
|
||||
|
||||
property int columns: Math.max(1, Math.floor(width / (120 * scaling)))
|
||||
property int itemSize: Math.floor((width - leftMargin - rightMargin - (columns * Style.marginS * scaling)) / columns)
|
||||
|
||||
cellWidth: Math.floor((width - leftMargin - rightMargin) / columns)
|
||||
cellHeight: Math.floor(itemSize * 0.8) + Style.marginXS * scaling + Style.fontSizeS * scaling + Style.marginM * scaling
|
||||
|
||||
leftMargin: Style.marginS * scaling
|
||||
rightMargin: Style.marginS * scaling
|
||||
topMargin: Style.marginS * scaling
|
||||
bottomMargin: Style.marginS * scaling
|
||||
|
||||
ScrollBar.vertical: scrollBarComponent.createObject(gridView, {
|
||||
"parent": gridView,
|
||||
"x": gridView.mirrored ? 0 : gridView.width - width,
|
||||
"y": 0,
|
||||
"height": gridView.height
|
||||
})
|
||||
|
||||
delegate: Rectangle {
|
||||
id: gridItem
|
||||
width: gridView.itemSize
|
||||
height: gridView.cellHeight
|
||||
color: Color.transparent
|
||||
radius: Style.radiusM * scaling
|
||||
|
||||
property bool isSelected: filePickerPanel.currentSelection.includes(model.filePath)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.transparent
|
||||
radius: parent.radius
|
||||
border.color: isSelected ? Color.mSecondary : Color.mSurface
|
||||
border.width: Math.max(1, Style.borderL * scaling)
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: (mouseArea.containsMouse && !isSelected) ? Color.mTertiary : Color.transparent
|
||||
radius: parent.radius
|
||||
border.color: (mouseArea.containsMouse && !isSelected) ? Color.mTertiary : Color.transparent
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
Rectangle {
|
||||
id: iconContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.round(gridView.itemSize * 0.67)
|
||||
color: Color.transparent
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
Image {
|
||||
id: thumbnail
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginXS * scaling
|
||||
source: iconContainer.isImage ? "file://" + model.filePath : ""
|
||||
fillMode: Image.PreserveAspectFit
|
||||
visible: iconContainer.isImage && status === Image.Ready
|
||||
smooth: false
|
||||
cache: true
|
||||
asynchronous: true
|
||||
sourceSize.width: 120 * scaling
|
||||
sourceSize.height: 120 * scaling
|
||||
onStatusChanged: {
|
||||
if (status === Image.Error)
|
||||
visible = false
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusS * scaling
|
||||
visible: thumbnail.status === Image.Loading
|
||||
NIcon {
|
||||
icon: "filepicker-photo"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NIcon {
|
||||
icon: model.fileIsDir ? "filepicker-folder" : root.getFileIcon(model.fileName)
|
||||
font.pointSize: Style.fontSizeXXL * 2 * scaling
|
||||
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
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
visible: !iconContainer.isImage || thumbnail.status !== Image.Ready
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Style.marginS * scaling
|
||||
width: 24 * scaling
|
||||
height: 24 * scaling
|
||||
radius: width / 2
|
||||
color: Color.mSecondary
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
visible: isSelected
|
||||
NIcon {
|
||||
icon: "filepicker-check"
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSecondary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: model.fileName
|
||||
color: {
|
||||
if (isSelected)
|
||||
return Color.mSecondary
|
||||
else if (mouseArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
else
|
||||
return Color.mOnSurface
|
||||
}
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
font.weight: isSelected ? Style.fontWeightBold : Style.fontWeightRegular
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 2
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (model.fileIsDir) {
|
||||
if (root.selectFolders && !root.selectFiles) {
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
} else {
|
||||
folderModel.folder = "file://" + model.filePath
|
||||
root.currentPath = model.filePath
|
||||
}
|
||||
} else {
|
||||
if (root.selectFiles)
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: mouse => {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (model.fileIsDir) {
|
||||
if (root.selectFolders && !root.selectFiles) {
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
root.confirmSelection()
|
||||
} else {
|
||||
folderModel.folder = "file://" + model.filePath
|
||||
root.currentPath = model.filePath
|
||||
}
|
||||
} else {
|
||||
if (root.selectFiles) {
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
root.confirmSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List view
|
||||
ListView {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
model: filteredModel
|
||||
visible: !filePickerPanel.viewMode
|
||||
clip: true
|
||||
|
||||
ScrollBar.vertical: scrollBarComponent.createObject(listView, {
|
||||
"parent": listView,
|
||||
"x": listView.mirrored ? 0 : listView.width - width,
|
||||
"y": 0,
|
||||
"height": listView.height
|
||||
})
|
||||
|
||||
delegate: Rectangle {
|
||||
id: listItem
|
||||
width: listView.width
|
||||
height: 40 * scaling
|
||||
color: {
|
||||
if (filePickerPanel.currentSelection.includes(model.filePath))
|
||||
return Color.mSecondary
|
||||
if (mouseArea.containsMouse)
|
||||
return Qt.alpha(Color.mOnSurface, 0.1)
|
||||
return Color.transparent
|
||||
}
|
||||
radius: Style.radiusS * scaling
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.marginM * scaling
|
||||
anchors.rightMargin: Style.marginM * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
icon: model.fileIsDir ? "filepicker-folder" : root.getFileIcon(model.fileName)
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: model.fileIsDir ? (filePickerPanel.currentSelection.includes(model.filePath) ? Color.mOnSecondary : Color.mPrimary) : Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: model.fileName
|
||||
color: filePickerPanel.currentSelection.includes(model.filePath) ? Color.mOnSecondary : Color.mOnSurface
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
font.weight: filePickerPanel.currentSelection.includes(model.filePath) ? Style.fontWeightBold : Style.fontWeightRegular
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
NText {
|
||||
text: model.fileIsDir ? "" : root.formatFileSize(model.fileSize)
|
||||
color: filePickerPanel.currentSelection.includes(model.filePath) ? Color.mOnSecondary : Color.mOnSurfaceVariant
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
visible: !model.fileIsDir
|
||||
Layout.preferredWidth: implicitWidth
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (model.fileIsDir) {
|
||||
if (root.selectFolders && !root.selectFiles) {
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
} else {
|
||||
folderModel.folder = "file://" + model.filePath
|
||||
root.currentPath = model.filePath
|
||||
}
|
||||
} else {
|
||||
if (root.selectFiles)
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: mouse => {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (model.fileIsDir) {
|
||||
if (root.selectFolders && !root.selectFiles) {
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
root.confirmSelection()
|
||||
} else {
|
||||
folderModel.folder = "file://" + model.filePath
|
||||
root.currentPath = model.filePath
|
||||
}
|
||||
} else {
|
||||
if (root.selectFiles) {
|
||||
filePickerPanel.currentSelection = [model.filePath]
|
||||
root.confirmSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NText {
|
||||
text: {
|
||||
if (filePickerPanel.searchText.length > 0) {
|
||||
return "Searching for: \"" + filePickerPanel.searchText + "\" (" + filteredModel.count + " matches)"
|
||||
} else if (filePickerPanel.currentSelection.length > 0) {
|
||||
return filePickerPanel.currentSelection.length + " item(s) selected"
|
||||
} else {
|
||||
return filteredModel.count + " items"
|
||||
}
|
||||
}
|
||||
color: filePickerPanel.searchText.length > 0 ? Color.mPrimary : Color.mOnSurfaceVariant
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: "Cancel"
|
||||
outlined: true
|
||||
onClicked: {
|
||||
root.cancelled()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: {
|
||||
if (root.selectFolders && !root.selectFiles)
|
||||
return "Select Folder"
|
||||
else if (root.selectFiles && !root.selectFolders)
|
||||
return "Select File"
|
||||
else
|
||||
return "Select"
|
||||
}
|
||||
icon: "filepicker-check"
|
||||
enabled: filePickerPanel.currentSelection.length > 0
|
||||
onClicked: root.confirmSelection()
|
||||
}
|
||||
}
|
||||
// Get default folder with proper fallback
|
||||
function getDefaultFolder() {
|
||||
if (root.initialPath) {
|
||||
return "file:///" + root.initialPath.replace(/^\//, "")
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onShouldResetSelectionChanged() {
|
||||
if (root.shouldResetSelection) {
|
||||
filePickerPanel.currentSelection = []
|
||||
root.shouldResetSelection = false
|
||||
// Fallback to home directory
|
||||
try {
|
||||
return StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
} catch (e) {
|
||||
// Final fallback if StandardPaths fails
|
||||
return "file:///" + (Qt.platform.os === "windows" ? "C:/Users" : "/home")
|
||||
}
|
||||
}
|
||||
|
||||
// FileDialog for file selection (Qt 6.x)
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
title: root.title
|
||||
currentFolder: getDefaultFolder()
|
||||
fileMode: root.multipleSelection ? FileDialog.OpenFiles : FileDialog.OpenFile
|
||||
nameFilters: root.nameFilters
|
||||
acceptLabel: root.acceptLabel
|
||||
rejectLabel: root.rejectLabel
|
||||
modality: Qt.WindowModal
|
||||
|
||||
onAccepted: {
|
||||
if (fileMode === FileDialog.OpenFiles) {
|
||||
var paths = []
|
||||
for (var i = 0; i < fileDialog.selectedFiles.length; i++) {
|
||||
paths.push(urlToPath(fileDialog.selectedFiles[i]))
|
||||
}
|
||||
root.selectedPaths = paths
|
||||
root.selectedPath = paths.length > 0 ? paths[0] : ""
|
||||
root.pathsSelected(paths)
|
||||
root.accepted(paths)
|
||||
} else {
|
||||
var singlePath = urlToPath(fileDialog.selectedFile)
|
||||
root.selectedPath = singlePath
|
||||
root.selectedPaths = [singlePath]
|
||||
root.pathSelected(singlePath)
|
||||
root.accepted([singlePath])
|
||||
}
|
||||
root.handleClose()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!root.currentPath)
|
||||
root.currentPath = root.initialPath
|
||||
folderModel.folder = "file://" + root.currentPath
|
||||
onRejected: {
|
||||
root.rejected()
|
||||
root.handleClose()
|
||||
}
|
||||
}
|
||||
|
||||
// FolderDialog for folder selection (Qt 6.x)
|
||||
FolderDialog {
|
||||
id: folderDialog
|
||||
title: root.title
|
||||
currentFolder: getDefaultFolder()
|
||||
acceptLabel: root.acceptLabel
|
||||
rejectLabel: root.rejectLabel
|
||||
modality: Qt.WindowModal
|
||||
|
||||
onAccepted: {
|
||||
var folderPath = urlToPath(folderDialog.selectedFolder)
|
||||
root.selectedPath = folderPath
|
||||
root.selectedPaths = [folderPath]
|
||||
root.pathSelected(folderPath)
|
||||
root.accepted([folderPath])
|
||||
root.handleClose()
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
root.rejected()
|
||||
root.handleClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -25,6 +25,8 @@ Loader {
|
||||
property bool panelAnchorLeft: false
|
||||
property bool panelAnchorRight: false
|
||||
|
||||
property bool isMasked: false
|
||||
|
||||
// Properties to support positioning relative to the opener (button)
|
||||
property bool useButtonPosition: false
|
||||
property point buttonPosition: Qt.point(0, 0)
|
||||
@@ -177,12 +179,18 @@ Loader {
|
||||
}
|
||||
|
||||
visible: true
|
||||
|
||||
color: Settings.data.general.dimDesktop ? Qt.alpha(Color.mShadow, dimmingOpacity) : Color.transparent
|
||||
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.namespace: "noctalia-panel"
|
||||
WlrLayershell.keyboardFocus: root.panelKeyboardFocus ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
|
||||
mask: root.isMasked ? maskRegion : null
|
||||
|
||||
Region {
|
||||
id: maskRegion
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationSlow
|
||||
@@ -244,7 +252,7 @@ Loader {
|
||||
}
|
||||
|
||||
scale: root.scaleValue
|
||||
opacity: root.opacityValue
|
||||
opacity: root.isMasked ? 0 : root.opacityValue
|
||||
x: isDragged ? manualX : calculatedX
|
||||
y: isDragged ? manualY : calculatedY
|
||||
|
||||
|
||||
Reference in New Issue
Block a user