added audio and apps page in settings
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
import qs.Modules.SettingsNew.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("All apps")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
Repeater {
|
||||
id: list
|
||||
|
||||
model: [...DesktopEntries.applications.values].sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
ConnectedRect {
|
||||
id: appItem
|
||||
|
||||
required property int index
|
||||
required property DesktopEntry modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
first: index === 0
|
||||
implicitHeight: appRow.implicitHeight + appRow.anchors.margins * 2
|
||||
last: index === list.count - 1
|
||||
|
||||
StateLayer {
|
||||
onClicked: {
|
||||
root.sState.selectedApp = appItem.modelData;
|
||||
root.sState.openSubPage(2);
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: appRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.largeIncreased
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
IconImage {
|
||||
asynchronous: true
|
||||
implicitSize: Math.round(Appearance.font.size.large * 1.8)
|
||||
source: Quickshell.iconPath(appItem.modelData.icon, "image-missing")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: appItem.modelData.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: (appItem.modelData.comment || appItem.modelData.genericName) ?? ""
|
||||
visible: text
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: DynamicColors.palette.m3primary
|
||||
fill: 1
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: "favorite"
|
||||
visible: Strings.testRegexList(Config.launcher.favoriteApps, appItem.modelData.id)
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
text: "chevron_right"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
import qs.Modules.SettingsNew.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
readonly property DesktopEntry app: sState.selectedApp
|
||||
readonly property bool favoriteByRegex: app && matchedByRegex(Config.launcher.favoriteApps, app.id)
|
||||
readonly property bool hiddenByRegex: app && matchedByRegex(Config.launcher.hiddenApps, app.id)
|
||||
|
||||
function isRegexEntry(s: string): bool {
|
||||
return /^\^.*\$$/.test(s);
|
||||
}
|
||||
|
||||
function matchedByRegex(filterList: list<string>, id: string): bool {
|
||||
return filterList.some(f => isRegexEntry(f) && new RegExp(f).test(id));
|
||||
}
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("App info")
|
||||
|
||||
onAppChanged: {
|
||||
// Auto close when app lost
|
||||
if (!app)
|
||||
sState.closeSubPage();
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
Layout.bottomMargin: Appearance.spacing.large
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.padding.small
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
IconImage {
|
||||
asynchronous: true
|
||||
implicitSize: Math.round(Appearance.font.size.large * 3)
|
||||
source: Quickshell.iconPath(root.app?.icon, "image-missing")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
text: root.app?.name ?? ""
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: (root.app?.comment || root.app?.genericName) ?? ""
|
||||
visible: text
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Launcher
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("Launcher")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: root.app && Strings.testRegexList(Config.launcher.favoriteApps, root.app.id)
|
||||
enabled: !root.favoriteByRegex
|
||||
first: true
|
||||
subtext: root.favoriteByRegex ? qsTr("Matched by a regex in favoriteApps — edit the config file to change") : qsTr("Pin to the top of the launcher")
|
||||
text: qsTr("Favorite")
|
||||
|
||||
onToggled: {
|
||||
const apps = Config.launcher.favoriteApps;
|
||||
Config.launcher.favoriteApps = checked ? [...apps, root.app.id] : apps.filter(a => a !== root.app.id);
|
||||
}
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: root.app && Strings.testRegexList(Config.launcher.hiddenApps, root.app.id)
|
||||
enabled: !root.hiddenByRegex
|
||||
last: true
|
||||
subtext: root.hiddenByRegex ? qsTr("Matched by a regex in hiddenApps — edit the config file to change") : qsTr("Hide from the launcher")
|
||||
text: qsTr("Hidden")
|
||||
|
||||
onToggled: {
|
||||
const apps = Config.launcher.hiddenApps;
|
||||
Config.launcher.hiddenApps = checked ? [...apps, root.app.id] : apps.filter(a => a !== root.app.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Details
|
||||
SectionHeader {
|
||||
text: qsTr("Details")
|
||||
}
|
||||
|
||||
WrapInfoRow {
|
||||
id: appId
|
||||
|
||||
first: true
|
||||
label: qsTr("App ID")
|
||||
labelComp.Layout.preferredWidth: Math.max(labelComp.implicitWidth, command.labelComp.implicitWidth)
|
||||
value: root.app?.id ?? ""
|
||||
}
|
||||
|
||||
WrapInfoRow {
|
||||
id: command
|
||||
|
||||
label: qsTr("Command")
|
||||
labelComp.Layout.preferredWidth: Math.max(labelComp.implicitWidth, appId.labelComp.implicitWidth)
|
||||
last: true
|
||||
value: (root.app?.command ?? []).join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
component WrapInfoRow: ConnectedRect {
|
||||
id: row
|
||||
|
||||
property alias label: label.text
|
||||
readonly property alias labelComp: label
|
||||
property alias value: value.text
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: rowLayout.implicitHeight + rowLayout.anchors.margins * 2
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.largeIncreased
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.alignment: Qt.AlignTop
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: value
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: implicitWidth + 1 // Whyyyyyyyyy
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.small
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import ZShell
|
||||
import qs.Helpers
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Modules.SettingsNew.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
title: qsTr("Apps")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// Default applications
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("Default applications")
|
||||
}
|
||||
|
||||
DefaultRow {
|
||||
first: true
|
||||
icon: "terminal"
|
||||
label: qsTr("Terminal")
|
||||
status: Config.general.apps.terminal.join(" ")
|
||||
|
||||
onSelected: app => Config.general.apps.terminal = app.command
|
||||
}
|
||||
|
||||
DefaultRow {
|
||||
icon: "volume_up"
|
||||
label: qsTr("Audio")
|
||||
status: Config.general.apps.audio.join(" ")
|
||||
|
||||
onSelected: app => Config.general.apps.audio = app.command
|
||||
}
|
||||
|
||||
DefaultRow {
|
||||
icon: "play_circle"
|
||||
label: qsTr("Media playback")
|
||||
status: Config.general.apps.playback.join(" ")
|
||||
|
||||
onSelected: app => Config.general.apps.playback = app.command
|
||||
}
|
||||
|
||||
DefaultRow {
|
||||
icon: "folder"
|
||||
label: qsTr("File manager")
|
||||
last: true
|
||||
status: Config.general.apps.explorer.join(" ")
|
||||
|
||||
onSelected: app => Config.general.apps.explorer = app.command
|
||||
}
|
||||
|
||||
// Library
|
||||
SectionHeader {
|
||||
text: qsTr("Library")
|
||||
}
|
||||
|
||||
NavRow {
|
||||
first: true
|
||||
icon: "apps"
|
||||
label: qsTr("All apps")
|
||||
last: true
|
||||
status: qsTr("Browse installed apps, set favorites and hidden")
|
||||
|
||||
onClicked: root.sState.openSubPage(1)
|
||||
}
|
||||
}
|
||||
|
||||
component DefaultRow: PopupRow {
|
||||
id: row
|
||||
|
||||
readonly property int popupHeight: root.flickable.height - y + root.flickable.contentY - Appearance.padding.large - Appearance.padding.extraLarge
|
||||
|
||||
signal selected(app: DesktopEntry)
|
||||
|
||||
keepPopupAsChild: {
|
||||
if (root.sState.animatingContainer || root.opacity < 1)
|
||||
return true;
|
||||
|
||||
let p = root.parent;
|
||||
while (p && p.objectName !== "PageContainer")
|
||||
p = p.parent;
|
||||
return p?.opacity < 1;
|
||||
}
|
||||
popup.topMovement: Math.max(0 - popupHeight, Appearance.padding.large)
|
||||
|
||||
Loader {
|
||||
active: row.popup.animDriver > 0
|
||||
anchors.centerIn: parent
|
||||
|
||||
sourceComponent: VerticalFadeListView {
|
||||
id: list
|
||||
|
||||
fadeAmount: 0.05
|
||||
implicitHeight: ZUtils.clamp(row.popupHeight, 200, 800)
|
||||
implicitWidth: 300
|
||||
model: {
|
||||
const apps = [...DesktopEntries.applications.values];
|
||||
const favorited = new Set(apps.filter(a => Strings.testRegexList(Config.launcher.favoriteApps, a.id)));
|
||||
return apps.sort((a, b) => (favorited.has(b) - favorited.has(a)) || a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
delegate: StateLayer {
|
||||
id: appItem
|
||||
|
||||
required property int index
|
||||
required property DesktopEntry modelData
|
||||
|
||||
anchors.fill: undefined
|
||||
anchors.left: list.contentItem.left
|
||||
anchors.right: list.contentItem.right
|
||||
implicitHeight: itemLayout.implicitHeight + itemLayout.anchors.margins * 2
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
onClicked: {
|
||||
row.popup.open = false;
|
||||
row.selected(modelData);
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: itemLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
IconImage {
|
||||
asynchronous: true
|
||||
implicitSize: Math.round(Appearance.font.size.large * 1.8)
|
||||
source: Quickshell.iconPath(appItem.modelData.icon, "image-missing")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: appItem.modelData.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: (appItem.modelData.comment || appItem.modelData.genericName) ?? ""
|
||||
visible: text
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: DynamicColors.palette.m3primary
|
||||
fill: 1
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: "favorite"
|
||||
visible: Strings.testRegexList(Config.launcher.favoriteApps, appItem.modelData.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Modules.SettingsNew.Common
|
||||
import qs.Helpers
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Daemons
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("App volumes")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
CustomText {
|
||||
Layout.bottomMargin: Appearance.spacing.small
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.padding.small
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: qsTr("Adjust the volume of individual apps currently playing audio.")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
ItemList {
|
||||
id: streamList
|
||||
|
||||
color: list.count === 0 ? DynamicColors.tPalette.m3surfaceContainer : "transparent"
|
||||
first: true
|
||||
last: true
|
||||
list.spacing: Appearance.spacing.extraSmall / 2
|
||||
placeholderIcon: "music_off"
|
||||
placeholderText: qsTr("No apps playing audio")
|
||||
showList: true
|
||||
|
||||
delegate: SliderRow {
|
||||
id: stream
|
||||
|
||||
required property int index
|
||||
required property PwNode modelData
|
||||
|
||||
anchors.left: streamList.list.contentItem.left
|
||||
anchors.right: streamList.list.contentItem.right
|
||||
enabled: !stream.modelData?.audio?.muted
|
||||
first: index === 0
|
||||
icon: Icons.getVolumeIcon(stream.modelData?.audio?.volume ?? 0, stream.modelData?.audio?.muted ?? false)
|
||||
label: Audio.getStreamName(stream.modelData)
|
||||
last: index === streamList.list.count - 1
|
||||
value: stream.modelData?.audio?.volume ?? 0
|
||||
valueLabel: Math.round(value * 100) + "%"
|
||||
|
||||
onMoved: v => Audio.setStreamVolume(stream.modelData, v)
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: [...Audio.streams]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Daemons
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.SettingsNew.Common
|
||||
import qs.Helpers
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
title: qsTr("Audio")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// Output
|
||||
SliderRow {
|
||||
enabled: !Audio.muted
|
||||
first: true
|
||||
icon: Icons.getVolumeIcon(Audio.volume, Audio.muted)
|
||||
label: qsTr("Output")
|
||||
value: Audio.volume
|
||||
valueLabel: Math.round(value * 100) + "%"
|
||||
|
||||
onMoved: v => Audio.setVolume(v)
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Audio.muted
|
||||
text: qsTr("Muted")
|
||||
|
||||
onToggled: Audio.setStreamMuted(Audio.sink, checked)
|
||||
}
|
||||
|
||||
AudioDeviceList {
|
||||
currentId: Audio.sink?.id ?? -1
|
||||
iconName: "speaker"
|
||||
nodes: Audio.sinks
|
||||
placeholderIcon: "speaker"
|
||||
placeholderText: qsTr("No output devices")
|
||||
|
||||
onSelected: node => Audio.setAudioSink(node)
|
||||
}
|
||||
|
||||
// Input
|
||||
SliderRow {
|
||||
Layout.topMargin: Appearance.spacing.large - parent.spacing
|
||||
enabled: !Audio.sourceMuted
|
||||
first: true
|
||||
icon: Icons.getMicVolumeIcon(Audio.sourceVolume, Audio.sourceMuted)
|
||||
label: qsTr("Input")
|
||||
value: Audio.sourceVolume
|
||||
valueLabel: Math.round(value * 100) + "%"
|
||||
|
||||
onMoved: v => Audio.setSourceVolume(v)
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Audio.sourceMuted
|
||||
text: qsTr("Muted")
|
||||
|
||||
onToggled: Audio.setStreamMuted(Audio.source, checked)
|
||||
}
|
||||
|
||||
AudioDeviceList {
|
||||
currentId: Audio.source?.id ?? -1
|
||||
iconName: "mic"
|
||||
nodes: Audio.sources
|
||||
placeholderIcon: "mic_off"
|
||||
placeholderText: qsTr("No input devices")
|
||||
|
||||
onSelected: node => Audio.setAudioSource(node)
|
||||
}
|
||||
|
||||
// Per-app volumes
|
||||
ConnectedRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Appearance.spacing.large - parent.spacing
|
||||
first: true
|
||||
implicitHeight: appLayout.implicitHeight + appLayout.anchors.margins * 2
|
||||
last: true
|
||||
|
||||
StateLayer {
|
||||
onClicked: root.sState.openSubPage(1)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: appLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.largeIncreased
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
text: "tune"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: qsTr("App volumes")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: Audio.streams.length === 0 ? qsTr("No apps playing audio") : Audio.streams.length === 1 ? qsTr("1 app playing audio") : qsTr("%1 apps playing audio").arg(Audio.streams.length)
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
text: "chevron_right"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ PageBase {
|
||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
||||
from: 0
|
||||
last: true
|
||||
stepSize: 1
|
||||
text: qsTr("Corner radius")
|
||||
to: 50
|
||||
@@ -80,9 +81,9 @@ PageBase {
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||
checked: Config.screenshot.shadow
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
||||
first: true
|
||||
text: qsTr("Enable shadow")
|
||||
|
||||
onToggled: Config.screenshot.shadow = checked
|
||||
|
||||
@@ -20,105 +20,18 @@ PageBase {
|
||||
spacing: Appearance.spacing.large
|
||||
width: root.cappedWidth
|
||||
|
||||
CustomClippingRect {
|
||||
Item {
|
||||
id: wallWrapper
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: {
|
||||
const screen = root.sState.screen;
|
||||
const cWidth = root.cappedWidth;
|
||||
return Math.min(Math.round(cWidth * 0.4), cWidth / screen.width * screen.height);
|
||||
}
|
||||
implicitHeight: cropper.height
|
||||
implicitWidth: {
|
||||
const screen = root.sState.screen;
|
||||
return implicitHeight / screen.height * screen.width;
|
||||
}
|
||||
radius: Appearance.rounding.large
|
||||
|
||||
Loader {
|
||||
active: opacity > 0
|
||||
anchors.centerIn: parent
|
||||
opacity: Config.background.enabled ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.extraSmall
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: "hide_image"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: qsTr("Wallpaper disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
opacity: Config.background.enabled ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: wallIndicatorLoader
|
||||
|
||||
active: opacity > 0
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
sourceComponent: CustomRect {
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: wallLoadDebounceTimer
|
||||
|
||||
interval: 100
|
||||
|
||||
onTriggered: {
|
||||
if (wallImg.status !== Image.Ready)
|
||||
wallIndicatorLoader.opacity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
FadeImage {
|
||||
id: wallImg
|
||||
|
||||
anchors.fill: parent
|
||||
fadeInAnim: Anim.SlowEffects
|
||||
fadeOutAnim: Anim.DefaultEffects
|
||||
preventInit: wallIndicatorLoader.opacity > 0
|
||||
source: Wallpapers.current
|
||||
|
||||
onSourceChanged: wallLoadDebounceTimer.restart()
|
||||
onStatusChanged: {
|
||||
if (status === Image.Ready) {
|
||||
wallLoadDebounceTimer.stop();
|
||||
wallIndicatorLoader.opacity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
WallpaperCropper {
|
||||
id: cropper
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +75,7 @@ PageBase {
|
||||
onToggled: DynamicColors.setMode(checked ? "dark" : "light")
|
||||
}
|
||||
|
||||
PopupRow {
|
||||
OverlayRow {
|
||||
checked: Config.general.color.scheduleDark
|
||||
first: true
|
||||
subtext: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(Config.general.color.scheduleDarkStart).arg(Config.general.color.scheduleDarkEnd)
|
||||
@@ -189,7 +102,7 @@ PageBase {
|
||||
}
|
||||
}
|
||||
|
||||
PopupRow {
|
||||
OverlayRow {
|
||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||
checked: Config.general.color.scheduleHyprsunset
|
||||
last: true
|
||||
|
||||
@@ -15,118 +15,102 @@ PageBase {
|
||||
isSubPage: true
|
||||
title: qsTr("Wallpapers")
|
||||
|
||||
Item {
|
||||
ColumnLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
implicitHeight: childrenRect.height
|
||||
spacing: Appearance.spacing.small
|
||||
width: root.cappedWidth
|
||||
|
||||
WallpaperCropper {
|
||||
id: cropper
|
||||
CustomText {
|
||||
Layout.topMargin: Appearance.spacing.large
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: qsTr("Wallpapers")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.margins: Appearance.spacing.normal
|
||||
anchors.top: cropper.bottom
|
||||
spacing: Appearance.spacing.small
|
||||
width: root.cappedWidth
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
columnSpacing: Appearance.spacing.extraSmall
|
||||
columns: 4
|
||||
rowSpacing: Appearance.spacing.extraSmall
|
||||
visible: localWalls.count > 0
|
||||
|
||||
CustomText {
|
||||
Layout.topMargin: Appearance.spacing.large
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: qsTr("Wallpapers")
|
||||
}
|
||||
Repeater {
|
||||
id: localWalls
|
||||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
columnSpacing: Appearance.spacing.extraSmall
|
||||
columns: 4
|
||||
rowSpacing: Appearance.spacing.extraSmall
|
||||
visible: localWalls.count > 0
|
||||
model: {
|
||||
const walls = Wallpapers.list;
|
||||
var baseDir = Paths.wallsdir;
|
||||
const categories = {};
|
||||
const list = [];
|
||||
for (const w of walls) {
|
||||
var parentDir = w.parentDir;
|
||||
if (!parentDir.endsWith("/"))
|
||||
parentDir = parentDir + "/";
|
||||
|
||||
Repeater {
|
||||
id: localWalls
|
||||
if (!baseDir.endsWith("/"))
|
||||
baseDir = baseDir + "/";
|
||||
|
||||
model: {
|
||||
const walls = Wallpapers.list;
|
||||
var baseDir = Paths.wallsdir;
|
||||
const categories = {};
|
||||
const list = [];
|
||||
for (const w of walls) {
|
||||
var parentDir = w.parentDir;
|
||||
if (!parentDir.endsWith("/"))
|
||||
parentDir = parentDir + "/";
|
||||
|
||||
if (!baseDir.endsWith("/"))
|
||||
baseDir = baseDir + "/";
|
||||
|
||||
if (parentDir !== baseDir) {
|
||||
const category = Wallpapers.getCategoryFor(w);
|
||||
if (category && (!(category in categories) || categories[category].name.localeCompare(w.name) > 0))
|
||||
categories[category] = w;
|
||||
} else {
|
||||
list.push(w);
|
||||
}
|
||||
if (parentDir !== baseDir) {
|
||||
const category = Wallpapers.getCategoryFor(w);
|
||||
if (category && (!(category in categories) || categories[category].name.localeCompare(w.name) > 0))
|
||||
categories[category] = w;
|
||||
} else {
|
||||
list.push(w);
|
||||
}
|
||||
list.push(...Object.values(categories));
|
||||
list.sort((a, b) => ((a.parentDir === baseDir) - (b.parentDir === baseDir)) || a.name.localeCompare(b.name));
|
||||
while (list.length < 4)
|
||||
list.push(null);
|
||||
|
||||
return list;
|
||||
}
|
||||
list.push(...Object.values(categories));
|
||||
list.sort((a, b) => ((a.parentDir === baseDir) - (b.parentDir === baseDir)) || a.name.localeCompare(b.name));
|
||||
while (list.length < 4)
|
||||
list.push(null);
|
||||
|
||||
WallItem {
|
||||
required property FileSystemEntry modelData
|
||||
return list;
|
||||
}
|
||||
|
||||
enabled: modelData
|
||||
WallItem {
|
||||
required property FileSystemEntry modelData
|
||||
|
||||
// Empty placeholders for sizing
|
||||
opacity: modelData ? 1 : 0
|
||||
source: String(modelData?.path ?? "")
|
||||
enabled: modelData
|
||||
|
||||
onClicked: {
|
||||
if (modelData.parentDir !== Paths.wallsdir) {
|
||||
root.sState.selectedWallpaperCategory = Wallpapers.getCategoryFor(modelData);
|
||||
root.sState.openSubPage(2); // Category page
|
||||
} else {
|
||||
Wallpapers.setWallpaper(modelData.path);
|
||||
root.sState.closeSubPage();
|
||||
}
|
||||
}
|
||||
// Empty placeholders for sizing
|
||||
opacity: modelData ? 1 : 0
|
||||
source: String(modelData?.path ?? "")
|
||||
|
||||
onClicked: {
|
||||
Wallpapers.setWallpaper(modelData.path);
|
||||
root.sState.closeSubPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
active: localWalls.count === 0
|
||||
asynchronous: true
|
||||
visible: active
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
active: localWalls.count === 0
|
||||
asynchronous: true
|
||||
visible: active
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: noWallsLayout.implicitHeight + Appearance.padding.extraLarge * 3
|
||||
radius: Appearance.rounding.large
|
||||
sourceComponent: CustomRect {
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: noWallsLayout.implicitHeight + Appearance.padding.extraLarge * 3
|
||||
radius: Appearance.rounding.large
|
||||
|
||||
ColumnLayout {
|
||||
id: noWallsLayout
|
||||
ColumnLayout {
|
||||
id: noWallsLayout
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.extraSmall
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.extraSmall
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outline
|
||||
text: "hide_image"
|
||||
}
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outline
|
||||
text: "hide_image"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outline
|
||||
text: qsTr("No local wallpapers found")
|
||||
}
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outline
|
||||
text: qsTr("No local wallpapers found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user