Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad23da4eda | |||
| 06ebc4ffbf | |||
| f2f9fa1302 | |||
| b4020438f9 |
+8
-1
@@ -31,6 +31,13 @@ if("shell" IN_LIST ENABLE_MODULES)
|
|||||||
foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths)
|
foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths)
|
||||||
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
|
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
|
||||||
endforeach()
|
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}")
|
install(DIRECTORY Greeter/ DESTINATION "${INSTALL_GREETERCONFDIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Text {
|
|||||||
color: DynamicColors.palette.m3onSurface
|
color: DynamicColors.palette.m3onSurface
|
||||||
font.family: Appearance.font.family.sans
|
font.family: Appearance.font.family.sans
|
||||||
font.pointSize: Appearance.font.size.normal
|
font.pointSize: Appearance.font.size.normal
|
||||||
|
linkColor: DynamicColors.palette.m3onPrimaryFixedVariant
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -22,6 +22,7 @@ Singleton {
|
|||||||
property alias notifs: adapter.notifs
|
property alias notifs: adapter.notifs
|
||||||
property alias osd: adapter.osd
|
property alias osd: adapter.osd
|
||||||
property alias overview: adapter.overview
|
property alias overview: adapter.overview
|
||||||
|
property alias plugins: adapter.plugins
|
||||||
property bool recentlySaved: false
|
property bool recentlySaved: false
|
||||||
property alias screenshot: adapter.screenshot
|
property alias screenshot: adapter.screenshot
|
||||||
property alias services: adapter.services
|
property alias services: adapter.services
|
||||||
@@ -140,7 +141,8 @@ Singleton {
|
|||||||
launcher: serializeLauncher(),
|
launcher: serializeLauncher(),
|
||||||
colors: serializeColors(),
|
colors: serializeColors(),
|
||||||
dock: serializeDock(),
|
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 {
|
function serializeScreenshot(): var {
|
||||||
return {
|
return {
|
||||||
enable_pp: screenshot.enable_pp,
|
enable_pp: screenshot.enable_pp,
|
||||||
@@ -458,6 +467,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
property Overview overview: Overview {
|
property Overview overview: Overview {
|
||||||
}
|
}
|
||||||
|
property PluginConfig plugins: PluginConfig {
|
||||||
|
}
|
||||||
property Screenshot screenshot: Screenshot {
|
property Screenshot screenshot: Screenshot {
|
||||||
}
|
}
|
||||||
property Services services: Services {
|
property Services services: Services {
|
||||||
|
|||||||
@@ -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/fzf.js" as Fzf
|
||||||
import "../scripts/fuzzysort.js" as Fuzzy
|
import "../scripts/fuzzysort.js" as Fuzzy
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
property var extraOpts: ({})
|
property var extraOpts: ({})
|
||||||
|
|||||||
@@ -136,7 +136,10 @@ CustomRect {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
|
|
||||||
onLinkActivated: link => {
|
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;
|
root.visibilities.sidebar = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,12 +100,14 @@ Item {
|
|||||||
icon: `brightness_${(Math.round(value * 6) + 1)}`
|
icon: `brightness_${(Math.round(value * 6) + 1)}`
|
||||||
value: root.brightness
|
value: root.brightness
|
||||||
|
|
||||||
onMoved: {
|
onPressedChanged: {
|
||||||
if (Config.osd.allMonBrightness) {
|
if (!pressed) {
|
||||||
root.monitor?.setBrightness(value);
|
if (Config.osd.allMonBrightness) {
|
||||||
} else {
|
for (const mon of Brightness.monitors) {
|
||||||
for (const mon of Brightness.monitors) {
|
mon.setBrightness(value);
|
||||||
mon.setBrightness(value);
|
}
|
||||||
|
} else {
|
||||||
|
root.monitor?.setBrightness(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,12 @@ Item {
|
|||||||
key: "updates"
|
key: "updates"
|
||||||
name: "Updates"
|
name: "Updates"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
icon: "extension"
|
||||||
|
key: "plugins"
|
||||||
|
name: "Extensions"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomClippingRect {
|
CustomClippingRect {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Media update interval"
|
|
||||||
min: 0
|
min: 0
|
||||||
|
name: "Media update interval"
|
||||||
object: Config.dashboard
|
object: Config.dashboard
|
||||||
setting: "mediaUpdateInterval"
|
setting: "mediaUpdateInterval"
|
||||||
step: 50
|
step: 50
|
||||||
@@ -30,8 +30,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Resource update interval"
|
|
||||||
min: 0
|
min: 0
|
||||||
|
name: "Resource update interval"
|
||||||
object: Config.dashboard
|
object: Config.dashboard
|
||||||
setting: "resourceUpdateInterval"
|
setting: "resourceUpdateInterval"
|
||||||
step: 50
|
step: 50
|
||||||
@@ -41,8 +41,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Drag threshold"
|
|
||||||
min: 0
|
min: 0
|
||||||
|
name: "Drag threshold"
|
||||||
object: Config.dashboard
|
object: Config.dashboard
|
||||||
setting: "dragThreshold"
|
setting: "dragThreshold"
|
||||||
}
|
}
|
||||||
@@ -107,112 +107,112 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsSection {
|
// SettingsSection {
|
||||||
sectionId: "Layout Sizes"
|
// sectionId: "Layout Sizes"
|
||||||
|
//
|
||||||
SettingsHeader {
|
// SettingsHeader {
|
||||||
name: "Layout Sizes"
|
// name: "Layout Sizes"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Tab indicator height"
|
// name: "Tab indicator height"
|
||||||
value: String(Config.dashboard.sizes.tabIndicatorHeight)
|
// value: String(Config.dashboard.sizes.tabIndicatorHeight)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Tab indicator spacing"
|
// name: "Tab indicator spacing"
|
||||||
value: String(Config.dashboard.sizes.tabIndicatorSpacing)
|
// value: String(Config.dashboard.sizes.tabIndicatorSpacing)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Info width"
|
// name: "Info width"
|
||||||
value: String(Config.dashboard.sizes.infoWidth)
|
// value: String(Config.dashboard.sizes.infoWidth)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Info icon size"
|
// name: "Info icon size"
|
||||||
value: String(Config.dashboard.sizes.infoIconSize)
|
// value: String(Config.dashboard.sizes.infoIconSize)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Date time width"
|
// name: "Date time width"
|
||||||
value: String(Config.dashboard.sizes.dateTimeWidth)
|
// value: String(Config.dashboard.sizes.dateTimeWidth)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Media width"
|
// name: "Media width"
|
||||||
value: String(Config.dashboard.sizes.mediaWidth)
|
// value: String(Config.dashboard.sizes.mediaWidth)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Media progress sweep"
|
// name: "Media progress sweep"
|
||||||
value: String(Config.dashboard.sizes.mediaProgressSweep)
|
// value: String(Config.dashboard.sizes.mediaProgressSweep)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Media progress thickness"
|
// name: "Media progress thickness"
|
||||||
value: String(Config.dashboard.sizes.mediaProgressThickness)
|
// value: String(Config.dashboard.sizes.mediaProgressThickness)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Resource progress thickness"
|
// name: "Resource progress thickness"
|
||||||
value: String(Config.dashboard.sizes.resourceProgessThickness)
|
// value: String(Config.dashboard.sizes.resourceProgessThickness)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Weather width"
|
// name: "Weather width"
|
||||||
value: String(Config.dashboard.sizes.weatherWidth)
|
// value: String(Config.dashboard.sizes.weatherWidth)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Media cover art size"
|
// name: "Media cover art size"
|
||||||
value: String(Config.dashboard.sizes.mediaCoverArtSize)
|
// value: String(Config.dashboard.sizes.mediaCoverArtSize)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Media visualiser size"
|
// name: "Media visualiser size"
|
||||||
value: String(Config.dashboard.sizes.mediaVisualiserSize)
|
// value: String(Config.dashboard.sizes.mediaVisualiserSize)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingReadOnly {
|
// SettingReadOnly {
|
||||||
name: "Resource size"
|
// name: "Resource size"
|
||||||
value: String(Config.dashboard.sizes.resourceSize)
|
// value: String(Config.dashboard.sizes.resourceSize)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ ColumnLayout {
|
|||||||
|
|
||||||
SettingList {
|
SettingList {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.right: undefined
|
||||||
|
|
||||||
onAddActiveActionRequested: {
|
onAddActiveActionRequested: {
|
||||||
root.updateTimeoutEntry(index, "activeAction", "");
|
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 {
|
SettingSpinBox {
|
||||||
name: "Max toasts"
|
|
||||||
min: 1
|
min: 1
|
||||||
|
name: "Max toasts"
|
||||||
object: Config.utilities
|
object: Config.utilities
|
||||||
setting: "maxToasts"
|
setting: "maxToasts"
|
||||||
}
|
}
|
||||||
@@ -29,8 +29,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Panel width"
|
|
||||||
min: 1
|
min: 1
|
||||||
|
name: "Panel width"
|
||||||
object: Config.utilities.sizes
|
object: Config.utilities.sizes
|
||||||
setting: "width"
|
setting: "width"
|
||||||
}
|
}
|
||||||
@@ -39,8 +39,8 @@ SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingSpinBox {
|
SettingSpinBox {
|
||||||
name: "Toast width"
|
|
||||||
min: 1
|
min: 1
|
||||||
|
name: "Toast width"
|
||||||
object: Config.utilities.sizes
|
object: Config.utilities.sizes
|
||||||
setting: "toastWidth"
|
setting: "toastWidth"
|
||||||
}
|
}
|
||||||
@@ -77,100 +77,100 @@ SettingsPage {
|
|||||||
setting: "gameModeChanged"
|
setting: "gameModeChanged"
|
||||||
}
|
}
|
||||||
|
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Do not disturb changed"
|
// name: "Do not disturb changed"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "dndChanged"
|
// setting: "dndChanged"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Audio output changed"
|
// name: "Audio output changed"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "audioOutputChanged"
|
// setting: "audioOutputChanged"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Audio input changed"
|
// name: "Audio input changed"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "audioInputChanged"
|
// setting: "audioInputChanged"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Caps lock changed"
|
// name: "Caps lock changed"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "capsLockChanged"
|
// setting: "capsLockChanged"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Num lock changed"
|
// name: "Num lock changed"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "numLockChanged"
|
// setting: "numLockChanged"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Keyboard layout changed"
|
// name: "Keyboard layout changed"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "kbLayoutChanged"
|
// setting: "kbLayoutChanged"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "VPN changed"
|
// name: "VPN changed"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "vpnChanged"
|
// setting: "vpnChanged"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Now playing"
|
// name: "Now playing"
|
||||||
object: Config.utilities.toasts
|
// object: Config.utilities.toasts
|
||||||
setting: "nowPlaying"
|
// setting: "nowPlaying"
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsSection {
|
// SettingsSection {
|
||||||
sectionId: "VPN"
|
// sectionId: "VPN"
|
||||||
|
//
|
||||||
SettingsHeader {
|
// SettingsHeader {
|
||||||
name: "VPN"
|
// name: "VPN"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingSwitch {
|
// SettingSwitch {
|
||||||
name: "Enable VPN integration"
|
// name: "Enable VPN integration"
|
||||||
object: Config.utilities.vpn
|
// object: Config.utilities.vpn
|
||||||
setting: "enabled"
|
// setting: "enabled"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Separator {
|
// Separator {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
SettingStringList {
|
// SettingStringList {
|
||||||
name: "Provider"
|
// name: "Provider"
|
||||||
addLabel: qsTr("Add VPN provider")
|
// addLabel: qsTr("Add VPN provider")
|
||||||
object: Config.utilities.vpn
|
// object: Config.utilities.vpn
|
||||||
setting: "provider"
|
// setting: "provider"
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ Item {
|
|||||||
stack.push(screenshot);
|
stack.push(screenshot);
|
||||||
else if (currentCategory === "updates")
|
else if (currentCategory === "updates")
|
||||||
stack.push(updates);
|
stack.push(updates);
|
||||||
|
else if (currentCategory === "plugins")
|
||||||
|
stack.push(plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
target: root
|
target: root
|
||||||
@@ -245,4 +247,11 @@ Item {
|
|||||||
Cat.SystemUpdates {
|
Cat.SystemUpdates {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: plugins
|
||||||
|
|
||||||
|
Cat.Plugins {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,9 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Separator {
|
Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.right: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -207,6 +210,8 @@ ColumnLayout {
|
|||||||
StringListEditor {
|
StringListEditor {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
addLabel: qsTr("Add command argument")
|
addLabel: qsTr("Add command argument")
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.right: undefined
|
||||||
values: [...(modelData.command ?? [])]
|
values: [...(modelData.command ?? [])]
|
||||||
|
|
||||||
onListEdited: function (values) {
|
onListEdited: function (values) {
|
||||||
@@ -215,6 +220,9 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Separator {
|
Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.right: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -233,6 +241,9 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Separator {
|
Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.right: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import qs.Components
|
|||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
|
|
||||||
ColumnLayout {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||||
@@ -43,10 +43,9 @@ ColumnLayout {
|
|||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
height: shouldBeActive ? implicitHeight : 0
|
height: shouldBeActive ? layout.implicitHeight : 0
|
||||||
opacity: shouldBeActive ? 1 : 0
|
opacity: shouldBeActive ? 1 : 0
|
||||||
scale: shouldBeActive ? 1 : 0.8
|
scale: shouldBeActive ? 1 : 0.8
|
||||||
spacing: Appearance.spacing.smaller
|
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
@@ -77,115 +76,128 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomText {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
id: layout
|
||||||
font.pointSize: Appearance.font.size.larger
|
|
||||||
text: root.name
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
anchors.left: parent.left
|
||||||
model: [...root.object[root.setting]]
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
Item {
|
spacing: Appearance.spacing.smaller
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
CustomText {
|
||||||
Layout.fillWidth: true
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,473 +7,464 @@
|
|||||||
namespace ZShell::models {
|
namespace ZShell::models {
|
||||||
|
|
||||||
FileSystemEntry::FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent)
|
FileSystemEntry::FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_fileInfo(path)
|
, m_fileInfo(path)
|
||||||
, m_path(path)
|
, m_path(path)
|
||||||
, m_relativePath(relativePath)
|
, m_relativePath(relativePath)
|
||||||
, m_isImageInitialised(false)
|
, m_isImageInitialised(false)
|
||||||
, m_mimeTypeInitialised(false) {}
|
, m_mimeTypeInitialised(false) {
|
||||||
|
}
|
||||||
|
|
||||||
QString FileSystemEntry::path() const {
|
QString FileSystemEntry::path() const {
|
||||||
return m_path;
|
return m_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString FileSystemEntry::relativePath() const {
|
QString FileSystemEntry::relativePath() const {
|
||||||
return m_relativePath;
|
return m_relativePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString FileSystemEntry::name() const {
|
QString FileSystemEntry::name() const {
|
||||||
return m_fileInfo.fileName();
|
return m_fileInfo.fileName();
|
||||||
};
|
};
|
||||||
|
|
||||||
QString FileSystemEntry::baseName() const {
|
QString FileSystemEntry::baseName() const {
|
||||||
return m_fileInfo.baseName();
|
return m_fileInfo.baseName();
|
||||||
};
|
};
|
||||||
|
|
||||||
QString FileSystemEntry::parentDir() const {
|
QString FileSystemEntry::parentDir() const {
|
||||||
return m_fileInfo.absolutePath();
|
return m_fileInfo.absolutePath();
|
||||||
};
|
};
|
||||||
|
|
||||||
QString FileSystemEntry::suffix() const {
|
QString FileSystemEntry::suffix() const {
|
||||||
return m_fileInfo.completeSuffix();
|
return m_fileInfo.completeSuffix();
|
||||||
};
|
};
|
||||||
|
|
||||||
qint64 FileSystemEntry::size() const {
|
qint64 FileSystemEntry::size() const {
|
||||||
return m_fileInfo.size();
|
return m_fileInfo.size();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FileSystemEntry::isDir() const {
|
bool FileSystemEntry::isDir() const {
|
||||||
return m_fileInfo.isDir();
|
return m_fileInfo.isDir();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FileSystemEntry::isImage() const {
|
bool FileSystemEntry::isImage() const {
|
||||||
if (!m_isImageInitialised) {
|
if (!m_isImageInitialised) {
|
||||||
QImageReader reader(m_path);
|
QImageReader reader(m_path);
|
||||||
m_isImage = reader.canRead();
|
m_isImage = reader.canRead();
|
||||||
m_isImageInitialised = true;
|
m_isImageInitialised = true;
|
||||||
}
|
}
|
||||||
return m_isImage;
|
return m_isImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileSystemEntry::mimeType() const {
|
QString FileSystemEntry::mimeType() const {
|
||||||
if (!m_mimeTypeInitialised) {
|
if (!m_mimeTypeInitialised) {
|
||||||
const QMimeDatabase db;
|
static const QMimeDatabase s_db;
|
||||||
m_mimeType = db.mimeTypeForFile(m_path).name();
|
m_mimeType = s_db.mimeTypeForFile(m_path).name();
|
||||||
m_mimeTypeInitialised = true;
|
m_mimeTypeInitialised = true;
|
||||||
}
|
}
|
||||||
return m_mimeType;
|
return m_mimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemEntry::updateRelativePath(const QDir& dir) {
|
void FileSystemEntry::updateRelativePath(const QDir& dir) {
|
||||||
const auto relPath = dir.relativeFilePath(m_path);
|
const auto relPath = dir.relativeFilePath(m_path);
|
||||||
if (m_relativePath != relPath) {
|
if (m_relativePath != relPath) {
|
||||||
m_relativePath = relPath;
|
m_relativePath = relPath;
|
||||||
emit relativePathChanged();
|
emit relativePathChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystemModel::FileSystemModel(QObject* parent)
|
FileSystemModel::FileSystemModel(QObject* parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, m_recursive(false)
|
, m_recursive(false)
|
||||||
, m_watchChanges(true)
|
, m_watchChanges(true)
|
||||||
, m_showHidden(false)
|
, m_showHidden(false)
|
||||||
, m_filter(NoFilter) {
|
, m_filter(NoFilter) {
|
||||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive);
|
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive);
|
||||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateEntriesForDir);
|
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateEntriesForDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystemModel::rowCount(const QModelIndex& parent) const {
|
int FileSystemModel::rowCount(const QModelIndex& parent) const {
|
||||||
if (parent != QModelIndex()) {
|
if (parent != QModelIndex()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return static_cast<int>(m_entries.size());
|
return static_cast<int>(m_entries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant FileSystemModel::data(const QModelIndex& index, int role) const {
|
QVariant FileSystemModel::data(const QModelIndex& index, int role) const {
|
||||||
if (role != Qt::UserRole || !index.isValid() || index.row() >= m_entries.size()) {
|
if (role != Qt::UserRole || !index.isValid() || index.row() >= m_entries.size()) {
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
return QVariant::fromValue(m_entries.at(index.row()));
|
return QVariant::fromValue(m_entries.at(index.row()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> FileSystemModel::roleNames() const {
|
QHash<int, QByteArray> FileSystemModel::roleNames() const {
|
||||||
return { { Qt::UserRole, "modelData" } };
|
return { { Qt::UserRole, "modelData" } };
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileSystemModel::path() const {
|
QString FileSystemModel::path() const {
|
||||||
return m_path;
|
return m_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::setPath(const QString& path) {
|
void FileSystemModel::setPath(const QString& path) {
|
||||||
if (m_path == path) {
|
if (m_path == path) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_path = path;
|
m_path = path;
|
||||||
emit pathChanged();
|
emit pathChanged();
|
||||||
|
|
||||||
m_dir.setPath(m_path);
|
m_dir.setPath(m_path);
|
||||||
|
|
||||||
for (const auto& entry : std::as_const(m_entries)) {
|
for (const auto& entry : std::as_const(m_entries)) {
|
||||||
entry->updateRelativePath(m_dir);
|
entry->updateRelativePath(m_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystemModel::recursive() const {
|
bool FileSystemModel::recursive() const {
|
||||||
return m_recursive;
|
return m_recursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::setRecursive(bool recursive) {
|
void FileSystemModel::setRecursive(bool recursive) {
|
||||||
if (m_recursive == recursive) {
|
if (m_recursive == recursive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_recursive = recursive;
|
m_recursive = recursive;
|
||||||
emit recursiveChanged();
|
emit recursiveChanged();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystemModel::watchChanges() const {
|
bool FileSystemModel::watchChanges() const {
|
||||||
return m_watchChanges;
|
return m_watchChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::setWatchChanges(bool watchChanges) {
|
void FileSystemModel::setWatchChanges(bool watchChanges) {
|
||||||
if (m_watchChanges == watchChanges) {
|
if (m_watchChanges == watchChanges) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_watchChanges = watchChanges;
|
m_watchChanges = watchChanges;
|
||||||
emit watchChangesChanged();
|
emit watchChangesChanged();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystemModel::showHidden() const {
|
bool FileSystemModel::showHidden() const {
|
||||||
return m_showHidden;
|
return m_showHidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::setShowHidden(bool showHidden) {
|
void FileSystemModel::setShowHidden(bool showHidden) {
|
||||||
if (m_showHidden == showHidden) {
|
if (m_showHidden == showHidden) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_showHidden = showHidden;
|
m_showHidden = showHidden;
|
||||||
emit showHiddenChanged();
|
emit showHiddenChanged();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystemModel::sortReverse() const {
|
bool FileSystemModel::sortReverse() const {
|
||||||
return m_sortReverse;
|
return m_sortReverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::setSortReverse(bool sortReverse) {
|
void FileSystemModel::setSortReverse(bool sortReverse) {
|
||||||
if (m_sortReverse == sortReverse) {
|
if (m_sortReverse == sortReverse) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sortReverse = sortReverse;
|
m_sortReverse = sortReverse;
|
||||||
emit sortReverseChanged();
|
emit sortReverseChanged();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystemModel::Filter FileSystemModel::filter() const {
|
FileSystemModel::Filter FileSystemModel::filter() const {
|
||||||
return m_filter;
|
return m_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::setFilter(Filter filter) {
|
void FileSystemModel::setFilter(Filter filter) {
|
||||||
if (m_filter == filter) {
|
if (m_filter == filter) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_filter = filter;
|
m_filter = filter;
|
||||||
emit filterChanged();
|
emit filterChanged();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList FileSystemModel::nameFilters() const {
|
QStringList FileSystemModel::nameFilters() const {
|
||||||
return m_nameFilters;
|
return m_nameFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::setNameFilters(const QStringList& nameFilters) {
|
void FileSystemModel::setNameFilters(const QStringList& nameFilters) {
|
||||||
if (m_nameFilters == nameFilters) {
|
if (m_nameFilters == nameFilters) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nameFilters = nameFilters;
|
m_nameFilters = nameFilters;
|
||||||
emit nameFiltersChanged();
|
emit nameFiltersChanged();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<FileSystemEntry> FileSystemModel::entries() {
|
QQmlListProperty<FileSystemEntry> FileSystemModel::entries() {
|
||||||
return QQmlListProperty<FileSystemEntry>(this, &m_entries);
|
return QQmlListProperty<FileSystemEntry>(this, &m_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::watchDirIfRecursive(const QString& path) {
|
void FileSystemModel::watchDirIfRecursive(const QString& path) {
|
||||||
if (m_recursive && m_watchChanges) {
|
if (m_recursive && m_watchChanges) {
|
||||||
const auto currentDir = m_dir;
|
const auto currentDir = m_dir;
|
||||||
const bool showHidden = m_showHidden;
|
const bool showHidden = m_showHidden;
|
||||||
const auto future = QtConcurrent::run([showHidden, path]() {
|
auto future = QtConcurrent::run([showHidden, path]() {
|
||||||
QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
||||||
if (showHidden) {
|
if (showHidden) {
|
||||||
filters |= QDir::Hidden;
|
filters |= QDir::Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDirIterator iter(path, filters, QDirIterator::Subdirectories);
|
QDirIterator iter(path, filters, QDirIterator::Subdirectories);
|
||||||
QStringList dirs;
|
QStringList dirs;
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
dirs << iter.next();
|
dirs << iter.next();
|
||||||
}
|
}
|
||||||
return dirs;
|
return dirs;
|
||||||
});
|
});
|
||||||
const auto watcher = new QFutureWatcher<QStringList>(this);
|
future.then(this, [currentDir, showHidden, this](const QStringList& paths) {
|
||||||
connect(watcher, &QFutureWatcher<QStringList>::finished, this, [currentDir, showHidden, watcher, this]() {
|
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) {
|
||||||
const auto paths = watcher->result();
|
// Ignore if dir or showHidden has changed
|
||||||
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) {
|
m_watcher.addPaths(paths);
|
||||||
// Ignore if dir or showHidden has changed
|
}
|
||||||
m_watcher.addPaths(paths);
|
});
|
||||||
}
|
}
|
||||||
watcher->deleteLater();
|
|
||||||
});
|
|
||||||
watcher->setFuture(future);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::update() {
|
void FileSystemModel::update() {
|
||||||
updateWatcher();
|
updateWatcher();
|
||||||
updateEntries();
|
updateEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::updateWatcher() {
|
void FileSystemModel::updateWatcher() {
|
||||||
if (!m_watcher.directories().isEmpty()) {
|
if (!m_watcher.directories().isEmpty()) {
|
||||||
m_watcher.removePaths(m_watcher.directories());
|
m_watcher.removePaths(m_watcher.directories());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_watchChanges || m_path.isEmpty()) {
|
if (!m_watchChanges || m_path.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_watcher.addPath(m_path);
|
m_watcher.addPath(m_path);
|
||||||
watchDirIfRecursive(m_path);
|
watchDirIfRecursive(m_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::updateEntries() {
|
void FileSystemModel::updateEntries() {
|
||||||
if (m_path.isEmpty()) {
|
if (m_path.isEmpty()) {
|
||||||
if (!m_entries.isEmpty()) {
|
if (!m_entries.isEmpty()) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
qDeleteAll(m_entries);
|
qDeleteAll(m_entries);
|
||||||
m_entries.clear();
|
m_entries.clear();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
emit entriesChanged();
|
emit entriesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& future : m_futures) {
|
for (auto& future : m_futures) {
|
||||||
future.cancel();
|
future.cancel();
|
||||||
}
|
}
|
||||||
m_futures.clear();
|
m_futures.clear();
|
||||||
|
|
||||||
updateEntriesForDir(m_path);
|
updateEntriesForDir(m_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
||||||
const auto recursive = m_recursive;
|
const auto recursive = m_recursive;
|
||||||
const auto showHidden = m_showHidden;
|
const auto showHidden = m_showHidden;
|
||||||
const auto filter = m_filter;
|
const auto filter = m_filter;
|
||||||
const auto nameFilters = m_nameFilters;
|
const auto nameFilters = m_nameFilters;
|
||||||
|
|
||||||
QSet<QString> oldPaths;
|
QSet<QString> oldPaths;
|
||||||
for (const auto& entry : std::as_const(m_entries)) {
|
for (const auto& entry : std::as_const(m_entries)) {
|
||||||
oldPaths << entry->path();
|
oldPaths << entry->path();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
|
auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString> > >& promise) {
|
||||||
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
||||||
|
|
||||||
std::optional<QDirIterator> iter;
|
std::optional<QDirIterator> iter;
|
||||||
|
|
||||||
if (filter == Images) {
|
if (filter == Images) {
|
||||||
QStringList extraNameFilters = nameFilters;
|
QStringList extraNameFilters = nameFilters;
|
||||||
const auto formats = QImageReader::supportedImageFormats();
|
const auto formats = QImageReader::supportedImageFormats();
|
||||||
for (const auto& format : formats) {
|
for (const auto& format : formats) {
|
||||||
extraNameFilters << "*." + format;
|
extraNameFilters << "*." + format;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir::Filters filters = QDir::Files;
|
QDir::Filters filters = QDir::Files;
|
||||||
if (showHidden) {
|
if (showHidden) {
|
||||||
filters |= QDir::Hidden;
|
filters |= QDir::Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
iter.emplace(dir, extraNameFilters, filters, flags);
|
iter.emplace(dir, extraNameFilters, filters, flags);
|
||||||
} else {
|
} else {
|
||||||
QDir::Filters filters;
|
QDir::Filters filters;
|
||||||
|
|
||||||
if (filter == Files) {
|
if (filter == Files) {
|
||||||
filters = QDir::Files;
|
filters = QDir::Files;
|
||||||
} else if (filter == Dirs) {
|
} else if (filter == Dirs) {
|
||||||
filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
||||||
} else {
|
} else {
|
||||||
filters = QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot;
|
filters = QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showHidden) {
|
if (showHidden) {
|
||||||
filters |= QDir::Hidden;
|
filters |= QDir::Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nameFilters.isEmpty()) {
|
if (nameFilters.isEmpty()) {
|
||||||
iter.emplace(dir, filters, flags);
|
iter.emplace(dir, filters, flags);
|
||||||
} else {
|
} else {
|
||||||
iter.emplace(dir, nameFilters, filters, flags);
|
iter.emplace(dir, nameFilters, filters, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSet<QString> newPaths;
|
QSet<QString> newPaths;
|
||||||
while (iter->hasNext()) {
|
while (iter->hasNext()) {
|
||||||
if (promise.isCanceled()) {
|
if (promise.isCanceled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString path = iter->next();
|
QString path = iter->next();
|
||||||
|
|
||||||
if (filter == Images) {
|
if (filter == Images) {
|
||||||
QImageReader reader(path);
|
QImageReader reader(path);
|
||||||
if (!reader.canRead()) {
|
if (!reader.canRead()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newPaths.insert(path);
|
newPaths.insert(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promise.isCanceled() || newPaths == oldPaths) {
|
if (promise.isCanceled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths));
|
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_futures.contains(dir)) {
|
if (m_futures.contains(dir)) {
|
||||||
m_futures[dir].cancel();
|
m_futures[dir].cancel();
|
||||||
}
|
}
|
||||||
m_futures.insert(dir, future);
|
m_futures.insert(dir, future);
|
||||||
|
|
||||||
const auto watcher = new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this);
|
future
|
||||||
|
.then(this,
|
||||||
connect(watcher, &QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished, this, [dir, watcher, this]() {
|
[dir, this](QPair<QSet<QString>, QSet<QString> > result) {
|
||||||
m_futures.remove(dir);
|
m_futures.remove(dir);
|
||||||
|
if (!result.first.isEmpty() || !result.second.isEmpty()) {
|
||||||
if (!watcher->future().isResultReadyAt(0)) {
|
applyChanges(result.first, result.second);
|
||||||
watcher->deleteLater();
|
}
|
||||||
return;
|
})
|
||||||
}
|
.onCanceled(this, [dir, this]() {
|
||||||
|
m_futures.remove(dir);
|
||||||
const auto result = watcher->result();
|
});
|
||||||
applyChanges(result.first, result.second);
|
|
||||||
|
|
||||||
watcher->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher->setFuture(future);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
|
void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
|
||||||
QList<int> removedIndices;
|
QList<int> removedIndices;
|
||||||
for (int i = 0; i < m_entries.size(); ++i) {
|
for (int i = 0; i < m_entries.size(); ++i) {
|
||||||
if (removedPaths.contains(m_entries[i]->path())) {
|
if (removedPaths.contains(m_entries[i]->path())) {
|
||||||
removedIndices << i;
|
removedIndices << i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::sort(removedIndices.begin(), removedIndices.end(), std::greater<int>());
|
std::sort(removedIndices.begin(), removedIndices.end(), std::greater<int>());
|
||||||
|
|
||||||
// Batch remove old entries
|
// Batch remove old entries
|
||||||
int start = -1;
|
int start = -1;
|
||||||
int end = -1;
|
int end = -1;
|
||||||
for (int idx : std::as_const(removedIndices)) {
|
for (int idx : std::as_const(removedIndices)) {
|
||||||
if (start == -1) {
|
if (start == -1) {
|
||||||
start = idx;
|
start = idx;
|
||||||
end = idx;
|
end = idx;
|
||||||
} else if (idx == end - 1) {
|
} else if (idx == end - 1) {
|
||||||
end = idx;
|
end = idx;
|
||||||
} else {
|
} else {
|
||||||
beginRemoveRows(QModelIndex(), end, start);
|
beginRemoveRows(QModelIndex(), end, start);
|
||||||
for (int i = start; i >= end; --i) {
|
for (int i = start; i >= end; --i) {
|
||||||
m_entries.takeAt(i)->deleteLater();
|
m_entries.takeAt(i)->deleteLater();
|
||||||
}
|
}
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
|
|
||||||
start = idx;
|
start = idx;
|
||||||
end = idx;
|
end = idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (start != -1) {
|
if (start != -1) {
|
||||||
beginRemoveRows(QModelIndex(), end, start);
|
beginRemoveRows(QModelIndex(), end, start);
|
||||||
for (int i = start; i >= end; --i) {
|
for (int i = start; i >= end; --i) {
|
||||||
m_entries.takeAt(i)->deleteLater();
|
m_entries.takeAt(i)->deleteLater();
|
||||||
}
|
}
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new entries
|
// Create new entries
|
||||||
QList<FileSystemEntry*> newEntries;
|
QList<FileSystemEntry*> newEntries;
|
||||||
for (const auto& path : addedPaths) {
|
for (const auto& path : addedPaths) {
|
||||||
newEntries << new FileSystemEntry(path, m_dir.relativeFilePath(path), this);
|
newEntries << new FileSystemEntry(path, m_dir.relativeFilePath(path), this);
|
||||||
}
|
}
|
||||||
std::sort(newEntries.begin(), newEntries.end(), [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
std::sort(newEntries.begin(), newEntries.end(), [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||||
return compareEntries(a, b);
|
return compareEntries(a, b);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Batch insert new entries
|
// Batch insert new entries
|
||||||
int insertStart = -1;
|
int insertStart = -1;
|
||||||
QList<FileSystemEntry*> batchItems;
|
QList<FileSystemEntry*> batchItems;
|
||||||
for (const auto& entry : std::as_const(newEntries)) {
|
for (const auto& entry : std::as_const(newEntries)) {
|
||||||
const auto it = std::lower_bound(
|
const auto it = std::lower_bound(
|
||||||
m_entries.begin(), m_entries.end(), entry, [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
m_entries.begin(), m_entries.end(), entry, [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||||
return compareEntries(a, b);
|
return compareEntries(a, b);
|
||||||
});
|
});
|
||||||
const auto row = static_cast<int>(it - m_entries.begin());
|
const auto row = static_cast<int>(it - m_entries.begin());
|
||||||
|
|
||||||
if (insertStart == -1) {
|
if (insertStart == -1) {
|
||||||
insertStart = row;
|
insertStart = row;
|
||||||
batchItems << entry;
|
batchItems << entry;
|
||||||
} else if (row == insertStart + batchItems.size()) {
|
} else if (row == insertStart + batchItems.size()) {
|
||||||
batchItems << entry;
|
batchItems << entry;
|
||||||
} else {
|
} else {
|
||||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||||
for (int i = 0; i < batchItems.size(); ++i) {
|
for (int i = 0; i < batchItems.size(); ++i) {
|
||||||
m_entries.insert(insertStart + i, batchItems[i]);
|
m_entries.insert(insertStart + i, batchItems[i]);
|
||||||
}
|
}
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
|
||||||
insertStart = row;
|
insertStart = row;
|
||||||
batchItems.clear();
|
batchItems.clear();
|
||||||
batchItems << entry;
|
batchItems << entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!batchItems.isEmpty()) {
|
if (!batchItems.isEmpty()) {
|
||||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||||
for (int i = 0; i < batchItems.size(); ++i) {
|
for (int i = 0; i < batchItems.size(); ++i) {
|
||||||
m_entries.insert(insertStart + i, batchItems[i]);
|
m_entries.insert(insertStart + i, batchItems[i]);
|
||||||
}
|
}
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit entriesChanged();
|
emit entriesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystemModel::compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const {
|
bool FileSystemModel::compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const {
|
||||||
if (a->isDir() != b->isDir()) {
|
if (a->isDir() != b->isDir()) {
|
||||||
return m_sortReverse ^ a->isDir();
|
return m_sortReverse ^ a->isDir();
|
||||||
}
|
}
|
||||||
const auto cmp = a->relativePath().localeAwareCompare(b->relativePath());
|
const auto cmp = a->relativePath().localeAwareCompare(b->relativePath());
|
||||||
return m_sortReverse ? cmp > 0 : cmp < 0;
|
return m_sortReverse ? cmp > 0 : cmp < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZShell::models
|
} // namespace ZShell::models
|
||||||
|
|||||||
@@ -13,136 +13,136 @@
|
|||||||
namespace ZShell::models {
|
namespace ZShell::models {
|
||||||
|
|
||||||
class FileSystemEntry : public QObject {
|
class FileSystemEntry : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
|
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path CONSTANT)
|
Q_PROPERTY(QString path READ path CONSTANT)
|
||||||
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
||||||
Q_PROPERTY(QString name READ name CONSTANT)
|
Q_PROPERTY(QString name READ name CONSTANT)
|
||||||
Q_PROPERTY(QString baseName READ baseName CONSTANT)
|
Q_PROPERTY(QString baseName READ baseName CONSTANT)
|
||||||
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
|
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
|
||||||
Q_PROPERTY(QString suffix READ suffix CONSTANT)
|
Q_PROPERTY(QString suffix READ suffix CONSTANT)
|
||||||
Q_PROPERTY(qint64 size READ size CONSTANT)
|
Q_PROPERTY(qint64 size READ size CONSTANT)
|
||||||
Q_PROPERTY(bool isDir READ isDir CONSTANT)
|
Q_PROPERTY(bool isDir READ isDir CONSTANT)
|
||||||
Q_PROPERTY(bool isImage READ isImage CONSTANT)
|
Q_PROPERTY(bool isImage READ isImage CONSTANT)
|
||||||
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
||||||
|
|
||||||
public:
|
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 path() const;
|
||||||
[[nodiscard]] QString relativePath() const;
|
[[nodiscard]] QString relativePath() const;
|
||||||
[[nodiscard]] QString name() const;
|
[[nodiscard]] QString name() const;
|
||||||
[[nodiscard]] QString baseName() const;
|
[[nodiscard]] QString baseName() const;
|
||||||
[[nodiscard]] QString parentDir() const;
|
[[nodiscard]] QString parentDir() const;
|
||||||
[[nodiscard]] QString suffix() const;
|
[[nodiscard]] QString suffix() const;
|
||||||
[[nodiscard]] qint64 size() const;
|
[[nodiscard]] qint64 size() const;
|
||||||
[[nodiscard]] bool isDir() const;
|
[[nodiscard]] bool isDir() const;
|
||||||
[[nodiscard]] bool isImage() const;
|
[[nodiscard]] bool isImage() const;
|
||||||
[[nodiscard]] QString mimeType() const;
|
[[nodiscard]] QString mimeType() const;
|
||||||
|
|
||||||
void updateRelativePath(const QDir& dir);
|
void updateRelativePath(const QDir& dir);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void relativePathChanged();
|
void relativePathChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QFileInfo m_fileInfo;
|
const QFileInfo m_fileInfo;
|
||||||
|
|
||||||
const QString m_path;
|
const QString m_path;
|
||||||
QString m_relativePath;
|
QString m_relativePath;
|
||||||
|
|
||||||
mutable bool m_isImage;
|
mutable bool m_isImage;
|
||||||
mutable bool m_isImageInitialised;
|
mutable bool m_isImageInitialised;
|
||||||
|
|
||||||
mutable QString m_mimeType;
|
mutable QString m_mimeType;
|
||||||
mutable bool m_mimeTypeInitialised;
|
mutable bool m_mimeTypeInitialised;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileSystemModel : public QAbstractListModel {
|
class FileSystemModel : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||||
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
||||||
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
|
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
|
||||||
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
|
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
|
||||||
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
|
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
|
||||||
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
||||||
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
|
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:
|
public:
|
||||||
enum Filter {
|
enum Filter {
|
||||||
NoFilter,
|
NoFilter,
|
||||||
Images,
|
Images,
|
||||||
Files,
|
Files,
|
||||||
Dirs
|
Dirs
|
||||||
};
|
};
|
||||||
Q_ENUM(Filter)
|
Q_ENUM(Filter)
|
||||||
|
|
||||||
explicit FileSystemModel(QObject* parent = nullptr);
|
explicit FileSystemModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
[[nodiscard]] QString path() const;
|
[[nodiscard]] QString path() const;
|
||||||
void setPath(const QString& path);
|
void setPath(const QString& path);
|
||||||
|
|
||||||
[[nodiscard]] bool recursive() const;
|
[[nodiscard]] bool recursive() const;
|
||||||
void setRecursive(bool recursive);
|
void setRecursive(bool recursive);
|
||||||
|
|
||||||
[[nodiscard]] bool watchChanges() const;
|
[[nodiscard]] bool watchChanges() const;
|
||||||
void setWatchChanges(bool watchChanges);
|
void setWatchChanges(bool watchChanges);
|
||||||
|
|
||||||
[[nodiscard]] bool showHidden() const;
|
[[nodiscard]] bool showHidden() const;
|
||||||
void setShowHidden(bool showHidden);
|
void setShowHidden(bool showHidden);
|
||||||
|
|
||||||
[[nodiscard]] bool sortReverse() const;
|
[[nodiscard]] bool sortReverse() const;
|
||||||
void setSortReverse(bool sortReverse);
|
void setSortReverse(bool sortReverse);
|
||||||
|
|
||||||
[[nodiscard]] Filter filter() const;
|
[[nodiscard]] Filter filter() const;
|
||||||
void setFilter(Filter filter);
|
void setFilter(Filter filter);
|
||||||
|
|
||||||
[[nodiscard]] QStringList nameFilters() const;
|
[[nodiscard]] QStringList nameFilters() const;
|
||||||
void setNameFilters(const QStringList& nameFilters);
|
void setNameFilters(const QStringList& nameFilters);
|
||||||
|
|
||||||
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
|
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pathChanged();
|
void pathChanged();
|
||||||
void recursiveChanged();
|
void recursiveChanged();
|
||||||
void watchChangesChanged();
|
void watchChangesChanged();
|
||||||
void showHiddenChanged();
|
void showHiddenChanged();
|
||||||
void sortReverseChanged();
|
void sortReverseChanged();
|
||||||
void filterChanged();
|
void filterChanged();
|
||||||
void nameFiltersChanged();
|
void nameFiltersChanged();
|
||||||
void entriesChanged();
|
void entriesChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
QFileSystemWatcher m_watcher;
|
QFileSystemWatcher m_watcher;
|
||||||
QList<FileSystemEntry*> m_entries;
|
QList<FileSystemEntry*> m_entries;
|
||||||
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString>>>> m_futures;
|
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString> > > > m_futures;
|
||||||
|
|
||||||
QString m_path;
|
QString m_path;
|
||||||
bool m_recursive;
|
bool m_recursive;
|
||||||
bool m_watchChanges;
|
bool m_watchChanges;
|
||||||
bool m_showHidden;
|
bool m_showHidden;
|
||||||
bool m_sortReverse;
|
bool m_sortReverse = false;
|
||||||
Filter m_filter;
|
Filter m_filter;
|
||||||
QStringList m_nameFilters;
|
QStringList m_nameFilters;
|
||||||
|
|
||||||
void watchDirIfRecursive(const QString& path);
|
void watchDirIfRecursive(const QString& path);
|
||||||
void update();
|
void update();
|
||||||
void updateWatcher();
|
void updateWatcher();
|
||||||
void updateEntries();
|
void updateEntries();
|
||||||
void updateEntriesForDir(const QString& dir);
|
void updateEntriesForDir(const QString& dir);
|
||||||
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
||||||
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
|
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::models
|
} // namespace ZShell::models
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
//@ pragma UseQApplication
|
//@ pragma UseQApplication
|
||||||
//@ pragma Env QSG_RENDER_LOOP=threaded
|
//@ 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 QSG_NO_VSYNC=1
|
||||||
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
||||||
//@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round
|
//@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round
|
||||||
//@ pragma DropExpensiveFonts
|
//@ pragma DropExpensiveFonts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import qs.Extensions
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
import qs.Modules.Wallpaper
|
import qs.Modules.Wallpaper
|
||||||
import qs.Modules.Lock
|
import qs.Modules.Lock
|
||||||
@@ -14,6 +15,8 @@ import qs.Helpers
|
|||||||
import qs.Modules.Polkit
|
import qs.Modules.Polkit
|
||||||
|
|
||||||
ShellRoot {
|
ShellRoot {
|
||||||
|
settings.watchFiles: true
|
||||||
|
|
||||||
Windows {
|
Windows {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,4 +39,7 @@ ShellRoot {
|
|||||||
|
|
||||||
Polkit {
|
Polkit {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadExtensions {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user