Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad23da4eda | |||
| 06ebc4ffbf | |||
| f2f9fa1302 | |||
| b4020438f9 | |||
| 184ab20d11 | |||
| 5097e30a77 | |||
| ef71ae8afd | |||
| 6533533936 | |||
| ba9926af18 |
+8
-1
@@ -31,6 +31,13 @@ if("shell" IN_LIST ENABLE_MODULES)
|
||||
foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths)
|
||||
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
|
||||
endforeach()
|
||||
install(FILES shell.qml DESTINATION "${INSTALL_QSCONFDIR}")
|
||||
|
||||
# Disable watching for changes
|
||||
file(READ shell.qml SHELL_QML)
|
||||
string(REPLACE "settings.watchFiles: true" "settings.watchFiles: false" SHELL_QML "${SHELL_QML}")
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/qml/shell.qml" "${SHELL_QML}")
|
||||
install(FILES "${CMAKE_BINARY_DIR}/qml/shell.qml" DESTINATION "${INSTALL_QSCONFDIR}")
|
||||
|
||||
# Greeter
|
||||
install(DIRECTORY Greeter/ DESTINATION "${INSTALL_GREETERCONFDIR}")
|
||||
endif()
|
||||
|
||||
@@ -15,6 +15,7 @@ Text {
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.family: Appearance.font.family.sans
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
linkColor: DynamicColors.palette.m3onPrimaryFixedVariant
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: Text.PlainText
|
||||
|
||||
|
||||
+12
-1
@@ -22,6 +22,7 @@ Singleton {
|
||||
property alias notifs: adapter.notifs
|
||||
property alias osd: adapter.osd
|
||||
property alias overview: adapter.overview
|
||||
property alias plugins: adapter.plugins
|
||||
property bool recentlySaved: false
|
||||
property alias screenshot: adapter.screenshot
|
||||
property alias services: adapter.services
|
||||
@@ -140,7 +141,8 @@ Singleton {
|
||||
launcher: serializeLauncher(),
|
||||
colors: serializeColors(),
|
||||
dock: serializeDock(),
|
||||
screenshot: serializeScreenshot()
|
||||
screenshot: serializeScreenshot(),
|
||||
plugins: serializePlugins()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -289,6 +291,13 @@ Singleton {
|
||||
};
|
||||
}
|
||||
|
||||
function serializePlugins(): var {
|
||||
return {
|
||||
enabled: plugins.enabled,
|
||||
entries: plugins.entries
|
||||
};
|
||||
}
|
||||
|
||||
function serializeScreenshot(): var {
|
||||
return {
|
||||
enable_pp: screenshot.enable_pp,
|
||||
@@ -458,6 +467,8 @@ Singleton {
|
||||
}
|
||||
property Overview overview: Overview {
|
||||
}
|
||||
property PluginConfig plugins: PluginConfig {
|
||||
}
|
||||
property Screenshot screenshot: Screenshot {
|
||||
}
|
||||
property Services services: Services {
|
||||
|
||||
@@ -30,9 +30,10 @@ Singleton {
|
||||
readonly property alias wallLuminance: analyser.luminance
|
||||
|
||||
function alterColor(c: color, a: real, layer: int): color {
|
||||
const luminance = getLuminance(c);
|
||||
const initLuminance = getLuminance(c);
|
||||
const luminance = Math.max(initLuminance, 0.001);
|
||||
|
||||
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
|
||||
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (0.2 + 0.3 * (1 - transparency.base)) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
|
||||
const scale = (luminance + offset) / luminance;
|
||||
const r = Math.max(0, Math.min(1, c.r * scale));
|
||||
const g = Math.max(0, Math.min(1, c.g * scale));
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import Quickshell.Io
|
||||
|
||||
JsonObject {
|
||||
property bool enabled: false
|
||||
property list<var> entries: [
|
||||
{
|
||||
id: "Plugin",
|
||||
enabled: false
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import ZShell.Models
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property alias plugins: plugins.entries
|
||||
|
||||
FileSystemModel {
|
||||
id: plugins
|
||||
|
||||
nameFilters: ["*.qml"]
|
||||
path: Quickshell.env("HOME") + "/.config/zshell"
|
||||
recursive: false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import ZShell.Models
|
||||
import qs.Config
|
||||
|
||||
Repeater {
|
||||
model: FetchPlugins.plugins
|
||||
|
||||
LazyLoader {
|
||||
required property FileSystemEntry modelData
|
||||
|
||||
activeAsync: Config.plugins.entries.some(p => {
|
||||
return p.id === modelData.baseName && p.enabled;
|
||||
})
|
||||
source: modelData.path
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import Quickshell
|
||||
import "../scripts/fzf.js" as Fzf
|
||||
import "../scripts/fuzzysort.js" as Fuzzy
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
property var extraOpts: ({})
|
||||
|
||||
@@ -136,7 +136,10 @@ CustomRect {
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
onLinkActivated: link => {
|
||||
Quickshell.execDetached(["app2unit", "-O", "--", link]);
|
||||
if (Config.launcher.uwsm)
|
||||
Quickshell.execDetached(["app2unit", "-O", "--", link]);
|
||||
else
|
||||
Quickshell.execDetached(["xdg-open", link]);
|
||||
root.visibilities.sidebar = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,12 +100,14 @@ Item {
|
||||
icon: `brightness_${(Math.round(value * 6) + 1)}`
|
||||
value: root.brightness
|
||||
|
||||
onMoved: {
|
||||
if (Config.osd.allMonBrightness) {
|
||||
root.monitor?.setBrightness(value);
|
||||
} else {
|
||||
for (const mon of Brightness.monitors) {
|
||||
mon.setBrightness(value);
|
||||
onPressedChanged: {
|
||||
if (!pressed) {
|
||||
if (Config.osd.allMonBrightness) {
|
||||
for (const mon of Brightness.monitors) {
|
||||
mon.setBrightness(value);
|
||||
}
|
||||
} else {
|
||||
root.monitor?.setBrightness(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,12 @@ Item {
|
||||
key: "updates"
|
||||
name: "Updates"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
icon: "extension"
|
||||
key: "plugins"
|
||||
name: "Extensions"
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
|
||||
@@ -19,8 +19,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Media update interval"
|
||||
min: 0
|
||||
name: "Media update interval"
|
||||
object: Config.dashboard
|
||||
setting: "mediaUpdateInterval"
|
||||
step: 50
|
||||
@@ -30,8 +30,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Resource update interval"
|
||||
min: 0
|
||||
name: "Resource update interval"
|
||||
object: Config.dashboard
|
||||
setting: "resourceUpdateInterval"
|
||||
step: 50
|
||||
@@ -41,8 +41,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Drag threshold"
|
||||
min: 0
|
||||
name: "Drag threshold"
|
||||
object: Config.dashboard
|
||||
setting: "dragThreshold"
|
||||
}
|
||||
@@ -107,112 +107,112 @@ SettingsPage {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Layout Sizes"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Layout Sizes"
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Tab indicator height"
|
||||
value: String(Config.dashboard.sizes.tabIndicatorHeight)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Tab indicator spacing"
|
||||
value: String(Config.dashboard.sizes.tabIndicatorSpacing)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Info width"
|
||||
value: String(Config.dashboard.sizes.infoWidth)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Info icon size"
|
||||
value: String(Config.dashboard.sizes.infoIconSize)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Date time width"
|
||||
value: String(Config.dashboard.sizes.dateTimeWidth)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Media width"
|
||||
value: String(Config.dashboard.sizes.mediaWidth)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Media progress sweep"
|
||||
value: String(Config.dashboard.sizes.mediaProgressSweep)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Media progress thickness"
|
||||
value: String(Config.dashboard.sizes.mediaProgressThickness)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Resource progress thickness"
|
||||
value: String(Config.dashboard.sizes.resourceProgessThickness)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Weather width"
|
||||
value: String(Config.dashboard.sizes.weatherWidth)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Media cover art size"
|
||||
value: String(Config.dashboard.sizes.mediaCoverArtSize)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Media visualiser size"
|
||||
value: String(Config.dashboard.sizes.mediaVisualiserSize)
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingReadOnly {
|
||||
name: "Resource size"
|
||||
value: String(Config.dashboard.sizes.resourceSize)
|
||||
}
|
||||
}
|
||||
// SettingsSection {
|
||||
// sectionId: "Layout Sizes"
|
||||
//
|
||||
// SettingsHeader {
|
||||
// name: "Layout Sizes"
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Tab indicator height"
|
||||
// value: String(Config.dashboard.sizes.tabIndicatorHeight)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Tab indicator spacing"
|
||||
// value: String(Config.dashboard.sizes.tabIndicatorSpacing)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Info width"
|
||||
// value: String(Config.dashboard.sizes.infoWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Info icon size"
|
||||
// value: String(Config.dashboard.sizes.infoIconSize)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Date time width"
|
||||
// value: String(Config.dashboard.sizes.dateTimeWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media width"
|
||||
// value: String(Config.dashboard.sizes.mediaWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media progress sweep"
|
||||
// value: String(Config.dashboard.sizes.mediaProgressSweep)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media progress thickness"
|
||||
// value: String(Config.dashboard.sizes.mediaProgressThickness)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Resource progress thickness"
|
||||
// value: String(Config.dashboard.sizes.resourceProgessThickness)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Weather width"
|
||||
// value: String(Config.dashboard.sizes.weatherWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media cover art size"
|
||||
// value: String(Config.dashboard.sizes.mediaCoverArtSize)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media visualiser size"
|
||||
// value: String(Config.dashboard.sizes.mediaVisualiserSize)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Resource size"
|
||||
// value: String(Config.dashboard.sizes.resourceSize)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -103,6 +103,18 @@ SettingsPage {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Greeter"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Greeter"
|
||||
}
|
||||
|
||||
SettingsIconButton {
|
||||
name: "Install wallpaper and color scheme to greeter"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Idle"
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import qs.Modules.Settings.Controls
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function addTimeoutEntry() {
|
||||
let list = [...Config.general.idle.timeouts];
|
||||
|
||||
@@ -40,8 +42,26 @@ ColumnLayout {
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: shouldBeActive ? implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
spacing: Appearance.spacing.smaller
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
name: "Idle Monitors"
|
||||
@@ -52,6 +72,8 @@ ColumnLayout {
|
||||
|
||||
SettingList {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
|
||||
onAddActiveActionRequested: {
|
||||
root.updateTimeoutEntry(index, "activeAction", "");
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Plugins"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Plugins"
|
||||
}
|
||||
|
||||
SettingBarEntryList {
|
||||
name: "Enable or disable plugins"
|
||||
object: Config.plugins
|
||||
setting: "entries"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Max toasts"
|
||||
min: 1
|
||||
name: "Max toasts"
|
||||
object: Config.utilities
|
||||
setting: "maxToasts"
|
||||
}
|
||||
@@ -29,8 +29,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Panel width"
|
||||
min: 1
|
||||
name: "Panel width"
|
||||
object: Config.utilities.sizes
|
||||
setting: "width"
|
||||
}
|
||||
@@ -39,8 +39,8 @@ SettingsPage {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Toast width"
|
||||
min: 1
|
||||
name: "Toast width"
|
||||
object: Config.utilities.sizes
|
||||
setting: "toastWidth"
|
||||
}
|
||||
@@ -77,100 +77,100 @@ SettingsPage {
|
||||
setting: "gameModeChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Do not disturb changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "dndChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Audio output changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "audioOutputChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Audio input changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "audioInputChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Caps lock changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "capsLockChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Num lock changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "numLockChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Keyboard layout changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "kbLayoutChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "VPN changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "vpnChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Now playing"
|
||||
object: Config.utilities.toasts
|
||||
setting: "nowPlaying"
|
||||
}
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Do not disturb changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "dndChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Audio output changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "audioOutputChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Audio input changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "audioInputChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Caps lock changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "capsLockChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Num lock changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "numLockChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Keyboard layout changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "kbLayoutChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "VPN changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "vpnChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Now playing"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "nowPlaying"
|
||||
// }
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "VPN"
|
||||
|
||||
SettingsHeader {
|
||||
name: "VPN"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable VPN integration"
|
||||
object: Config.utilities.vpn
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
name: "Provider"
|
||||
addLabel: qsTr("Add VPN provider")
|
||||
object: Config.utilities.vpn
|
||||
setting: "provider"
|
||||
}
|
||||
}
|
||||
// SettingsSection {
|
||||
// sectionId: "VPN"
|
||||
//
|
||||
// SettingsHeader {
|
||||
// name: "VPN"
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Enable VPN integration"
|
||||
// object: Config.utilities.vpn
|
||||
// setting: "enabled"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingStringList {
|
||||
// name: "Provider"
|
||||
// addLabel: qsTr("Add VPN provider")
|
||||
// object: Config.utilities.vpn
|
||||
// setting: "provider"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ Item {
|
||||
stack.push(screenshot);
|
||||
else if (currentCategory === "updates")
|
||||
stack.push(updates);
|
||||
else if (currentCategory === "plugins")
|
||||
stack.push(plugins);
|
||||
}
|
||||
|
||||
target: root
|
||||
@@ -134,7 +136,7 @@ Item {
|
||||
anchors.right: parent.right
|
||||
anchors.top: searchBar.bottom
|
||||
anchors.topMargin: Appearance.spacing.smaller
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
color: DynamicColors.tPalette.m3surfaceContainerLowest
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
StackView {
|
||||
@@ -245,4 +247,11 @@ Item {
|
||||
Cat.SystemUpdates {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: plugins
|
||||
|
||||
Cat.Plugins {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,9 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -207,6 +210,8 @@ ColumnLayout {
|
||||
StringListEditor {
|
||||
Layout.fillWidth: true
|
||||
addLabel: qsTr("Add command argument")
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
values: [...(modelData.command ?? [])]
|
||||
|
||||
onListEdited: function (values) {
|
||||
@@ -215,6 +220,9 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -233,6 +241,9 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
||||
@@ -6,7 +6,7 @@ import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
ColumnLayout {
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
@@ -43,10 +43,9 @@ ColumnLayout {
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: shouldBeActive ? implicitHeight : 0
|
||||
height: shouldBeActive ? layout.implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
spacing: Appearance.spacing.smaller
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
@@ -77,115 +76,128 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
Repeater {
|
||||
model: [...root.object[root.setting]]
|
||||
|
||||
Item {
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: layout.implicitHeight + Appearance.padding.smaller * 2
|
||||
|
||||
CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: -(Appearance.spacing.smaller / 2)
|
||||
color: DynamicColors.tPalette.m3outlineVariant
|
||||
implicitHeight: 1
|
||||
visible: index !== 0
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("From")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 33
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
text: modelData.from ?? ""
|
||||
|
||||
onEditingFinished: root.updateAlias(index, "from", text)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "delete"
|
||||
type: IconButton.Tonal
|
||||
|
||||
onClicked: root.removeAlias(index)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("To")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 33
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomTextField {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
text: modelData.to ?? ""
|
||||
|
||||
onEditingFinished: root.updateAlias(index, "to", text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "add"
|
||||
|
||||
onClicked: root.addAlias()
|
||||
}
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Add alias")
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [...root.object[root.setting]]
|
||||
|
||||
Item {
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: layout.implicitHeight + Appearance.padding.smaller * 2
|
||||
|
||||
CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: -(Appearance.spacing.smaller / 2)
|
||||
color: DynamicColors.tPalette.m3outlineVariant
|
||||
implicitHeight: 1
|
||||
visible: index !== 0
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("From")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: Math.max(Math.min(fromTextField.contentWidth + Appearance.padding.large * 2, 550), 50)
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
id: fromTextField
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
|
||||
text: modelData.from ?? ""
|
||||
|
||||
onEditingFinished: root.updateAlias(index, "from", text)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "delete"
|
||||
type: IconButton.Tonal
|
||||
|
||||
onClicked: root.removeAlias(index)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("To")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: Math.max(Math.min(toTextField.contentWidth + Appearance.padding.large * 2, 550), 50)
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
id: toTextField
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
|
||||
text: modelData.to ?? ""
|
||||
|
||||
onEditingFinished: root.updateAlias(index, "to", text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "add"
|
||||
|
||||
onClicked: root.addAlias()
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Add alias")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +194,9 @@ Item {
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -225,6 +228,9 @@ Item {
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
Item {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Paths
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias button: iButton
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
property bool shouldBeActive: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: iButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
icon: "download"
|
||||
|
||||
onClicked: {
|
||||
const lockBg = `${Paths.state}/lockscreen_bg.png`;
|
||||
const scheme = `${Paths.state}/scheme.json`;
|
||||
const face = `${Paths.home}/.face`;
|
||||
const destination = "/etc/zshell-greeter/images";
|
||||
Quickshell.execDetached(["pkexec", "sh", "-c", `mkdir -p ${destination}; cp ${lockBg} ${destination}; cp ${scheme} /etc/zshell-greeter; cp ${face} ${destination}`]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,473 +7,464 @@
|
||||
namespace ZShell::models {
|
||||
|
||||
FileSystemEntry::FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_fileInfo(path)
|
||||
, m_path(path)
|
||||
, m_relativePath(relativePath)
|
||||
, m_isImageInitialised(false)
|
||||
, m_mimeTypeInitialised(false) {}
|
||||
: QObject(parent)
|
||||
, m_fileInfo(path)
|
||||
, m_path(path)
|
||||
, m_relativePath(relativePath)
|
||||
, m_isImageInitialised(false)
|
||||
, m_mimeTypeInitialised(false) {
|
||||
}
|
||||
|
||||
QString FileSystemEntry::path() const {
|
||||
return m_path;
|
||||
return m_path;
|
||||
};
|
||||
|
||||
QString FileSystemEntry::relativePath() const {
|
||||
return m_relativePath;
|
||||
return m_relativePath;
|
||||
};
|
||||
|
||||
QString FileSystemEntry::name() const {
|
||||
return m_fileInfo.fileName();
|
||||
return m_fileInfo.fileName();
|
||||
};
|
||||
|
||||
QString FileSystemEntry::baseName() const {
|
||||
return m_fileInfo.baseName();
|
||||
return m_fileInfo.baseName();
|
||||
};
|
||||
|
||||
QString FileSystemEntry::parentDir() const {
|
||||
return m_fileInfo.absolutePath();
|
||||
return m_fileInfo.absolutePath();
|
||||
};
|
||||
|
||||
QString FileSystemEntry::suffix() const {
|
||||
return m_fileInfo.completeSuffix();
|
||||
return m_fileInfo.completeSuffix();
|
||||
};
|
||||
|
||||
qint64 FileSystemEntry::size() const {
|
||||
return m_fileInfo.size();
|
||||
return m_fileInfo.size();
|
||||
};
|
||||
|
||||
bool FileSystemEntry::isDir() const {
|
||||
return m_fileInfo.isDir();
|
||||
return m_fileInfo.isDir();
|
||||
};
|
||||
|
||||
bool FileSystemEntry::isImage() const {
|
||||
if (!m_isImageInitialised) {
|
||||
QImageReader reader(m_path);
|
||||
m_isImage = reader.canRead();
|
||||
m_isImageInitialised = true;
|
||||
}
|
||||
return m_isImage;
|
||||
if (!m_isImageInitialised) {
|
||||
QImageReader reader(m_path);
|
||||
m_isImage = reader.canRead();
|
||||
m_isImageInitialised = true;
|
||||
}
|
||||
return m_isImage;
|
||||
}
|
||||
|
||||
QString FileSystemEntry::mimeType() const {
|
||||
if (!m_mimeTypeInitialised) {
|
||||
const QMimeDatabase db;
|
||||
m_mimeType = db.mimeTypeForFile(m_path).name();
|
||||
m_mimeTypeInitialised = true;
|
||||
}
|
||||
return m_mimeType;
|
||||
if (!m_mimeTypeInitialised) {
|
||||
static const QMimeDatabase s_db;
|
||||
m_mimeType = s_db.mimeTypeForFile(m_path).name();
|
||||
m_mimeTypeInitialised = true;
|
||||
}
|
||||
return m_mimeType;
|
||||
}
|
||||
|
||||
void FileSystemEntry::updateRelativePath(const QDir& dir) {
|
||||
const auto relPath = dir.relativeFilePath(m_path);
|
||||
if (m_relativePath != relPath) {
|
||||
m_relativePath = relPath;
|
||||
emit relativePathChanged();
|
||||
}
|
||||
const auto relPath = dir.relativeFilePath(m_path);
|
||||
if (m_relativePath != relPath) {
|
||||
m_relativePath = relPath;
|
||||
emit relativePathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemModel::FileSystemModel(QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_recursive(false)
|
||||
, m_watchChanges(true)
|
||||
, m_showHidden(false)
|
||||
, m_filter(NoFilter) {
|
||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive);
|
||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateEntriesForDir);
|
||||
: QAbstractListModel(parent)
|
||||
, m_recursive(false)
|
||||
, m_watchChanges(true)
|
||||
, m_showHidden(false)
|
||||
, m_filter(NoFilter) {
|
||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive);
|
||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateEntriesForDir);
|
||||
}
|
||||
|
||||
int FileSystemModel::rowCount(const QModelIndex& parent) const {
|
||||
if (parent != QModelIndex()) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<int>(m_entries.size());
|
||||
if (parent != QModelIndex()) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<int>(m_entries.size());
|
||||
}
|
||||
|
||||
QVariant FileSystemModel::data(const QModelIndex& index, int role) const {
|
||||
if (role != Qt::UserRole || !index.isValid() || index.row() >= m_entries.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
return QVariant::fromValue(m_entries.at(index.row()));
|
||||
if (role != Qt::UserRole || !index.isValid() || index.row() >= m_entries.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
return QVariant::fromValue(m_entries.at(index.row()));
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> FileSystemModel::roleNames() const {
|
||||
return { { Qt::UserRole, "modelData" } };
|
||||
return { { Qt::UserRole, "modelData" } };
|
||||
}
|
||||
|
||||
QString FileSystemModel::path() const {
|
||||
return m_path;
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void FileSystemModel::setPath(const QString& path) {
|
||||
if (m_path == path) {
|
||||
return;
|
||||
}
|
||||
if (m_path == path) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_path = path;
|
||||
emit pathChanged();
|
||||
m_path = path;
|
||||
emit pathChanged();
|
||||
|
||||
m_dir.setPath(m_path);
|
||||
m_dir.setPath(m_path);
|
||||
|
||||
for (const auto& entry : std::as_const(m_entries)) {
|
||||
entry->updateRelativePath(m_dir);
|
||||
}
|
||||
for (const auto& entry : std::as_const(m_entries)) {
|
||||
entry->updateRelativePath(m_dir);
|
||||
}
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
bool FileSystemModel::recursive() const {
|
||||
return m_recursive;
|
||||
return m_recursive;
|
||||
}
|
||||
|
||||
void FileSystemModel::setRecursive(bool recursive) {
|
||||
if (m_recursive == recursive) {
|
||||
return;
|
||||
}
|
||||
if (m_recursive == recursive) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_recursive = recursive;
|
||||
emit recursiveChanged();
|
||||
m_recursive = recursive;
|
||||
emit recursiveChanged();
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
bool FileSystemModel::watchChanges() const {
|
||||
return m_watchChanges;
|
||||
return m_watchChanges;
|
||||
}
|
||||
|
||||
void FileSystemModel::setWatchChanges(bool watchChanges) {
|
||||
if (m_watchChanges == watchChanges) {
|
||||
return;
|
||||
}
|
||||
if (m_watchChanges == watchChanges) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_watchChanges = watchChanges;
|
||||
emit watchChangesChanged();
|
||||
m_watchChanges = watchChanges;
|
||||
emit watchChangesChanged();
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
bool FileSystemModel::showHidden() const {
|
||||
return m_showHidden;
|
||||
return m_showHidden;
|
||||
}
|
||||
|
||||
void FileSystemModel::setShowHidden(bool showHidden) {
|
||||
if (m_showHidden == showHidden) {
|
||||
return;
|
||||
}
|
||||
if (m_showHidden == showHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_showHidden = showHidden;
|
||||
emit showHiddenChanged();
|
||||
m_showHidden = showHidden;
|
||||
emit showHiddenChanged();
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
bool FileSystemModel::sortReverse() const {
|
||||
return m_sortReverse;
|
||||
return m_sortReverse;
|
||||
}
|
||||
|
||||
void FileSystemModel::setSortReverse(bool sortReverse) {
|
||||
if (m_sortReverse == sortReverse) {
|
||||
return;
|
||||
}
|
||||
if (m_sortReverse == sortReverse) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sortReverse = sortReverse;
|
||||
emit sortReverseChanged();
|
||||
m_sortReverse = sortReverse;
|
||||
emit sortReverseChanged();
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
FileSystemModel::Filter FileSystemModel::filter() const {
|
||||
return m_filter;
|
||||
return m_filter;
|
||||
}
|
||||
|
||||
void FileSystemModel::setFilter(Filter filter) {
|
||||
if (m_filter == filter) {
|
||||
return;
|
||||
}
|
||||
if (m_filter == filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_filter = filter;
|
||||
emit filterChanged();
|
||||
m_filter = filter;
|
||||
emit filterChanged();
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
QStringList FileSystemModel::nameFilters() const {
|
||||
return m_nameFilters;
|
||||
return m_nameFilters;
|
||||
}
|
||||
|
||||
void FileSystemModel::setNameFilters(const QStringList& nameFilters) {
|
||||
if (m_nameFilters == nameFilters) {
|
||||
return;
|
||||
}
|
||||
if (m_nameFilters == nameFilters) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_nameFilters = nameFilters;
|
||||
emit nameFiltersChanged();
|
||||
m_nameFilters = nameFilters;
|
||||
emit nameFiltersChanged();
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
QQmlListProperty<FileSystemEntry> FileSystemModel::entries() {
|
||||
return QQmlListProperty<FileSystemEntry>(this, &m_entries);
|
||||
return QQmlListProperty<FileSystemEntry>(this, &m_entries);
|
||||
}
|
||||
|
||||
void FileSystemModel::watchDirIfRecursive(const QString& path) {
|
||||
if (m_recursive && m_watchChanges) {
|
||||
const auto currentDir = m_dir;
|
||||
const bool showHidden = m_showHidden;
|
||||
const auto future = QtConcurrent::run([showHidden, path]() {
|
||||
QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
||||
if (showHidden) {
|
||||
filters |= QDir::Hidden;
|
||||
}
|
||||
if (m_recursive && m_watchChanges) {
|
||||
const auto currentDir = m_dir;
|
||||
const bool showHidden = m_showHidden;
|
||||
auto future = QtConcurrent::run([showHidden, path]() {
|
||||
QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
||||
if (showHidden) {
|
||||
filters |= QDir::Hidden;
|
||||
}
|
||||
|
||||
QDirIterator iter(path, filters, QDirIterator::Subdirectories);
|
||||
QStringList dirs;
|
||||
while (iter.hasNext()) {
|
||||
dirs << iter.next();
|
||||
}
|
||||
return dirs;
|
||||
});
|
||||
const auto watcher = new QFutureWatcher<QStringList>(this);
|
||||
connect(watcher, &QFutureWatcher<QStringList>::finished, this, [currentDir, showHidden, watcher, this]() {
|
||||
const auto paths = watcher->result();
|
||||
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) {
|
||||
// Ignore if dir or showHidden has changed
|
||||
m_watcher.addPaths(paths);
|
||||
}
|
||||
watcher->deleteLater();
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
}
|
||||
QDirIterator iter(path, filters, QDirIterator::Subdirectories);
|
||||
QStringList dirs;
|
||||
while (iter.hasNext()) {
|
||||
dirs << iter.next();
|
||||
}
|
||||
return dirs;
|
||||
});
|
||||
future.then(this, [currentDir, showHidden, this](const QStringList& paths) {
|
||||
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) {
|
||||
// Ignore if dir or showHidden has changed
|
||||
m_watcher.addPaths(paths);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemModel::update() {
|
||||
updateWatcher();
|
||||
updateEntries();
|
||||
updateWatcher();
|
||||
updateEntries();
|
||||
}
|
||||
|
||||
void FileSystemModel::updateWatcher() {
|
||||
if (!m_watcher.directories().isEmpty()) {
|
||||
m_watcher.removePaths(m_watcher.directories());
|
||||
}
|
||||
if (!m_watcher.directories().isEmpty()) {
|
||||
m_watcher.removePaths(m_watcher.directories());
|
||||
}
|
||||
|
||||
if (!m_watchChanges || m_path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!m_watchChanges || m_path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_watcher.addPath(m_path);
|
||||
watchDirIfRecursive(m_path);
|
||||
m_watcher.addPath(m_path);
|
||||
watchDirIfRecursive(m_path);
|
||||
}
|
||||
|
||||
void FileSystemModel::updateEntries() {
|
||||
if (m_path.isEmpty()) {
|
||||
if (!m_entries.isEmpty()) {
|
||||
beginResetModel();
|
||||
qDeleteAll(m_entries);
|
||||
m_entries.clear();
|
||||
endResetModel();
|
||||
emit entriesChanged();
|
||||
}
|
||||
if (m_path.isEmpty()) {
|
||||
if (!m_entries.isEmpty()) {
|
||||
beginResetModel();
|
||||
qDeleteAll(m_entries);
|
||||
m_entries.clear();
|
||||
endResetModel();
|
||||
emit entriesChanged();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& future : m_futures) {
|
||||
future.cancel();
|
||||
}
|
||||
m_futures.clear();
|
||||
for (auto& future : m_futures) {
|
||||
future.cancel();
|
||||
}
|
||||
m_futures.clear();
|
||||
|
||||
updateEntriesForDir(m_path);
|
||||
updateEntriesForDir(m_path);
|
||||
}
|
||||
|
||||
void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
||||
const auto recursive = m_recursive;
|
||||
const auto showHidden = m_showHidden;
|
||||
const auto filter = m_filter;
|
||||
const auto nameFilters = m_nameFilters;
|
||||
const auto recursive = m_recursive;
|
||||
const auto showHidden = m_showHidden;
|
||||
const auto filter = m_filter;
|
||||
const auto nameFilters = m_nameFilters;
|
||||
|
||||
QSet<QString> oldPaths;
|
||||
for (const auto& entry : std::as_const(m_entries)) {
|
||||
oldPaths << entry->path();
|
||||
}
|
||||
QSet<QString> oldPaths;
|
||||
for (const auto& entry : std::as_const(m_entries)) {
|
||||
oldPaths << entry->path();
|
||||
}
|
||||
|
||||
const auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
|
||||
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
||||
auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString> > >& promise) {
|
||||
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
||||
|
||||
std::optional<QDirIterator> iter;
|
||||
std::optional<QDirIterator> iter;
|
||||
|
||||
if (filter == Images) {
|
||||
QStringList extraNameFilters = nameFilters;
|
||||
const auto formats = QImageReader::supportedImageFormats();
|
||||
for (const auto& format : formats) {
|
||||
extraNameFilters << "*." + format;
|
||||
}
|
||||
if (filter == Images) {
|
||||
QStringList extraNameFilters = nameFilters;
|
||||
const auto formats = QImageReader::supportedImageFormats();
|
||||
for (const auto& format : formats) {
|
||||
extraNameFilters << "*." + format;
|
||||
}
|
||||
|
||||
QDir::Filters filters = QDir::Files;
|
||||
if (showHidden) {
|
||||
filters |= QDir::Hidden;
|
||||
}
|
||||
QDir::Filters filters = QDir::Files;
|
||||
if (showHidden) {
|
||||
filters |= QDir::Hidden;
|
||||
}
|
||||
|
||||
iter.emplace(dir, extraNameFilters, filters, flags);
|
||||
} else {
|
||||
QDir::Filters filters;
|
||||
iter.emplace(dir, extraNameFilters, filters, flags);
|
||||
} else {
|
||||
QDir::Filters filters;
|
||||
|
||||
if (filter == Files) {
|
||||
filters = QDir::Files;
|
||||
} else if (filter == Dirs) {
|
||||
filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
||||
} else {
|
||||
filters = QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot;
|
||||
}
|
||||
if (filter == Files) {
|
||||
filters = QDir::Files;
|
||||
} else if (filter == Dirs) {
|
||||
filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
||||
} else {
|
||||
filters = QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot;
|
||||
}
|
||||
|
||||
if (showHidden) {
|
||||
filters |= QDir::Hidden;
|
||||
}
|
||||
if (showHidden) {
|
||||
filters |= QDir::Hidden;
|
||||
}
|
||||
|
||||
if (nameFilters.isEmpty()) {
|
||||
iter.emplace(dir, filters, flags);
|
||||
} else {
|
||||
iter.emplace(dir, nameFilters, filters, flags);
|
||||
}
|
||||
}
|
||||
if (nameFilters.isEmpty()) {
|
||||
iter.emplace(dir, filters, flags);
|
||||
} else {
|
||||
iter.emplace(dir, nameFilters, filters, flags);
|
||||
}
|
||||
}
|
||||
|
||||
QSet<QString> newPaths;
|
||||
while (iter->hasNext()) {
|
||||
if (promise.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
QSet<QString> newPaths;
|
||||
while (iter->hasNext()) {
|
||||
if (promise.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = iter->next();
|
||||
QString path = iter->next();
|
||||
|
||||
if (filter == Images) {
|
||||
QImageReader reader(path);
|
||||
if (!reader.canRead()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (filter == Images) {
|
||||
QImageReader reader(path);
|
||||
if (!reader.canRead()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
newPaths.insert(path);
|
||||
}
|
||||
newPaths.insert(path);
|
||||
}
|
||||
|
||||
if (promise.isCanceled() || newPaths == oldPaths) {
|
||||
return;
|
||||
}
|
||||
if (promise.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths));
|
||||
});
|
||||
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths));
|
||||
});
|
||||
|
||||
if (m_futures.contains(dir)) {
|
||||
m_futures[dir].cancel();
|
||||
}
|
||||
m_futures.insert(dir, future);
|
||||
if (m_futures.contains(dir)) {
|
||||
m_futures[dir].cancel();
|
||||
}
|
||||
m_futures.insert(dir, future);
|
||||
|
||||
const auto watcher = new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this);
|
||||
|
||||
connect(watcher, &QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished, this, [dir, watcher, this]() {
|
||||
m_futures.remove(dir);
|
||||
|
||||
if (!watcher->future().isResultReadyAt(0)) {
|
||||
watcher->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = watcher->result();
|
||||
applyChanges(result.first, result.second);
|
||||
|
||||
watcher->deleteLater();
|
||||
});
|
||||
|
||||
watcher->setFuture(future);
|
||||
future
|
||||
.then(this,
|
||||
[dir, this](QPair<QSet<QString>, QSet<QString> > result) {
|
||||
m_futures.remove(dir);
|
||||
if (!result.first.isEmpty() || !result.second.isEmpty()) {
|
||||
applyChanges(result.first, result.second);
|
||||
}
|
||||
})
|
||||
.onCanceled(this, [dir, this]() {
|
||||
m_futures.remove(dir);
|
||||
});
|
||||
}
|
||||
|
||||
void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
|
||||
QList<int> removedIndices;
|
||||
for (int i = 0; i < m_entries.size(); ++i) {
|
||||
if (removedPaths.contains(m_entries[i]->path())) {
|
||||
removedIndices << i;
|
||||
}
|
||||
}
|
||||
std::sort(removedIndices.begin(), removedIndices.end(), std::greater<int>());
|
||||
QList<int> removedIndices;
|
||||
for (int i = 0; i < m_entries.size(); ++i) {
|
||||
if (removedPaths.contains(m_entries[i]->path())) {
|
||||
removedIndices << i;
|
||||
}
|
||||
}
|
||||
std::sort(removedIndices.begin(), removedIndices.end(), std::greater<int>());
|
||||
|
||||
// Batch remove old entries
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
for (int idx : std::as_const(removedIndices)) {
|
||||
if (start == -1) {
|
||||
start = idx;
|
||||
end = idx;
|
||||
} else if (idx == end - 1) {
|
||||
end = idx;
|
||||
} else {
|
||||
beginRemoveRows(QModelIndex(), end, start);
|
||||
for (int i = start; i >= end; --i) {
|
||||
m_entries.takeAt(i)->deleteLater();
|
||||
}
|
||||
endRemoveRows();
|
||||
// Batch remove old entries
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
for (int idx : std::as_const(removedIndices)) {
|
||||
if (start == -1) {
|
||||
start = idx;
|
||||
end = idx;
|
||||
} else if (idx == end - 1) {
|
||||
end = idx;
|
||||
} else {
|
||||
beginRemoveRows(QModelIndex(), end, start);
|
||||
for (int i = start; i >= end; --i) {
|
||||
m_entries.takeAt(i)->deleteLater();
|
||||
}
|
||||
endRemoveRows();
|
||||
|
||||
start = idx;
|
||||
end = idx;
|
||||
}
|
||||
}
|
||||
if (start != -1) {
|
||||
beginRemoveRows(QModelIndex(), end, start);
|
||||
for (int i = start; i >= end; --i) {
|
||||
m_entries.takeAt(i)->deleteLater();
|
||||
}
|
||||
endRemoveRows();
|
||||
}
|
||||
start = idx;
|
||||
end = idx;
|
||||
}
|
||||
}
|
||||
if (start != -1) {
|
||||
beginRemoveRows(QModelIndex(), end, start);
|
||||
for (int i = start; i >= end; --i) {
|
||||
m_entries.takeAt(i)->deleteLater();
|
||||
}
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
// Create new entries
|
||||
QList<FileSystemEntry*> newEntries;
|
||||
for (const auto& path : addedPaths) {
|
||||
newEntries << new FileSystemEntry(path, m_dir.relativeFilePath(path), this);
|
||||
}
|
||||
std::sort(newEntries.begin(), newEntries.end(), [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||
return compareEntries(a, b);
|
||||
});
|
||||
// Create new entries
|
||||
QList<FileSystemEntry*> newEntries;
|
||||
for (const auto& path : addedPaths) {
|
||||
newEntries << new FileSystemEntry(path, m_dir.relativeFilePath(path), this);
|
||||
}
|
||||
std::sort(newEntries.begin(), newEntries.end(), [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||
return compareEntries(a, b);
|
||||
});
|
||||
|
||||
// Batch insert new entries
|
||||
int insertStart = -1;
|
||||
QList<FileSystemEntry*> batchItems;
|
||||
for (const auto& entry : std::as_const(newEntries)) {
|
||||
const auto it = std::lower_bound(
|
||||
m_entries.begin(), m_entries.end(), entry, [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||
return compareEntries(a, b);
|
||||
});
|
||||
const auto row = static_cast<int>(it - m_entries.begin());
|
||||
// Batch insert new entries
|
||||
int insertStart = -1;
|
||||
QList<FileSystemEntry*> batchItems;
|
||||
for (const auto& entry : std::as_const(newEntries)) {
|
||||
const auto it = std::lower_bound(
|
||||
m_entries.begin(), m_entries.end(), entry, [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||
return compareEntries(a, b);
|
||||
});
|
||||
const auto row = static_cast<int>(it - m_entries.begin());
|
||||
|
||||
if (insertStart == -1) {
|
||||
insertStart = row;
|
||||
batchItems << entry;
|
||||
} else if (row == insertStart + batchItems.size()) {
|
||||
batchItems << entry;
|
||||
} else {
|
||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||
for (int i = 0; i < batchItems.size(); ++i) {
|
||||
m_entries.insert(insertStart + i, batchItems[i]);
|
||||
}
|
||||
endInsertRows();
|
||||
if (insertStart == -1) {
|
||||
insertStart = row;
|
||||
batchItems << entry;
|
||||
} else if (row == insertStart + batchItems.size()) {
|
||||
batchItems << entry;
|
||||
} else {
|
||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||
for (int i = 0; i < batchItems.size(); ++i) {
|
||||
m_entries.insert(insertStart + i, batchItems[i]);
|
||||
}
|
||||
endInsertRows();
|
||||
|
||||
insertStart = row;
|
||||
batchItems.clear();
|
||||
batchItems << entry;
|
||||
}
|
||||
}
|
||||
if (!batchItems.isEmpty()) {
|
||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||
for (int i = 0; i < batchItems.size(); ++i) {
|
||||
m_entries.insert(insertStart + i, batchItems[i]);
|
||||
}
|
||||
endInsertRows();
|
||||
}
|
||||
insertStart = row;
|
||||
batchItems.clear();
|
||||
batchItems << entry;
|
||||
}
|
||||
}
|
||||
if (!batchItems.isEmpty()) {
|
||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||
for (int i = 0; i < batchItems.size(); ++i) {
|
||||
m_entries.insert(insertStart + i, batchItems[i]);
|
||||
}
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
emit entriesChanged();
|
||||
emit entriesChanged();
|
||||
}
|
||||
|
||||
bool FileSystemModel::compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const {
|
||||
if (a->isDir() != b->isDir()) {
|
||||
return m_sortReverse ^ a->isDir();
|
||||
}
|
||||
const auto cmp = a->relativePath().localeAwareCompare(b->relativePath());
|
||||
return m_sortReverse ? cmp > 0 : cmp < 0;
|
||||
if (a->isDir() != b->isDir()) {
|
||||
return m_sortReverse ^ a->isDir();
|
||||
}
|
||||
const auto cmp = a->relativePath().localeAwareCompare(b->relativePath());
|
||||
return m_sortReverse ? cmp > 0 : cmp < 0;
|
||||
}
|
||||
|
||||
} // namespace ZShell::models
|
||||
|
||||
@@ -13,136 +13,136 @@
|
||||
namespace ZShell::models {
|
||||
|
||||
class FileSystemEntry : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
|
||||
|
||||
Q_PROPERTY(QString path READ path CONSTANT)
|
||||
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(QString baseName READ baseName CONSTANT)
|
||||
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
|
||||
Q_PROPERTY(QString suffix READ suffix CONSTANT)
|
||||
Q_PROPERTY(qint64 size READ size CONSTANT)
|
||||
Q_PROPERTY(bool isDir READ isDir CONSTANT)
|
||||
Q_PROPERTY(bool isImage READ isImage CONSTANT)
|
||||
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
||||
Q_PROPERTY(QString path READ path CONSTANT)
|
||||
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(QString baseName READ baseName CONSTANT)
|
||||
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
|
||||
Q_PROPERTY(QString suffix READ suffix CONSTANT)
|
||||
Q_PROPERTY(qint64 size READ size CONSTANT)
|
||||
Q_PROPERTY(bool isDir READ isDir CONSTANT)
|
||||
Q_PROPERTY(bool isImage READ isImage CONSTANT)
|
||||
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
||||
|
||||
public:
|
||||
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
|
||||
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
|
||||
|
||||
[[nodiscard]] QString path() const;
|
||||
[[nodiscard]] QString relativePath() const;
|
||||
[[nodiscard]] QString name() const;
|
||||
[[nodiscard]] QString baseName() const;
|
||||
[[nodiscard]] QString parentDir() const;
|
||||
[[nodiscard]] QString suffix() const;
|
||||
[[nodiscard]] qint64 size() const;
|
||||
[[nodiscard]] bool isDir() const;
|
||||
[[nodiscard]] bool isImage() const;
|
||||
[[nodiscard]] QString mimeType() const;
|
||||
[[nodiscard]] QString path() const;
|
||||
[[nodiscard]] QString relativePath() const;
|
||||
[[nodiscard]] QString name() const;
|
||||
[[nodiscard]] QString baseName() const;
|
||||
[[nodiscard]] QString parentDir() const;
|
||||
[[nodiscard]] QString suffix() const;
|
||||
[[nodiscard]] qint64 size() const;
|
||||
[[nodiscard]] bool isDir() const;
|
||||
[[nodiscard]] bool isImage() const;
|
||||
[[nodiscard]] QString mimeType() const;
|
||||
|
||||
void updateRelativePath(const QDir& dir);
|
||||
void updateRelativePath(const QDir& dir);
|
||||
|
||||
signals:
|
||||
void relativePathChanged();
|
||||
void relativePathChanged();
|
||||
|
||||
private:
|
||||
const QFileInfo m_fileInfo;
|
||||
const QFileInfo m_fileInfo;
|
||||
|
||||
const QString m_path;
|
||||
QString m_relativePath;
|
||||
const QString m_path;
|
||||
QString m_relativePath;
|
||||
|
||||
mutable bool m_isImage;
|
||||
mutable bool m_isImageInitialised;
|
||||
mutable bool m_isImage;
|
||||
mutable bool m_isImageInitialised;
|
||||
|
||||
mutable QString m_mimeType;
|
||||
mutable bool m_mimeTypeInitialised;
|
||||
mutable QString m_mimeType;
|
||||
mutable bool m_mimeTypeInitialised;
|
||||
};
|
||||
|
||||
class FileSystemModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
||||
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
|
||||
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
|
||||
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
|
||||
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
||||
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
||||
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
|
||||
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
|
||||
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
|
||||
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
||||
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
|
||||
|
||||
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged)
|
||||
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged)
|
||||
|
||||
public:
|
||||
enum Filter {
|
||||
NoFilter,
|
||||
Images,
|
||||
Files,
|
||||
Dirs
|
||||
};
|
||||
Q_ENUM(Filter)
|
||||
enum Filter {
|
||||
NoFilter,
|
||||
Images,
|
||||
Files,
|
||||
Dirs
|
||||
};
|
||||
Q_ENUM(Filter)
|
||||
|
||||
explicit FileSystemModel(QObject* parent = nullptr);
|
||||
explicit FileSystemModel(QObject* parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
[[nodiscard]] QString path() const;
|
||||
void setPath(const QString& path);
|
||||
[[nodiscard]] QString path() const;
|
||||
void setPath(const QString& path);
|
||||
|
||||
[[nodiscard]] bool recursive() const;
|
||||
void setRecursive(bool recursive);
|
||||
[[nodiscard]] bool recursive() const;
|
||||
void setRecursive(bool recursive);
|
||||
|
||||
[[nodiscard]] bool watchChanges() const;
|
||||
void setWatchChanges(bool watchChanges);
|
||||
[[nodiscard]] bool watchChanges() const;
|
||||
void setWatchChanges(bool watchChanges);
|
||||
|
||||
[[nodiscard]] bool showHidden() const;
|
||||
void setShowHidden(bool showHidden);
|
||||
[[nodiscard]] bool showHidden() const;
|
||||
void setShowHidden(bool showHidden);
|
||||
|
||||
[[nodiscard]] bool sortReverse() const;
|
||||
void setSortReverse(bool sortReverse);
|
||||
[[nodiscard]] bool sortReverse() const;
|
||||
void setSortReverse(bool sortReverse);
|
||||
|
||||
[[nodiscard]] Filter filter() const;
|
||||
void setFilter(Filter filter);
|
||||
[[nodiscard]] Filter filter() const;
|
||||
void setFilter(Filter filter);
|
||||
|
||||
[[nodiscard]] QStringList nameFilters() const;
|
||||
void setNameFilters(const QStringList& nameFilters);
|
||||
[[nodiscard]] QStringList nameFilters() const;
|
||||
void setNameFilters(const QStringList& nameFilters);
|
||||
|
||||
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
|
||||
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
|
||||
|
||||
signals:
|
||||
void pathChanged();
|
||||
void recursiveChanged();
|
||||
void watchChangesChanged();
|
||||
void showHiddenChanged();
|
||||
void sortReverseChanged();
|
||||
void filterChanged();
|
||||
void nameFiltersChanged();
|
||||
void entriesChanged();
|
||||
void pathChanged();
|
||||
void recursiveChanged();
|
||||
void watchChangesChanged();
|
||||
void showHiddenChanged();
|
||||
void sortReverseChanged();
|
||||
void filterChanged();
|
||||
void nameFiltersChanged();
|
||||
void entriesChanged();
|
||||
|
||||
private:
|
||||
QDir m_dir;
|
||||
QFileSystemWatcher m_watcher;
|
||||
QList<FileSystemEntry*> m_entries;
|
||||
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString>>>> m_futures;
|
||||
QDir m_dir;
|
||||
QFileSystemWatcher m_watcher;
|
||||
QList<FileSystemEntry*> m_entries;
|
||||
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString> > > > m_futures;
|
||||
|
||||
QString m_path;
|
||||
bool m_recursive;
|
||||
bool m_watchChanges;
|
||||
bool m_showHidden;
|
||||
bool m_sortReverse;
|
||||
Filter m_filter;
|
||||
QStringList m_nameFilters;
|
||||
QString m_path;
|
||||
bool m_recursive;
|
||||
bool m_watchChanges;
|
||||
bool m_showHidden;
|
||||
bool m_sortReverse = false;
|
||||
Filter m_filter;
|
||||
QStringList m_nameFilters;
|
||||
|
||||
void watchDirIfRecursive(const QString& path);
|
||||
void update();
|
||||
void updateWatcher();
|
||||
void updateEntries();
|
||||
void updateEntriesForDir(const QString& dir);
|
||||
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
||||
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
|
||||
void watchDirIfRecursive(const QString& path);
|
||||
void update();
|
||||
void updateWatcher();
|
||||
void updateEntries();
|
||||
void updateEntriesForDir(const QString& dir);
|
||||
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
||||
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
|
||||
};
|
||||
|
||||
} // namespace ZShell::models
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
//@ pragma UseQApplication
|
||||
//@ pragma Env QSG_RENDER_LOOP=threaded
|
||||
// @ pragma Env QSG_RHI_BACKEND=vulkan
|
||||
//@ pragma Env QSG_RHI_BACKEND=vulkan
|
||||
//@ pragma Env QSG_NO_VSYNC=1
|
||||
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
||||
//@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round
|
||||
//@ pragma DropExpensiveFonts
|
||||
import Quickshell
|
||||
import qs.Extensions
|
||||
import qs.Modules
|
||||
import qs.Modules.Wallpaper
|
||||
import qs.Modules.Lock
|
||||
@@ -14,6 +15,8 @@ import qs.Helpers
|
||||
import qs.Modules.Polkit
|
||||
|
||||
ShellRoot {
|
||||
settings.watchFiles: true
|
||||
|
||||
Windows {
|
||||
}
|
||||
|
||||
@@ -36,4 +39,7 @@ ShellRoot {
|
||||
|
||||
Polkit {
|
||||
}
|
||||
|
||||
LoadExtensions {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user