merge main into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m42s
C++ / build (pull_request) Successful in 2m28s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m42s
C++ / build (pull_request) Successful in 2m28s
This commit is contained in:
+5
-5
@@ -56,15 +56,15 @@ RowLayout {
|
||||
const item = ch.item;
|
||||
const itemWidth = item.implicitWidth;
|
||||
|
||||
if (id === "audio" && Config.barConfig.popouts.audio) {
|
||||
if (id === "audio" && Config.bar.popouts.audio) {
|
||||
popouts.currentName = "audio";
|
||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||
popouts.hasCurrent = true;
|
||||
} else if (id === "network" && Config.barConfig.popouts.network) {
|
||||
} else if (id === "network" && Config.bar.popouts.network) {
|
||||
popouts.currentName = "network";
|
||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||
popouts.hasCurrent = true;
|
||||
} else if (id === "upower" && Config.barConfig.popouts.upower) {
|
||||
} else if (id === "upower" && Config.bar.popouts.upower) {
|
||||
popouts.currentName = "upower";
|
||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||
popouts.hasCurrent = true;
|
||||
@@ -80,8 +80,8 @@ RowLayout {
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
// model: Config.barConfig.entries.filted(n => n.index > 50).sort(n => n.index)
|
||||
model: Config.barConfig.entries
|
||||
// model: Config.bar.entries.filted(n => n.index > 50).sort(n => n.index)
|
||||
model: Config.bar.entries
|
||||
|
||||
DelegateChooser {
|
||||
role: "id"
|
||||
|
||||
@@ -14,15 +14,15 @@ import qs.Modules.SysTray.Widgets
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int contentHeight: Config.barConfig.height + padding * 2
|
||||
readonly property int exclusiveZone: Config.barConfig.autoHide ? Config.barConfig.border : contentHeight
|
||||
readonly property int contentHeight: Config.bar.height + padding * 2
|
||||
readonly property int exclusiveZone: Config.bar.autoHide ? Config.bar.border : contentHeight
|
||||
required property bool fullscreen
|
||||
property bool isHovered
|
||||
readonly property int padding: Math.max(Appearance.padding.smaller, Config.barConfig.border)
|
||||
readonly property int padding: Math.max(Appearance.padding.smaller, Config.bar.border)
|
||||
required property Wrapper popouts
|
||||
required property ClipWrapper popoutsWrapper
|
||||
required property ShellScreen screen
|
||||
readonly property bool shouldBeVisible: !fullscreen && (!Config.barConfig.autoHide || visibilities.bar || isHovered)
|
||||
readonly property bool shouldBeVisible: !fullscreen && (!Config.bar.autoHide || visibilities.bar || isHovered)
|
||||
readonly property int vPadding: 6
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
@@ -30,8 +30,8 @@ Item {
|
||||
content.item?.checkPopout(x);
|
||||
}
|
||||
|
||||
implicitHeight: fullscreen ? 0 : Config.barConfig.border
|
||||
visible: height > Config.barConfig.border
|
||||
implicitHeight: fullscreen ? 0 : Config.bar.border
|
||||
visible: height > Config.bar.border
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
|
||||
@@ -38,11 +38,11 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Config.barConfig.border + 1
|
||||
anchors.margins: Config.bar.border + 1
|
||||
anchors.topMargin: root.bar.implicitHeight + 1
|
||||
radius: Config.barConfig.border > 0 ? Config.barConfig.rounding : 0
|
||||
topLeftRadius: Config.barConfig.rounding
|
||||
topRightRadius: Config.barConfig.rounding
|
||||
radius: Config.bar.border > 0 ? Config.bar.rounding : 0
|
||||
topLeftRadius: Config.bar.rounding
|
||||
topRightRadius: Config.bar.rounding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ CustomRect {
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
color: visibilities.dashboard ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||
implicitHeight: Config.bar.height + Appearance.padding.smallest * 2
|
||||
implicitWidth: timeText.contentWidth + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ CustomRect {
|
||||
property bool tempEnabled: Hyprsunset.enabled
|
||||
|
||||
color: root.tempEnabled ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||
implicitHeight: Config.bar.height + Appearance.padding.smallest * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import qs.Config
|
||||
CustomListView {
|
||||
id: root
|
||||
|
||||
required property CustomTextField search
|
||||
required property SearchBar search
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
highlightFollowsCurrentItem: false
|
||||
|
||||
+47
-109
@@ -16,13 +16,13 @@ Item {
|
||||
readonly property int rounding: Appearance.rounding.large
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitHeight: searchWrapper.height + listWrapper.height + padding * 2
|
||||
implicitHeight: search.height + listWrapper.height + padding * 2
|
||||
implicitWidth: listWrapper.width + padding * 2
|
||||
|
||||
Item {
|
||||
id: listWrapper
|
||||
|
||||
anchors.bottom: searchWrapper.top
|
||||
anchors.bottom: search.top
|
||||
anchors.bottomMargin: root.padding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitHeight: list.height + root.padding
|
||||
@@ -32,7 +32,7 @@ Item {
|
||||
id: list
|
||||
|
||||
content: root
|
||||
maxHeight: root.maxHeight - searchWrapper.implicitHeight - root.padding * 3
|
||||
maxHeight: root.maxHeight - search.implicitHeight - root.padding * 3
|
||||
padding: root.padding
|
||||
panels: root.panels
|
||||
rounding: root.rounding
|
||||
@@ -41,131 +41,69 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: searchWrapper
|
||||
SearchBar {
|
||||
id: search
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: root.padding
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||
implicitHeight: Math.max(searchIcon.implicitHeight, search.implicitHeight, clearIcon.implicitHeight)
|
||||
radius: Appearance.rounding.smallest
|
||||
bottomPadding: Appearance.padding.larger
|
||||
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
||||
topPadding: Appearance.padding.larger
|
||||
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.padding + 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: "search"
|
||||
Component.onCompleted: {
|
||||
console.log(search.color);
|
||||
console.log(search.placeholderTextColor);
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: search
|
||||
|
||||
anchors.left: searchIcon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.right: clearIcon.left
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
bottomPadding: Appearance.padding.larger
|
||||
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
||||
topPadding: Appearance.padding.larger
|
||||
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
||||
Keys.onEscapePressed: root.visibilities.launcher = false
|
||||
Keys.onPressed: event => {
|
||||
if (!Config.launcher.vimKeybinds)
|
||||
return;
|
||||
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_J) {
|
||||
list.currentList?.incrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_K) {
|
||||
list.currentList?.decrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
}
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
||||
Keys.onEscapePressed: root.visibilities.launcher = false
|
||||
Keys.onPressed: event => {
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_J) {
|
||||
list.currentList?.incrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
|
||||
} else if (event.key === Qt.Key_K) {
|
||||
list.currentList?.decrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
}
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
list.currentList?.incrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
|
||||
list.currentList?.decrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
}
|
||||
Keys.onUpPressed: list.currentList?.incrementCurrentIndex()
|
||||
onAccepted: {
|
||||
const currentItem = list.currentList?.currentItem;
|
||||
if (currentItem) {
|
||||
if (list.showWallpapers) {
|
||||
if (DynamicColors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent)
|
||||
Wallpapers.previewColourLock = true;
|
||||
Wallpapers.setWallpaper(currentItem.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
} else if (text.startsWith(Config.launcher.actionPrefix)) {
|
||||
if (text.startsWith(`${Config.launcher.actionPrefix}calc `))
|
||||
currentItem.onClicked();
|
||||
else
|
||||
currentItem.modelData.onClicked(list.currentList);
|
||||
} else {
|
||||
Apps.launch(currentItem.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
Keys.onUpPressed: list.currentList?.incrementCurrentIndex()
|
||||
onAccepted: {
|
||||
const currentItem = list.currentList?.currentItem;
|
||||
if (currentItem) {
|
||||
if (list.showWallpapers) {
|
||||
if (DynamicColors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent)
|
||||
Wallpapers.previewColourLock = true;
|
||||
Wallpapers.setWallpaper(currentItem.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
} else if (text.startsWith(Config.launcher.actionPrefix)) {
|
||||
if (text.startsWith(`${Config.launcher.actionPrefix}calc `))
|
||||
currentItem.onClicked();
|
||||
else
|
||||
currentItem.modelData.onClicked(list.currentList);
|
||||
} else {
|
||||
Apps.launch(currentItem.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onLauncherChanged(): void {
|
||||
if (!root.visibilities.launcher)
|
||||
search.text = "";
|
||||
}
|
||||
|
||||
target: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: clearIcon
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.padding + 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
opacity: {
|
||||
if (!search.text)
|
||||
return 0;
|
||||
if (mouse.pressed)
|
||||
return 0.7;
|
||||
if (mouse.containsMouse)
|
||||
return 0.8;
|
||||
return 1;
|
||||
}
|
||||
text: "close"
|
||||
width: search.text ? implicitWidth : implicitWidth / 2
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
Behavior on width {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Connections {
|
||||
function onLauncherChanged(): void {
|
||||
if (!root.visibilities.launcher)
|
||||
search.text = "";
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
cursorShape: search.text ? Qt.PointingHandCursor : undefined
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: search.text = ""
|
||||
}
|
||||
target: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ Item {
|
||||
required property int padding
|
||||
required property var panels
|
||||
required property int rounding
|
||||
required property CustomTextField search
|
||||
required property SearchBar search
|
||||
readonly property bool showWallpapers: search.text.startsWith(`${Config.launcher.actionPrefix}wallpaper `)
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
|
||||
@@ -65,5 +65,20 @@ Item {
|
||||
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: favoriteIcon
|
||||
|
||||
active: root.modelData && Strings.testRegexList(Config.launcher.favoriteApps, root.modelData.id)
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
color: DynamicColors.palette.m3primary
|
||||
fill: 1
|
||||
text: "favorite"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ Searcher {
|
||||
Variants {
|
||||
id: variants
|
||||
|
||||
model: Config.launcher.actions.filter(a => (a.enabled ?? true))
|
||||
model: Config.launcher.actions.filter(a => (a.enabled ?? true) && (Config.launcher.enableDangerousActions || !(a.dangerous ?? false)))
|
||||
|
||||
Action {
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@ Searcher {
|
||||
AppDb {
|
||||
id: appDb
|
||||
|
||||
entries: DesktopEntries.applications.values
|
||||
entries: DesktopEntries.applications.values.filter(a => !Strings.testRegexList(Config.launcher.hiddenApps, a.id))
|
||||
favoriteApps: Config.launcher.favoriteApps
|
||||
path: `${Paths.state}/apps.sqlite`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ PathView {
|
||||
let outerMargins = 0;
|
||||
if ((visibilities.utilities || visibilities.sidebar) && panels.utilities.implicitWidth > outerMargins)
|
||||
outerMargins = panels.utilities.implicitWidth;
|
||||
const maxWidth = screen.width - Config.barConfig.rounding * 4 - (barMargins + outerMargins) * 2;
|
||||
const maxWidth = screen.width - Config.bar.rounding * 4 - (barMargins + outerMargins) * 2;
|
||||
|
||||
if (maxWidth <= 0)
|
||||
return 0;
|
||||
@@ -37,7 +37,7 @@ PathView {
|
||||
return visible;
|
||||
}
|
||||
required property var panels
|
||||
required property CustomTextField search
|
||||
required property SearchBar search
|
||||
required property var visibilities
|
||||
|
||||
cacheItemCount: 4
|
||||
|
||||
@@ -12,7 +12,7 @@ CustomRect {
|
||||
readonly property int textWidth: Math.min(metrics.width, 200)
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||
implicitHeight: Config.bar.height + Appearance.padding.smallest * 2
|
||||
implicitWidth: layout.implicitWidth + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
import qs.Daemons
|
||||
import qs.Components
|
||||
|
||||
CustomRect {
|
||||
@@ -12,7 +11,7 @@ CustomRect {
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
color: visibilities.sidebar ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||
implicitHeight: Config.bar.height + Appearance.padding.smallest * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
@@ -24,7 +23,7 @@ CustomRect {
|
||||
fill: root.visibilities.sidebar ? 1 : 0
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
||||
text: NotifServer.list.length ? "\uf4fe" : "\ue7f4"
|
||||
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Daemons
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Daemons
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -72,7 +73,15 @@ Item {
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: NotifServer.popups.filter(n => !n.closed)
|
||||
values: {
|
||||
const popups = NotifServer.popups.filter(n => !n.closed);
|
||||
|
||||
if (!Config.notifs.showInFullscreen) {
|
||||
if (Hypr.monitorFor(root.panels.screen).activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false)
|
||||
return [];
|
||||
}
|
||||
return popups;
|
||||
}
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
|
||||
@@ -7,7 +7,7 @@ ShapePath {
|
||||
id: root
|
||||
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real rounding: Config.barConfig.rounding
|
||||
readonly property real rounding: Config.bar.rounding
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
required property var sidebar
|
||||
required property Wrapper wrapper
|
||||
|
||||
@@ -73,6 +73,7 @@ CustomRect {
|
||||
CustomSplitButton {
|
||||
active: menuItems.find(m => root.props.recordingMode === m.icon + m.text) ?? menuItems[0]
|
||||
enabled: !Recorder.running
|
||||
menuOnTop: true
|
||||
|
||||
menuItems: [
|
||||
MenuItem {
|
||||
|
||||
@@ -16,7 +16,7 @@ CustomRect {
|
||||
|
||||
clip: true
|
||||
color: visibilities.resources ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2
|
||||
implicitHeight: Config.bar.height + Appearance.padding.smallest * 2
|
||||
implicitWidth: rowLayout.implicitWidth + Appearance.padding.larger * 2
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
|
||||
@@ -1,370 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Modules as Modules
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Item content
|
||||
|
||||
signal settingSelected(string category, string section, string settingName)
|
||||
|
||||
// Function to select category by key
|
||||
function selectCategory(categoryKey: string) {
|
||||
for (let i = 0; i < appearanceCats.count; i++) {
|
||||
const item = appearanceCats.get(i);
|
||||
if (item.key === categoryKey) {
|
||||
appearanceView.currentIndex = i;
|
||||
root.content.setCategory(item.key, item._index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < systemCats.count; i++) {
|
||||
const item = systemCats.get(i);
|
||||
if (item.key === categoryKey) {
|
||||
sysView.currentIndex = i;
|
||||
root.content.setCategory(item.key, item._index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < panelCats.count; i++) {
|
||||
const item = panelCats.get(i);
|
||||
if (item.key === categoryKey) {
|
||||
panelsView.currentIndex = i;
|
||||
root.content.setCategory(item.key, item._index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: searchBar.implicitHeight + Appearance.spacing.smaller + clayout.contentHeight + clayout.anchors.margins * 2
|
||||
implicitWidth: clayout.contentWidth + clayout.anchors.margins * 2
|
||||
|
||||
ListModel {
|
||||
id: systemCats
|
||||
|
||||
ListElement {
|
||||
_index: 0
|
||||
icon: "settings"
|
||||
key: "general"
|
||||
name: "General"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 1
|
||||
icon: "build_circle"
|
||||
key: "services"
|
||||
name: "Services"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 2
|
||||
icon: "notifications"
|
||||
key: "notifications"
|
||||
name: "Notifications"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 3
|
||||
icon: "handyman"
|
||||
key: "utilities"
|
||||
name: "Utilities"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 4
|
||||
icon: "cached"
|
||||
key: "updates"
|
||||
name: "Updates"
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: panelCats
|
||||
|
||||
ListElement {
|
||||
_index: 5
|
||||
icon: "settop_component"
|
||||
key: "bar"
|
||||
name: "Bar"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 6
|
||||
icon: "lock"
|
||||
key: "lockscreen"
|
||||
name: "Lockscreen"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 7
|
||||
icon: "view_sidebar"
|
||||
key: "sidebar"
|
||||
name: "Sidebar"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 8
|
||||
icon: "dashboard"
|
||||
key: "dashboard"
|
||||
name: "Dashboard"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 9
|
||||
icon: "display_settings"
|
||||
key: "osd"
|
||||
name: "On screen display"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 10
|
||||
icon: "rocket_launch"
|
||||
key: "launcher"
|
||||
name: "Launcher"
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: appearanceCats
|
||||
|
||||
ListElement {
|
||||
_index: 11
|
||||
icon: "wallpaper"
|
||||
key: "wallpaper"
|
||||
name: "Wallpaper"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 12
|
||||
icon: "screenshot_region"
|
||||
key: "screenshot"
|
||||
name: "Screenshot"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
_index: 13
|
||||
icon: "colors"
|
||||
key: "appearance"
|
||||
name: "Appearance"
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
anchors.fill: parent
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
CustomFlickable {
|
||||
id: clayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.extraSmall
|
||||
contentHeight: contentItem.childrenRect.height
|
||||
contentWidth: contentItem.childrenRect.width
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomListView {
|
||||
id: sysView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentItem.childrenRect.height
|
||||
Layout.preferredWidth: contentItem.childrenRect.width
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
highlightFollowsCurrentItem: false
|
||||
interactive: false
|
||||
model: systemCats
|
||||
spacing: 2
|
||||
|
||||
delegate: Category {
|
||||
view: sysView
|
||||
}
|
||||
// highlight: CustomRect {
|
||||
// color: DynamicColors.palette.m3primary
|
||||
// implicitHeight: sysView.currentItem?.implicitHeight ?? 0
|
||||
// implicitWidth: sysView.width
|
||||
// radius: Appearance.rounding.normal - Appearance.padding.smaller
|
||||
// y: sysView.currentItem?.y ?? 0
|
||||
//
|
||||
// Behavior on y {
|
||||
// Anim {
|
||||
// duration: Appearance.anim.durations.small
|
||||
// easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
CustomListView {
|
||||
id: panelsView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentItem.childrenRect.height
|
||||
Layout.preferredWidth: contentItem.childrenRect.width
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
contentWidth: contentItem.childrenRect.width
|
||||
highlightFollowsCurrentItem: false
|
||||
interactive: false
|
||||
model: panelCats
|
||||
spacing: 2
|
||||
|
||||
delegate: Category {
|
||||
view: panelsView
|
||||
}
|
||||
// highlight: CustomRect {
|
||||
// color: DynamicColors.palette.m3primary
|
||||
// implicitHeight: panelsView.currentItem?.implicitHeight ?? 0
|
||||
// implicitWidth: panelsView.width
|
||||
// radius: Appearance.rounding.normal - Appearance.padding.smaller
|
||||
// y: panelsView.currentItem?.y ?? 0
|
||||
//
|
||||
// Behavior on y {
|
||||
// Anim {
|
||||
// duration: Appearance.anim.durations.small
|
||||
// easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
CustomListView {
|
||||
id: appearanceView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentItem.childrenRect.height
|
||||
Layout.preferredWidth: contentItem.childrenRect.width
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
contentWidth: contentItem.childrenRect.width
|
||||
highlightFollowsCurrentItem: false
|
||||
interactive: false
|
||||
model: appearanceCats
|
||||
spacing: 2
|
||||
|
||||
delegate: Category {
|
||||
view: appearanceView
|
||||
}
|
||||
// highlight: CustomRect {
|
||||
// color: DynamicColors.palette.m3primary
|
||||
// implicitHeight: appearanceView.currentItem?.implicitHeight ?? 0
|
||||
// implicitWidth: appearanceView.width
|
||||
// radius: Appearance.rounding.normal - Appearance.padding.smaller
|
||||
// y: appearanceView.currentItem?.y ?? 0
|
||||
//
|
||||
// Behavior on y {
|
||||
// Anim {
|
||||
// duration: Appearance.anim.durations.small
|
||||
// easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Category: CustomRect {
|
||||
id: categoryItem
|
||||
|
||||
required property int _index
|
||||
readonly property bool first: index === 0
|
||||
required property string icon
|
||||
required property int index
|
||||
required property string key
|
||||
readonly property bool last: index === view.model.count - 1
|
||||
required property string name
|
||||
readonly property bool selected: key === root.content.currentCategory
|
||||
required property ListView view
|
||||
|
||||
bottomLeftRadius: layer.pressed ? Appearance.rounding.smallest : selected || last ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
|
||||
bottomRightRadius: layer.pressed ? Appearance.rounding.smallest : selected || last ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
|
||||
color: selected ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 50
|
||||
implicitWidth: 256
|
||||
topLeftRadius: layer.pressed ? Appearance.rounding.smallest : selected || first ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
|
||||
topRightRadius: layer.pressed ? Appearance.rounding.smallest : selected || first ? Appearance.rounding.normal - clayout.anchors.margins : Appearance.rounding.extraSmall
|
||||
|
||||
Behavior on bottomLeftRadius {
|
||||
Anim {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
}
|
||||
Behavior on bottomRightRadius {
|
||||
Anim {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
}
|
||||
Behavior on topLeftRadius {
|
||||
Anim {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
}
|
||||
Behavior on topRightRadius {
|
||||
Anim {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: Appearance.padding.small
|
||||
Layout.preferredWidth: icon.contentWidth
|
||||
color: categoryItem.key === root.content.currentCategory ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
fill: categoryItem.key === root.content.currentCategory ? 1 : 0
|
||||
font.pointSize: Appearance.font.size.small * 2
|
||||
text: categoryItem.icon
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.spacing.normal
|
||||
color: categoryItem.key === root.content.currentCategory ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
text: categoryItem.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: layer
|
||||
|
||||
color: categoryItem.key === root.content.currentCategory ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
|
||||
onClicked: {
|
||||
root.content.setCategory(categoryItem.key, categoryItem._index);
|
||||
categoryItem.view.currentIndex = categoryItem.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
import QtQuick
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
id: root
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Scale"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Scale"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Rounding scale"
|
||||
object: Config.appearance.rounding
|
||||
setting: "scale"
|
||||
step: 0.1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Spacing scale"
|
||||
object: Config.appearance.spacing
|
||||
setting: "scale"
|
||||
step: 0.1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Padding scale"
|
||||
object: Config.appearance.padding
|
||||
setting: "scale"
|
||||
step: 0.1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Font size scale"
|
||||
object: Config.appearance.font.size
|
||||
setting: "scale"
|
||||
step: 0.1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Animation duration scale"
|
||||
object: Config.appearance.anim.durations
|
||||
setting: "scale"
|
||||
step: 0.1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Deform animation scale"
|
||||
object: Config.appearance.deform
|
||||
setting: "scale"
|
||||
step: 0.1
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Fonts"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Fonts"
|
||||
}
|
||||
|
||||
SettingListView {
|
||||
name: "Sans family"
|
||||
object: Config.appearance.font.family
|
||||
setting: "sans"
|
||||
stringList: Qt.fontFamilies()
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingListView {
|
||||
name: "Monospace family"
|
||||
object: Config.appearance.font.family
|
||||
setting: "mono"
|
||||
stringList: Qt.fontFamilies()
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Animation"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Animation"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Media GIF speed adjustment"
|
||||
object: Config.appearance.anim
|
||||
setting: "mediaGifSpeedAdjustment"
|
||||
step: 10
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 5
|
||||
min: 0
|
||||
name: "Session GIF speed"
|
||||
object: Config.appearance.anim
|
||||
setting: "sessionGifSpeed"
|
||||
step: 0.1
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Transparency"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Transparency"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable transparency"
|
||||
object: Config.appearance.transparency
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 1
|
||||
min: 0
|
||||
name: "Base opacity"
|
||||
object: Config.appearance.transparency
|
||||
setting: "base"
|
||||
step: 0.05
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 1
|
||||
min: 0
|
||||
name: "Layer opacity"
|
||||
object: Config.appearance.transparency
|
||||
setting: "layers"
|
||||
step: 0.05
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import Quickshell
|
||||
import QtQuick.Layouts
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Wallpaper"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Wallpaper"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable wallpaper rendering"
|
||||
object: Config.background
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Fade duration"
|
||||
object: Config.background
|
||||
setting: "wallFadeDuration"
|
||||
step: 50
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
WallpaperCropper {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Wallpapers"
|
||||
|
||||
WallpaperGrid {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Bar"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Bar"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Auto hide"
|
||||
object: Config.barConfig
|
||||
setting: "autoHide"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Height"
|
||||
object: Config.barConfig
|
||||
setting: "height"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Rounding"
|
||||
object: Config.barConfig
|
||||
setting: "rounding"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Border"
|
||||
object: Config.barConfig
|
||||
setting: "border"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Smoothing"
|
||||
object: Config.barConfig
|
||||
setting: "smoothing"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Tray"
|
||||
|
||||
SettingsHeader {
|
||||
name: "System tray"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 16
|
||||
name: "Tray icon size"
|
||||
object: Config.barConfig.tray
|
||||
setting: "trayIconSize"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Popouts"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Popouts"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Tray"
|
||||
object: Config.barConfig.popouts
|
||||
setting: "tray"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Audio"
|
||||
object: Config.barConfig.popouts
|
||||
setting: "audio"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Active window"
|
||||
object: Config.barConfig.popouts
|
||||
setting: "activeWindow"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Resources"
|
||||
object: Config.barConfig.popouts
|
||||
setting: "resources"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Clock"
|
||||
object: Config.barConfig.popouts
|
||||
setting: "clock"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Network"
|
||||
object: Config.barConfig.popouts
|
||||
setting: "network"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Power"
|
||||
object: Config.barConfig.popouts
|
||||
setting: "upower"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Entries"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Entries"
|
||||
}
|
||||
|
||||
SettingBarEntryList {
|
||||
name: "Bar entries"
|
||||
object: Config.barConfig
|
||||
setting: "entries"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Dock"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Dock"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable dock"
|
||||
object: Config.dock
|
||||
setting: "enable"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Dock height"
|
||||
object: Config.dock
|
||||
setting: "height"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Hover to reveal"
|
||||
object: Config.dock
|
||||
setting: "hoverToReveal"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Pin on startup"
|
||||
object: Config.dock
|
||||
setting: "pinnedOnStartup"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add pinned app")
|
||||
name: "Pinned apps"
|
||||
object: Config.dock
|
||||
setting: "pinnedApps"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add ignored regex")
|
||||
name: "Ignored app regexes"
|
||||
object: Config.dock
|
||||
setting: "ignoredAppRegexes"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Dashboard"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Dashboard"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable dashboard"
|
||||
object: Config.dashboard
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Media update interval"
|
||||
object: Config.dashboard
|
||||
setting: "mediaUpdateInterval"
|
||||
step: 50
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Resource update interval"
|
||||
object: Config.dashboard
|
||||
setting: "resourceUpdateInterval"
|
||||
step: 50
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Drag threshold"
|
||||
object: Config.dashboard
|
||||
setting: "dragThreshold"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Performance"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Performance"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show battery"
|
||||
object: Config.dashboard.performance
|
||||
setting: "showBattery"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show GPU"
|
||||
object: Config.dashboard.performance
|
||||
setting: "showGpu"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show CPU"
|
||||
object: Config.dashboard.performance
|
||||
setting: "showCpu"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show memory"
|
||||
object: Config.dashboard.performance
|
||||
setting: "showMemory"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show storage"
|
||||
object: Config.dashboard.performance
|
||||
setting: "showStorage"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show network"
|
||||
object: Config.dashboard.performance
|
||||
setting: "showNetwork"
|
||||
}
|
||||
}
|
||||
|
||||
// SettingsSection {
|
||||
// sectionId: "Layout Sizes"
|
||||
//
|
||||
// SettingsHeader {
|
||||
// name: "Layout Sizes"
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Tab indicator height"
|
||||
// value: String(Config.dashboard.sizes.tabIndicatorHeight)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Tab indicator spacing"
|
||||
// value: String(Config.dashboard.sizes.tabIndicatorSpacing)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Info width"
|
||||
// value: String(Config.dashboard.sizes.infoWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Info icon size"
|
||||
// value: String(Config.dashboard.sizes.infoIconSize)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Date time width"
|
||||
// value: String(Config.dashboard.sizes.dateTimeWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media width"
|
||||
// value: String(Config.dashboard.sizes.mediaWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media progress sweep"
|
||||
// value: String(Config.dashboard.sizes.mediaProgressSweep)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media progress thickness"
|
||||
// value: String(Config.dashboard.sizes.mediaProgressThickness)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Resource progress thickness"
|
||||
// value: String(Config.dashboard.sizes.resourceProgessThickness)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Weather width"
|
||||
// value: String(Config.dashboard.sizes.weatherWidth)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media cover art size"
|
||||
// value: String(Config.dashboard.sizes.mediaCoverArtSize)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Media visualiser size"
|
||||
// value: String(Config.dashboard.sizes.mediaVisualiserSize)
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingReadOnly {
|
||||
// name: "Resource size"
|
||||
// value: String(Config.dashboard.sizes.resourceSize)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
|
||||
SettingsPage {
|
||||
id: root
|
||||
|
||||
function schemeTypeItem(items, value) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (item.value === value)
|
||||
return item;
|
||||
}
|
||||
|
||||
return items[0] ?? null;
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "General"
|
||||
|
||||
SettingsHeader {
|
||||
name: "General"
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Logo"
|
||||
object: Config.general
|
||||
setting: "logo"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Wallpaper path"
|
||||
object: Config.general
|
||||
setting: "wallpaperPath"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Desktop icons"
|
||||
object: Config.general
|
||||
setting: "desktopIcons"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Date format"
|
||||
object: Config.general
|
||||
setting: "dateFormat"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Color"
|
||||
z: 1
|
||||
|
||||
SettingsHeader {
|
||||
name: "Color"
|
||||
}
|
||||
|
||||
CustomSplitButtonRow {
|
||||
active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1]
|
||||
enabled: Config.general.color.schemeGeneration
|
||||
label: qsTr("Scheme mode")
|
||||
|
||||
menuItems: [
|
||||
MenuItem {
|
||||
icon: "light_mode"
|
||||
text: qsTr("Light")
|
||||
value: "light"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "dark_mode"
|
||||
text: qsTr("Dark")
|
||||
value: "dark"
|
||||
}
|
||||
]
|
||||
|
||||
onSelected: item => {
|
||||
Config.general.color.mode = item.value;
|
||||
Config.save();
|
||||
|
||||
if (item.value === "light")
|
||||
ModeScheduler.applyLightMode();
|
||||
else if (item.value === "dark")
|
||||
ModeScheduler.applyDarkMode();
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
CustomSplitButtonRow {
|
||||
id: schemeType
|
||||
|
||||
active: root.schemeTypeItem(menuItems, Config.colors.schemeType)
|
||||
enabled: Config.general.color.schemeGeneration
|
||||
label: qsTr("Scheme type")
|
||||
|
||||
menuItems: [
|
||||
MenuItem {
|
||||
icon: "palette"
|
||||
text: qsTr("Vibrant")
|
||||
value: "vibrant"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "gesture"
|
||||
text: qsTr("Expressive")
|
||||
value: "expressive"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "contrast"
|
||||
text: qsTr("Monochrome")
|
||||
value: "monochrome"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "tonality"
|
||||
text: qsTr("Neutral")
|
||||
value: "neutral"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "gradient"
|
||||
text: qsTr("Tonal spot")
|
||||
value: "tonal-spot"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "target"
|
||||
text: qsTr("Fidelity")
|
||||
value: "fidelity"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "article"
|
||||
text: qsTr("Content")
|
||||
value: "content"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "colors"
|
||||
text: qsTr("Rainbow")
|
||||
value: "rainbow"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "nutrition"
|
||||
text: qsTr("Fruit salad")
|
||||
value: "fruit-salad"
|
||||
}
|
||||
]
|
||||
|
||||
onSelected: item => {
|
||||
Config.colors.schemeType = item.value;
|
||||
Config.save();
|
||||
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--scheme", item.value]);
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Automatic color scheme"
|
||||
object: Config.general.color
|
||||
setting: "schemeGeneration"
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.general.color.schemeGeneration ? 0 : 1
|
||||
}
|
||||
|
||||
SchemesListView {
|
||||
name: "Color scheme presets"
|
||||
object: Config.colors.presets
|
||||
setting: "name"
|
||||
shouldBeActive: Config.general.color.schemeGeneration ? 0 : 1
|
||||
stringList: FetchPresets.presetNames()
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.colors.presets.name !== "" && !Config.general.color.schemeGeneration
|
||||
}
|
||||
|
||||
SchemesListView {
|
||||
name: "Preset variant"
|
||||
object: Config.colors.presets
|
||||
setting: "variant"
|
||||
shouldBeActive: Config.colors.presets.name !== "" && !Config.general.color.schemeGeneration
|
||||
stringList: FetchPresets.variantNames(Config.colors.presets.name)
|
||||
|
||||
onOptionSet: item => {
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${Config.colors.presets.name.toLowerCase()}:${item}`]);
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.colors.presets.variant !== "" && FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant).length > 0 && !Config.general.color.schemeGeneration
|
||||
}
|
||||
|
||||
SchemesListView {
|
||||
name: "Preset accent"
|
||||
object: Config.colors.presets
|
||||
setting: "accent"
|
||||
shouldBeActive: Config.colors.presets.variant !== "" && FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant).length > 0 && !Config.general.color.schemeGeneration
|
||||
stringList: FetchPresets.accents(Config.colors.presets.name, Config.colors.presets.variant)
|
||||
|
||||
onOptionSet: item => {
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${Config.colors.presets.name.toLowerCase()}:${Config.colors.presets.variant}`, "--accent", `${item}`]);
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Smart color scheme"
|
||||
object: Config.general.color
|
||||
setting: "smart"
|
||||
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0
|
||||
}
|
||||
|
||||
TimeInput {
|
||||
name: "Schedule dark mode"
|
||||
object: Config.general.color
|
||||
settings: ["scheduleDark", "scheduleDarkStart", "scheduleDarkEnd"]
|
||||
shouldBeActive: Config.general.color.schemeGeneration
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
HyprTimeInput {
|
||||
name: "Schedule Hyprsunset"
|
||||
object: Config.general.color
|
||||
settings: ["scheduleHyprsunset", "scheduleHyprsunsetStart", "scheduleHyprsunsetEnd", "hyprsunsetTemp"]
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 20000
|
||||
min: 1000
|
||||
name: "Hyprsunset temperature"
|
||||
object: Config.general.color
|
||||
setting: "hyprsunsetTemp"
|
||||
step: 200
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Default Apps"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Default Apps"
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add terminal command")
|
||||
name: "Terminal"
|
||||
object: Config.general.apps
|
||||
setting: "terminal"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add audio command")
|
||||
name: "Audio"
|
||||
object: Config.general.apps
|
||||
setting: "audio"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add playback command")
|
||||
name: "Playback"
|
||||
object: Config.general.apps
|
||||
setting: "playback"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingStringList {
|
||||
addLabel: qsTr("Add explorer command")
|
||||
name: "Explorer"
|
||||
object: Config.general.apps
|
||||
setting: "explorer"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Launcher"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Launcher"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Max apps shown"
|
||||
object: Config.launcher
|
||||
setting: "maxAppsShown"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Max wallpapers shown"
|
||||
object: Config.launcher
|
||||
setting: "maxWallpapers"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Action prefix"
|
||||
object: Config.launcher
|
||||
setting: "actionPrefix"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Special prefix"
|
||||
object: Config.launcher
|
||||
setting: "specialPrefix"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Use UWSM launch command"
|
||||
object: Config.launcher
|
||||
setting: "uwsm"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Fuzzy Search"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Fuzzy Search"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Apps"
|
||||
object: Config.launcher.useFuzzy
|
||||
setting: "apps"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Actions"
|
||||
object: Config.launcher.useFuzzy
|
||||
setting: "actions"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Schemes"
|
||||
object: Config.launcher.useFuzzy
|
||||
setting: "schemes"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Variants"
|
||||
object: Config.launcher.useFuzzy
|
||||
setting: "variants"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Wallpapers"
|
||||
object: Config.launcher.useFuzzy
|
||||
setting: "wallpapers"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Sizes"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Sizes"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Item width"
|
||||
object: Config.launcher.sizes
|
||||
setting: "itemWidth"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Item height"
|
||||
object: Config.launcher.sizes
|
||||
setting: "itemHeight"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Wallpaper width"
|
||||
object: Config.launcher.sizes
|
||||
setting: "wallpaperWidth"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Wallpaper height"
|
||||
object: Config.launcher.sizes
|
||||
setting: "wallpaperHeight"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Actions"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Actions"
|
||||
}
|
||||
|
||||
SettingActionList {
|
||||
name: "Launcher actions"
|
||||
object: Config.launcher
|
||||
setting: "actions"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import qs.Modules.Settings.Categories.Lockscreen
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
id: root
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Lockscreen"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Lockscreen"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Recolor logo"
|
||||
object: Config.lock
|
||||
setting: "recolorLogo"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable fingerprint"
|
||||
object: Config.lock
|
||||
setting: "enableFprint"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Max fingerprint tries"
|
||||
object: Config.lock
|
||||
setting: "maxFprintTries"
|
||||
step: 1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show notification details"
|
||||
object: Config.lock
|
||||
setting: "showNotifContent"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Show notification icon"
|
||||
object: Config.lock
|
||||
setting: "showNotifIcon"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 0
|
||||
name: "Blur amount"
|
||||
object: Config.lock
|
||||
setting: "blurAmount"
|
||||
step: 1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 2
|
||||
min: 0.1
|
||||
name: "Height multiplier"
|
||||
object: Config.lock.sizes
|
||||
setting: "heightMult"
|
||||
step: 0.05
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 4
|
||||
min: 0.5
|
||||
name: "Aspect ratio"
|
||||
object: Config.lock.sizes
|
||||
setting: "ratio"
|
||||
step: 0.05
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 100
|
||||
name: "Center width"
|
||||
object: Config.lock.sizes
|
||||
setting: "centerWidth"
|
||||
step: 10
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Greeter"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Greeter"
|
||||
}
|
||||
|
||||
SettingsIconButton {
|
||||
name: "Install wallpaper and color scheme to greeter"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Idle"
|
||||
|
||||
Idle {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Modules.Settings.Controls
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function addTimeoutEntry() {
|
||||
let list = [...Config.general.idle.timeouts];
|
||||
|
||||
list.push({
|
||||
name: "New Entry",
|
||||
timeout: 600,
|
||||
idleAction: "lock"
|
||||
});
|
||||
|
||||
Config.general.idle.timeouts = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function deleteTimeoutEntry(index) {
|
||||
let list = [...Config.general.idle.timeouts];
|
||||
list.splice(index, 1);
|
||||
Config.general.idle.timeouts = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function updateTimeoutEntry(i, key, value) {
|
||||
const list = [...Config.general.idle.timeouts];
|
||||
let entry = list[i];
|
||||
|
||||
entry[key] = value;
|
||||
list[i] = entry;
|
||||
|
||||
Config.general.idle.timeouts = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: shouldBeActive ? implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
spacing: Appearance.spacing.smaller
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
name: "Idle Monitors"
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [...Config.general.idle.timeouts]
|
||||
|
||||
SettingList {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
|
||||
onAddActiveActionRequested: {
|
||||
root.updateTimeoutEntry(index, "activeAction", "");
|
||||
}
|
||||
onDeleteRequested: function (index) {
|
||||
root.deleteTimeoutEntry(index);
|
||||
}
|
||||
onFieldEdited: function (key, value) {
|
||||
root.updateTimeoutEntry(index, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "add"
|
||||
|
||||
onClicked: root.addTimeoutEntry()
|
||||
}
|
||||
|
||||
component Settings: CustomRect {
|
||||
id: settingsItem
|
||||
|
||||
required property string name
|
||||
|
||||
Layout.preferredHeight: 60
|
||||
Layout.preferredWidth: 200
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
anchors.fill: parent
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.large * 2
|
||||
text: settingsItem.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Notifications"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Notifications"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Expire notifications"
|
||||
object: Config.notifs
|
||||
setting: "expire"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Default expire timeout"
|
||||
min: 0
|
||||
object: Config.notifs
|
||||
setting: "defaultExpireTimeout"
|
||||
step: 100
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "App notification cooldown"
|
||||
min: 0
|
||||
object: Config.notifs
|
||||
setting: "appNotifCooldown"
|
||||
step: 100
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Clear threshold"
|
||||
max: 1
|
||||
min: 0
|
||||
object: Config.notifs
|
||||
setting: "clearThreshold"
|
||||
step: 0.05
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Expand threshold"
|
||||
min: 0
|
||||
object: Config.notifs
|
||||
setting: "expandThreshold"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Action on click"
|
||||
object: Config.notifs
|
||||
setting: "actionOnClick"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Group preview count"
|
||||
min: 1
|
||||
object: Config.notifs
|
||||
setting: "groupPreviewNum"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Sizes"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Sizes"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Width"
|
||||
min: 1
|
||||
object: Config.notifs.sizes
|
||||
setting: "width"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Image size"
|
||||
min: 1
|
||||
object: Config.notifs.sizes
|
||||
setting: "image"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Badge size"
|
||||
min: 1
|
||||
object: Config.notifs.sizes
|
||||
setting: "badge"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "On Screen Display"
|
||||
|
||||
SettingsHeader {
|
||||
name: "On Screen Display"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable OSD"
|
||||
object: Config.osd
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Hide delay"
|
||||
min: 0
|
||||
object: Config.osd
|
||||
setting: "hideDelay"
|
||||
step: 100
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable brightness OSD"
|
||||
object: Config.osd
|
||||
setting: "enableBrightness"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable microphone OSD"
|
||||
object: Config.osd
|
||||
setting: "enableMicrophone"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Brightness on all monitors"
|
||||
object: Config.osd
|
||||
setting: "allMonBrightness"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Sizes"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Sizes"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Slider width"
|
||||
min: 1
|
||||
object: Config.osd.sizes
|
||||
setting: "sliderWidth"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Slider height"
|
||||
min: 1
|
||||
object: Config.osd.sizes
|
||||
setting: "sliderHeight"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Screenshot"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Screenshot"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable effects"
|
||||
object: Config.screenshot
|
||||
setting: "enable_pp"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
CustomSplitButtonRow {
|
||||
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
|
||||
enabled: Config.screenshot.enable_pp
|
||||
label: qsTr("Effects mode")
|
||||
|
||||
menuItems: [
|
||||
MenuItem {
|
||||
icon: "build"
|
||||
text: qsTr("Manual")
|
||||
value: "manual"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "rotate_auto"
|
||||
text: qsTr("Auto")
|
||||
value: "auto"
|
||||
}
|
||||
]
|
||||
|
||||
onSelected: item => {
|
||||
Config.screenshot.mode = item.value;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.screenshot.mode === "manual"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
enabled: Config.screenshot.enable_pp
|
||||
name: "Enable rounded corners"
|
||||
object: Config.screenshot
|
||||
setting: "rounding"
|
||||
shouldBeActive: Config.screenshot.mode === "manual"
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
enabled: Config.screenshot.enable_pp
|
||||
min: 0
|
||||
name: "Corner radius"
|
||||
object: Config.screenshot
|
||||
setting: "radius"
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
||||
step: 1
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.screenshot.mode === "manual"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
enabled: Config.screenshot.enable_pp
|
||||
name: "Enable shadow"
|
||||
object: Config.screenshot
|
||||
setting: "shadow"
|
||||
shouldBeActive: Config.screenshot.mode === "manual"
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
enabled: Config.screenshot.enable_pp
|
||||
min: 0
|
||||
name: "Shadow blur amount"
|
||||
object: Config.screenshot
|
||||
setting: "shadow_blur"
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
step: 1
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
}
|
||||
|
||||
// SettingSwitch {
|
||||
// name: "Shadow color broken atm"
|
||||
// object: Config.Screenshot
|
||||
// setting: "shadow_color"
|
||||
// shouldBeActive: Config.screenshot.mode === "manual"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// shouldBeActive: Config.screenshot.mode === "manual"
|
||||
// }
|
||||
|
||||
// SettingSpinBox {
|
||||
// min: 1
|
||||
// name: "Shadow passes"
|
||||
// object: Config.screenshot
|
||||
// setting: "shadow_blur_passes"
|
||||
// shouldBeActive: Config.screenshot.mode === "manual"
|
||||
// step: 1
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// shouldBeActive: Config.screenshot.mode === "manual"
|
||||
// }
|
||||
|
||||
SettingSpinBox {
|
||||
enabled: Config.screenshot.enable_pp
|
||||
min: 0
|
||||
name: "Shadow offset X"
|
||||
object: Config.screenshot
|
||||
setting: "shadow_offset_x"
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
step: 1
|
||||
}
|
||||
|
||||
Separator {
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
enabled: Config.screenshot.enable_pp
|
||||
min: 0
|
||||
name: "Shadow offset Y"
|
||||
object: Config.screenshot
|
||||
setting: "shadow_offset_y"
|
||||
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
step: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Services"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Services"
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Weather location"
|
||||
object: Config.services
|
||||
setting: "weatherLocation"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Check for updates"
|
||||
object: Config.services
|
||||
setting: "updates"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Use Fahrenheit"
|
||||
object: Config.services
|
||||
setting: "useFahrenheit"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Use twelve hour clock"
|
||||
object: Config.services
|
||||
setting: "useTwelveHourClock"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable ddcutil service"
|
||||
object: Config.services
|
||||
setting: "ddcutilService"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "GPU type"
|
||||
object: Config.services
|
||||
setting: "gpuType"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Media"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Media"
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 1
|
||||
min: 0
|
||||
name: "Audio increment"
|
||||
object: Config.services
|
||||
setting: "audioIncrement"
|
||||
step: 0.05
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 1
|
||||
min: 0
|
||||
name: "Brightness increment"
|
||||
object: Config.services
|
||||
setting: "brightnessIncrement"
|
||||
step: 0.05
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 1.0
|
||||
min: 0
|
||||
name: "Minimum brightness"
|
||||
object: Config.services
|
||||
setting: "minBrightness"
|
||||
step: 0.01
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 5
|
||||
min: 0
|
||||
name: "Max volume"
|
||||
object: Config.services
|
||||
setting: "maxVolume"
|
||||
step: 0.05
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingInput {
|
||||
name: "Default player"
|
||||
object: Config.services
|
||||
setting: "defaultPlayer"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Visualizer resolution"
|
||||
object: Config.services
|
||||
setting: "visualizerBars"
|
||||
step: 1
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingAliasList {
|
||||
name: "Player aliases"
|
||||
object: Config.services
|
||||
setting: "playerAliases"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Sidebar"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Sidebar"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable sidebar"
|
||||
object: Config.sidebar
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
name: "Width"
|
||||
min: 1
|
||||
object: Config.sidebar.sizes
|
||||
setting: "width"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick.Layouts
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.Controls
|
||||
|
||||
CustomClippingRect {
|
||||
id: root
|
||||
|
||||
radius: Appearance.rounding.normal - Appearance.padding.smaller
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
MaterialIcon {
|
||||
font.pointSize: Appearance.font.size.larger * 4
|
||||
text: "update"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
CustomText {
|
||||
font.pointSize: Appearance.font.size.large * 2
|
||||
text: "System updates"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: `${Updates.availableUpdates} available updates`
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 150
|
||||
color: Updates.updating ? DynamicColors.layer(DynamicColors.palette.m3outline, 2) : DynamicColors.palette.m3primary
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: Updates.updating ? "update" : "download"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
color: Updates.updating ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onPrimary
|
||||
text: "Update all"
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
enabled: !Updates.updating
|
||||
|
||||
onClicked: Updates.performSystemUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomListView {
|
||||
id: view
|
||||
|
||||
readonly property int itemHeight: 50 + Appearance.padding.smaller * 2
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
contentHeight: height
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
delegate: CustomRect {
|
||||
id: update
|
||||
|
||||
required property var modelData
|
||||
readonly property list<string> sections: modelData.update.split(" ")
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: view.itemHeight
|
||||
implicitWidth: parent.width
|
||||
radius: Appearance.rounding.small - Appearance.padding.small
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.smaller
|
||||
anchors.rightMargin: Appearance.padding.smaller
|
||||
|
||||
MaterialIcon {
|
||||
font.pointSize: Appearance.font.size.large * 2
|
||||
text: "package_2"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 25
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: update.sections[0]
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: Updates.formatUpdateTime(update.modelData.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 500
|
||||
|
||||
MarqueeText {
|
||||
id: versionFrom
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 225
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
marqueeEnabled: true
|
||||
pauseMs: 4000
|
||||
text: update.sections[1]
|
||||
width: 225
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.fillHeight: true
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "arrow_right_alt"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
MarqueeText {
|
||||
id: versionTo
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 225
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
marqueeEnabled: true
|
||||
pauseMs: 4000
|
||||
text: update.sections[3]
|
||||
width: 225
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
Layout.preferredHeight: width
|
||||
icon: "download"
|
||||
|
||||
onClicked: {
|
||||
Updates.performPackageUpdate(update.sections[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
id: script
|
||||
|
||||
objectProp: "update"
|
||||
values: Object.entries(Updates.updates).sort((a, b) => b[1] - a[1]).map(([update, timestamp]) => ({
|
||||
update,
|
||||
timestamp
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
import qs.Modules.Settings.Controls
|
||||
import qs.Config
|
||||
|
||||
SettingsPage {
|
||||
SettingsSection {
|
||||
sectionId: "Utilities"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Utilities"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable utilities"
|
||||
object: Config.utilities
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Max toasts"
|
||||
object: Config.utilities
|
||||
setting: "maxToasts"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Panel width"
|
||||
object: Config.utilities.sizes
|
||||
setting: "width"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
min: 1
|
||||
name: "Toast width"
|
||||
object: Config.utilities.sizes
|
||||
setting: "toastWidth"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Clipboard"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Clipboard"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Enable clipboard history viewer"
|
||||
object: Config.clipboard
|
||||
setting: "enabled"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 20
|
||||
min: 1
|
||||
name: "Max entries visible"
|
||||
object: Config.clipboard
|
||||
setting: "maxEntriesShown"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 80
|
||||
min: 30
|
||||
name: "Entry height"
|
||||
object: Config.clipboard.sizes
|
||||
setting: "itemHeight"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 700
|
||||
min: 300
|
||||
name: "Entry width"
|
||||
object: Config.clipboard.sizes
|
||||
setting: "width"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 1800
|
||||
min: 50
|
||||
name: "Minimum preview width"
|
||||
object: Config.clipboard.sizes
|
||||
setting: "minPreviewWidth"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSpinBox {
|
||||
max: 1800
|
||||
min: 50
|
||||
name: "Maximum preview width"
|
||||
object: Config.clipboard.sizes
|
||||
setting: "previewWidth"
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSection {
|
||||
sectionId: "Toasts"
|
||||
|
||||
SettingsHeader {
|
||||
name: "Toasts"
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Config loaded"
|
||||
object: Config.utilities.toasts
|
||||
setting: "configLoaded"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Charging changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "chargingChanged"
|
||||
}
|
||||
|
||||
Separator {
|
||||
}
|
||||
|
||||
SettingSwitch {
|
||||
name: "Game mode changed"
|
||||
object: Config.utilities.toasts
|
||||
setting: "gameModeChanged"
|
||||
}
|
||||
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Do not disturb changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "dndChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Audio output changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "audioOutputChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Audio input changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "audioInputChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Caps lock changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "capsLockChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Num lock changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "numLockChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Keyboard layout changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "kbLayoutChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "VPN changed"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "vpnChanged"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Now playing"
|
||||
// object: Config.utilities.toasts
|
||||
// setting: "nowPlaying"
|
||||
// }
|
||||
}
|
||||
|
||||
// SettingsSection {
|
||||
// sectionId: "VPN"
|
||||
//
|
||||
// SettingsHeader {
|
||||
// name: "VPN"
|
||||
// }
|
||||
//
|
||||
// SettingSwitch {
|
||||
// name: "Enable VPN integration"
|
||||
// object: Config.utilities.vpn
|
||||
// setting: "enabled"
|
||||
// }
|
||||
//
|
||||
// Separator {
|
||||
// }
|
||||
//
|
||||
// SettingStringList {
|
||||
// name: "Provider"
|
||||
// addLabel: qsTr("Add VPN provider")
|
||||
// object: Config.utilities.vpn
|
||||
// setting: "provider"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ItemList {
|
||||
id: root
|
||||
|
||||
property int currentId: -1
|
||||
property string iconName: "speaker"
|
||||
property var nodes: []
|
||||
|
||||
signal selected(node: PwNode)
|
||||
|
||||
last: true
|
||||
showList: true
|
||||
|
||||
delegate: Item {
|
||||
id: device
|
||||
|
||||
readonly property bool active: device.modelData?.id === root.currentId
|
||||
required property int index
|
||||
required property PwNode modelData
|
||||
|
||||
anchors.left: root.list.contentItem.left
|
||||
anchors.right: root.list.contentItem.right
|
||||
implicitHeight: deviceLayout.implicitHeight + deviceLayout.anchors.margins * 2
|
||||
|
||||
StateLayer {
|
||||
bottomLeftRadius: device.index === root?.list.count - 1 ? Appearance.rounding.large : radius
|
||||
bottomRightRadius: device.index === root?.list.count - 1 ? Appearance.rounding.large : radius
|
||||
radius: Appearance.rounding.extraSmall
|
||||
|
||||
onClicked: root.selected(device.modelData)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: deviceLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.rightMargin: Appearance.padding.largeIncreased
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomRect {
|
||||
color: device.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
|
||||
implicitHeight: devIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
id: devIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: device.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
|
||||
fill: device.active ? 1 : 0
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: root.iconName
|
||||
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
text: device.modelData?.description || device.modelData?.name || qsTr("Unknown")
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
opacity: device.active ? 1 : 0
|
||||
text: "check"
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: [...root.nodes].sort((a, b) => (a.description || a.name || "").localeCompare(b.description || b.name || ""))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import QtQuick
|
||||
import ZShell.Blobs
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real animDriver
|
||||
property alias color: blobGroup.color
|
||||
default required property Item content
|
||||
property bool hoverOverride
|
||||
readonly property alias hovered: btn.containsMouse
|
||||
property alias icon: icon.text
|
||||
property bool open
|
||||
property int padding
|
||||
property bool pressOverride
|
||||
property int topMovement: Appearance.padding.large
|
||||
|
||||
implicitHeight: btn.implicitHeight * 0.9
|
||||
implicitWidth: btn.implicitWidth * 0.9
|
||||
|
||||
Binding {
|
||||
property: "opacity"
|
||||
target: root.content
|
||||
value: root.animDriver
|
||||
}
|
||||
|
||||
BlobGroup {
|
||||
id: blobGroup
|
||||
|
||||
color: DynamicColors.palette.m3surfaceContainerHighest
|
||||
cornerFill: false
|
||||
smoothing: Appearance.rounding.medium
|
||||
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlobRect {
|
||||
id: btnRect
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: (!(btn.pressed || root.pressOverride) && (btn.containsMouse || root.hoverOverride) ? -Appearance.padding.extraSmall : 0) + (root.open ? -Appearance.padding.extraSmall : 0)
|
||||
group: blobGroup
|
||||
radius: root.open ? Appearance.rounding.large : Appearance.rounding.medium
|
||||
|
||||
Behavior on anchors.margins {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlobRect {
|
||||
id: rect
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
deformScale: 0.00001
|
||||
group: blobGroup
|
||||
implicitHeight: parent.height
|
||||
implicitWidth: parent.width
|
||||
radius: Appearance.rounding.small + Appearance.padding.small
|
||||
|
||||
states: State {
|
||||
name: "open"
|
||||
when: root.open
|
||||
|
||||
PropertyChanges {
|
||||
rect.anchors.rightMargin: root.width - Appearance.spacing.small
|
||||
rect.anchors.topMargin: -root.topMovement
|
||||
rect.implicitHeight: root.content.implicitHeight + root.padding * 2
|
||||
rect.implicitWidth: root.content.implicitWidth + root.padding * 2
|
||||
root.animDriver: 1
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
Anim {
|
||||
properties: "rightMargin,implicitWidth"
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastEffects
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastEffects
|
||||
properties: "topMargin,implicitHeight"
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "animDriver"
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // MouseArea to catch inputs
|
||||
anchors.fill: parent
|
||||
children: [root.content]
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: btn
|
||||
|
||||
anchors.centerIn: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.extraSmall * 2
|
||||
implicitWidth: implicitHeight
|
||||
|
||||
onClicked: root.open = !root.open
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
text: "view_apps"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import QtQuick
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
property bool first
|
||||
property bool last
|
||||
|
||||
bottomLeftRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||
bottomRightRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
topLeftRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||
topRightRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property string icon
|
||||
property color iconColor: DynamicColors.palette.m3onSurfaceVariant
|
||||
property alias label: label.text
|
||||
property Component leadingComponent: icon ? iconComp : null
|
||||
property string subtext
|
||||
property alias value: value.text
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: rowLayout.implicitHeight + rowLayout.anchors.margins * 2
|
||||
|
||||
Component {
|
||||
id: iconComp
|
||||
|
||||
MaterialIcon {
|
||||
color: root.iconColor
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.icon
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
anchors.margins: Appearance.padding.larger
|
||||
anchors.rightMargin: Appearance.padding.largeIncreased
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
Loader {
|
||||
active: root.leadingComponent
|
||||
sourceComponent: root.leadingComponent
|
||||
visible: root.leadingComponent
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.subtext
|
||||
visible: root.subtext
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: value
|
||||
|
||||
Layout.maximumWidth: root.width / 2
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property alias delegate: list.delegate
|
||||
property int extraHeight
|
||||
readonly property alias list: list
|
||||
property alias model: list.model
|
||||
property string placeholderIcon
|
||||
property string placeholderText
|
||||
property bool showList
|
||||
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: (showList && list.count > 0 ? list.contentHeight : placeholder.implicitHeight + Appearance.padding.extraLarge * 2) + extraHeight
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: placeholder
|
||||
|
||||
active: opacity > 0
|
||||
anchors.centerIn: parent
|
||||
opacity: root.showList && list.count > 0 ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.extraSmall
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: root.placeholderIcon
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: root.placeholderText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: list
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
interactive: false
|
||||
opacity: root.showList ? 1 : 0
|
||||
spacing: 0
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 1
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 1
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 1
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
remove: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property alias icon: icon.text
|
||||
property alias status: status.text
|
||||
property alias text: label.text
|
||||
|
||||
signal clicked
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: navLayout.implicitHeight + navLayout.anchors.margins * 2
|
||||
|
||||
StateLayer {
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: navLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.largeIncreased
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: status
|
||||
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
visible: text
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
text: "chevron_right"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Settings
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property alias checked: switchButton.checked
|
||||
property int horizontalPadding: Appearance.padding.largeIncreased
|
||||
required property Component popup
|
||||
property alias subtext: subtext.text
|
||||
property alias text: text.text
|
||||
property int verticalPadding: Appearance.padding.normal
|
||||
|
||||
signal clicked(checked: bool)
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight + verticalPadding * 2
|
||||
|
||||
Column {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.horizontalPadding
|
||||
anchors.right: icon.left
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: subtext
|
||||
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.right: switchButton.left
|
||||
anchors.rightMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "open_in_new"
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
onClicked: PopupManager.requestOpen(root.popup)
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
id: switchButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.horizontalPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
onClicked: root.clicked(checked)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Modules.Settings
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
readonly property int cappedWidth: Math.min(800, width)
|
||||
default property Item contentChild
|
||||
readonly property alias flickable: flickable
|
||||
property bool isSubPage
|
||||
required property SettingsState sState
|
||||
required property string title
|
||||
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
MouseArea { // Prevent clicks from reaching flickable
|
||||
Layout.bottomMargin: -flickable.topMargin // Extra height to block clicks on flickable top margin
|
||||
|
||||
implicitHeight: header.implicitHeight - Layout.bottomMargin
|
||||
implicitWidth: header.implicitWidth
|
||||
z: 1
|
||||
|
||||
onClicked: focus = true
|
||||
|
||||
RowLayout {
|
||||
id: header
|
||||
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
Loader {
|
||||
active: root.isSubPage
|
||||
asynchronous: true
|
||||
visible: active
|
||||
|
||||
sourceComponent: IconButton {
|
||||
icon: "arrow_back"
|
||||
inactiveColor: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
inactiveOnColor: DynamicColors.palette.m3onSurfaceVariant
|
||||
isRound: true
|
||||
type: IconButton.Tonal
|
||||
|
||||
onClicked: root.sState.closeSubPage()
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: root.title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalFadeFlickable {
|
||||
id: flickable
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -topMargin
|
||||
bottomMargin: Appearance.padding.extraLarge
|
||||
contentHeight: root.contentChild?.implicitHeight ?? 0
|
||||
contentItem.children: [root.contentChild]
|
||||
fadeAmount: 0.1
|
||||
topMargin: Appearance.padding.large
|
||||
|
||||
TapHandler {
|
||||
onTapped: flickable.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Drawers
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
default required property Item content
|
||||
property alias icon: icon.text
|
||||
property bool keepPopupAsChild
|
||||
readonly property alias popup: popup
|
||||
property alias status: status.text
|
||||
property alias text: label.text
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: navLayout.implicitHeight + navLayout.anchors.margins * 2
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
manualHoverOverride: popup.hovered && !popup.open
|
||||
|
||||
onClicked: popup.open = true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: navLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.largeIncreased
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: status
|
||||
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
visible: text
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: triggerArea
|
||||
|
||||
implicitHeight: popup.implicitHeight
|
||||
implicitWidth: popup.implicitWidth
|
||||
|
||||
TransformWatcher {
|
||||
id: tWatcher
|
||||
|
||||
a: area.parent
|
||||
b: triggerArea
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: area
|
||||
|
||||
anchors.fill: parent
|
||||
cursorShape: popup.open ? Qt.ArrowCursor : undefined
|
||||
enabled: popup.open
|
||||
hoverEnabled: popup.open
|
||||
parent: {
|
||||
if (root.keepPopupAsChild)
|
||||
return triggerArea;
|
||||
|
||||
const win = QsWindow.window;
|
||||
const contentWin = win as Windows; // If inside the drawer content window, put it inside the interaction wrapper so hover works
|
||||
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
|
||||
}
|
||||
z: popup.animDriver > 0 ? 1 : 0
|
||||
|
||||
onClicked: popup.open = false
|
||||
|
||||
BlobPopup {
|
||||
id: popup
|
||||
|
||||
color: open || hovered || stateLayer.containsMouse ? DynamicColors.palette.m3secondaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
content: root.content
|
||||
hoverOverride: stateLayer.containsMouse
|
||||
padding: Appearance.padding.small
|
||||
pressOverride: stateLayer.pressed
|
||||
x: {
|
||||
tWatcher.transform;
|
||||
return triggerArea.mapToItem(area.parent, 0, 0).x;
|
||||
}
|
||||
y: {
|
||||
tWatcher.transform;
|
||||
return triggerArea.mapToItem(area.parent, 0, 0).y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomText {
|
||||
property bool first
|
||||
|
||||
Layout.bottomMargin: Appearance.spacing.extraSmall
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.padding.small
|
||||
Layout.topMargin: first ? 0 : Appearance.spacing.large - ((parent as ColumnLayout).spacing ?? 0)
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property alias active: splitButton.active
|
||||
property alias fallbackIcon: splitButton.fallbackIcon
|
||||
property alias fallbackText: splitButton.fallbackText
|
||||
property alias menuItems: splitButton.menuItems
|
||||
property alias menuOnTop: splitButton.menuOnTop
|
||||
property string subtext
|
||||
property alias text: label.text
|
||||
|
||||
signal selected(item: MenuItem)
|
||||
|
||||
Layout.fillWidth: true
|
||||
clip: false
|
||||
implicitHeight: rowLayout.implicitHeight + rowLayout.anchors.margins * 2
|
||||
z: splitButton.expanded ? 1 : 0
|
||||
|
||||
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
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.subtext
|
||||
visible: root.subtext
|
||||
}
|
||||
}
|
||||
|
||||
CustomSplitButton {
|
||||
id: splitButton
|
||||
|
||||
type: CustomSplitButton.Tonal
|
||||
|
||||
menu.onItemSelected: item => root.selected(item)
|
||||
stateLayer.onClicked: splitButton.expanded = !splitButton.expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property alias icon: slider.insetIcon
|
||||
property alias label: label.text
|
||||
property real value
|
||||
property alias valueLabel: valueLabel.text
|
||||
|
||||
signal moved(value: real)
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: rowLayout.implicitHeight + rowLayout.anchors.margins + rowLayout.anchors.topMargin
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.largeIncreased
|
||||
anchors.topMargin: Appearance.padding.large
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: valueLabel
|
||||
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
}
|
||||
|
||||
CustomMouseArea {
|
||||
function onWheel(event: WheelEvent): void {
|
||||
const step = Config.services.audioIncrement;
|
||||
if (event.angleDelta.y > 0)
|
||||
root.moved(Math.min(1, root.value + step));
|
||||
else if (event.angleDelta.y < 0)
|
||||
root.moved(Math.max(0, root.value - step));
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Appearance.padding.larger * 3
|
||||
|
||||
CustomSlider {
|
||||
id: slider
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: root.enabled
|
||||
implicitHeight: parent.implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
value: root.value
|
||||
|
||||
onInteraction: v => root.moved(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property real from: 0
|
||||
property real stepSize: 1
|
||||
property string subtext
|
||||
property alias text: label.text
|
||||
property real to: 99
|
||||
property real value
|
||||
|
||||
signal moved(value: real)
|
||||
|
||||
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
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.subtext
|
||||
visible: root.subtext
|
||||
}
|
||||
}
|
||||
|
||||
CustomSpinBox {
|
||||
from: root.from
|
||||
stepSize: root.stepSize
|
||||
to: root.to
|
||||
value: root.value
|
||||
|
||||
onValueModified: root.moved(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Components
|
||||
import qs.Modules.Settings
|
||||
import qs.Config
|
||||
|
||||
StackView {
|
||||
id: root
|
||||
|
||||
readonly property int animMovement: Appearance.padding.extraLarge * 2
|
||||
default property list<Component> pages
|
||||
required property SettingsState sState
|
||||
|
||||
function openSubPage(idx: int, immediate: bool): void {
|
||||
const page = pages[idx];
|
||||
if (page) {
|
||||
push(page, {
|
||||
sState
|
||||
}, immediate ? StackView.Immediate : StackView.PushTransition);
|
||||
} else {
|
||||
console.warn(logCat, "Attempted to open invalid sub-page index", idx);
|
||||
sState.closeSubPage();
|
||||
}
|
||||
}
|
||||
|
||||
clip: busy
|
||||
|
||||
popEnter: Transition {
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
property: "opacity"
|
||||
value: 0
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.expressiveEffects
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 1
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
|
||||
Anim {
|
||||
from: -root.animMovement
|
||||
property: "x"
|
||||
to: 0
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
popExit: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
pushEnter: Transition {
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
property: "opacity"
|
||||
value: 0
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.expressiveEffects
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 1
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
|
||||
Anim {
|
||||
from: root.animMovement
|
||||
property: "x"
|
||||
to: 0
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pushExit: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
openSubPage(0, true);
|
||||
for (const page of sState.subPageIdxStack)
|
||||
openSubPage(page, true);
|
||||
}
|
||||
|
||||
LoggingCategory {
|
||||
id: logCat
|
||||
|
||||
defaultLogLevel: LoggingCategory.Info
|
||||
name: "ZShell.settings"
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onSubPageClosed(): void {
|
||||
if (root.depth < root.sState.subPageIdxStack.length) {
|
||||
console.log(logCat, "Attempted to close page while depth < stack depth. Ignoring.");
|
||||
return;
|
||||
}
|
||||
root.pop();
|
||||
}
|
||||
|
||||
function onSubPageOpened(idx: int): void {
|
||||
root.openSubPage(idx, false);
|
||||
}
|
||||
|
||||
target: root.sState
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
|
||||
CustomClippingRect {
|
||||
id: root
|
||||
|
||||
required property var object
|
||||
required property list<string> settings
|
||||
property bool shouldBeActive: true
|
||||
|
||||
signal applySettings(startTime: int, endTime: int)
|
||||
signal close
|
||||
|
||||
function convertHour(timeValue: int): int {
|
||||
return Math.floor(timeValue / 60);
|
||||
}
|
||||
|
||||
function convertMinute(timeValue: int): int {
|
||||
return timeValue % 60;
|
||||
}
|
||||
|
||||
function convertToMinutes(hour: int, minute: int): int {
|
||||
return hour * 60 + minute;
|
||||
}
|
||||
|
||||
color: DynamicColors.palette.m3surfaceContainer
|
||||
implicitHeight: column.implicitHeight + column.anchors.margins * 2 + buttonRow.implicitHeight + buttonRow.anchors.topMargin
|
||||
implicitWidth: column.implicitWidth + column.anchors.margins * 2
|
||||
radius: Appearance.rounding.large
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.largeIncreased
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomText {
|
||||
text: qsTr("Select time")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CustomRect {
|
||||
id: startHourRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: startHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: startHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: startHourField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldBase {
|
||||
id: startHourField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertHour(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[1])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (startHourField.text.length >= 2) {
|
||||
startHourField.text = "0" + startHourField.text[0];
|
||||
} else if (startHourField.text.length === 1) {
|
||||
startHourField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
startHourField.text = setConfigText(root.settings[1]);
|
||||
startHourField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
startHourField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
startMinuteField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
endMinuteField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = startHourField.text.length;
|
||||
|
||||
if (textLen >= 2 && startHourField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(startHourField.text + digit);
|
||||
} else {
|
||||
val = parseInt(startHourField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
startHourField.text = "0" + val;
|
||||
} else {
|
||||
startHourField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onTextEdited: {
|
||||
if (startHourField.text === "")
|
||||
return;
|
||||
var val = parseInt(startHourField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== startHourField.text)
|
||||
startHourField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: startSeparator
|
||||
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: startMinuteRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: startMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: startMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: startMinuteField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldBase {
|
||||
id: startMinuteField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertMinute(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[1])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (startMinuteField.text.length >= 2) {
|
||||
startMinuteField.text = "0" + startMinuteField.text[0];
|
||||
} else if (startMinuteField.text.length === 1) {
|
||||
startMinuteField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
startMinuteField.text = setConfigText(root.settings[1]);
|
||||
startMinuteField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
startMinuteField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
endHourField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
startHourField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = startMinuteField.text.length;
|
||||
|
||||
if (textLen >= 2 && startMinuteField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(startMinuteField.text + digit);
|
||||
} else {
|
||||
val = parseInt(startMinuteField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(59, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
startMinuteField.text = "0" + val;
|
||||
} else {
|
||||
startMinuteField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onTextEdited: {
|
||||
if (startMinuteField.text === "")
|
||||
return;
|
||||
var val = parseInt(startMinuteField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== startMinuteField.text)
|
||||
startMinuteField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: spacer
|
||||
|
||||
Layout.preferredWidth: Appearance.spacing.large
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: endHourRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: endHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: endHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: endHourField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldBase {
|
||||
id: endHourField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertHour(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[2])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (endHourField.text.length >= 2) {
|
||||
endHourField.text = "0" + endHourField.text[0];
|
||||
} else if (endHourField.text.length === 1) {
|
||||
endHourField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
endHourField.text = setConfigText(root.settings[2]);
|
||||
endHourField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
endHourField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
endMinuteField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
startMinuteField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = endHourField.text.length;
|
||||
|
||||
if (textLen >= 2 && endHourField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(endHourField.text + digit);
|
||||
} else {
|
||||
val = parseInt(endHourField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
endHourField.text = "0" + val;
|
||||
} else {
|
||||
endHourField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onTextEdited: {
|
||||
if (endHourField.text === "")
|
||||
return;
|
||||
var val = parseInt(endHourField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== endHourField.text)
|
||||
endHourField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: endSeparator
|
||||
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: endMinuteRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: endMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: endMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: endMinuteField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldBase {
|
||||
id: endMinuteField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertMinute(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[2])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (endMinuteField.text.length >= 2) {
|
||||
endMinuteField.text = "0" + endMinuteField.text[0];
|
||||
} else if (endMinuteField.text.length === 1) {
|
||||
endMinuteField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
endMinuteField.text = setConfigText(root.settings[2]);
|
||||
endMinuteField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
endMinuteField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
startHourField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
endHourField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = endMinuteField.text.length;
|
||||
|
||||
if (textLen >= 2 && endMinuteField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(endMinuteField.text + digit);
|
||||
} else {
|
||||
val = parseInt(endMinuteField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(59, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
endMinuteField.text = "0" + val;
|
||||
} else {
|
||||
endMinuteField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onTextEdited: {
|
||||
if (endMinuteField.text === "")
|
||||
return;
|
||||
var val = parseInt(endMinuteField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== endMinuteField.text)
|
||||
endMinuteField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CustomText {
|
||||
Layout.preferredWidth: spacer.x + spacer.width
|
||||
text: qsTr("Start")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: endMinuteRect.width + endHourRect.width
|
||||
text: qsTr("End")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttonRow
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.largeIncreased
|
||||
anchors.right: parent.right
|
||||
anchors.top: column.bottom
|
||||
anchors.topMargin: Appearance.spacing.normal
|
||||
|
||||
Item {
|
||||
id: buttonSpacer
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ButtonRow {
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
IconTextButton {
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
icon: "close"
|
||||
inactiveColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
inactiveOnColor: DynamicColors.palette.m3onSurfaceVariant
|
||||
isRound: true
|
||||
isToggle: false
|
||||
shapeMorph: true
|
||||
shapeMorphExpansion: pressed ? 12 : 0
|
||||
text: "Cancel"
|
||||
|
||||
onClicked: root.close()
|
||||
}
|
||||
|
||||
IconTextButton {
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
icon: "check"
|
||||
isRound: true
|
||||
isToggle: false
|
||||
shapeMorph: true
|
||||
shapeMorphExpansion: pressed ? 12 : 0
|
||||
text: "Apply"
|
||||
|
||||
onClicked: {
|
||||
const start = root.convertToMinutes(parseInt(startHourField.text)) + parseInt(startMinuteField.text);
|
||||
const end = root.convertToMinutes(parseInt(endHourField.text)) + parseInt(endMinuteField.text);
|
||||
root.applySettings(start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomSwitch {
|
||||
id: root
|
||||
|
||||
readonly property alias bg: bg
|
||||
property alias first: bg.first
|
||||
property alias last: bg.last
|
||||
property string subtext
|
||||
|
||||
Layout.fillWidth: true
|
||||
cLayer: 2
|
||||
horizontalPadding: Appearance.padding.largeIncreased
|
||||
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + verticalPadding * 2
|
||||
implicitWidth: implicitContentWidth + implicitIndicatorWidth + horizontalPadding * 2
|
||||
indicator.anchors.right: right
|
||||
indicator.anchors.rightMargin: root.horizontalPadding
|
||||
indicator.anchors.verticalCenter: verticalCenter
|
||||
verticalPadding: Appearance.padding.normal
|
||||
|
||||
background: ConnectedRect {
|
||||
id: bg
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
manualPressOverride: root.pressed
|
||||
}
|
||||
}
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.horizontalPadding
|
||||
anchors.right: root.indicator.left
|
||||
anchors.rightMargin: Appearance.spacing.normal
|
||||
implicitHeight: column.implicitHeight
|
||||
implicitWidth: column.implicitWidth
|
||||
|
||||
Column {
|
||||
id: column
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
elide: Text.ElideRight
|
||||
font: {
|
||||
const f = Qt.font(label.font);
|
||||
f.pointSize = Appearance.font.size.smaller;
|
||||
return f;
|
||||
}
|
||||
text: root.text
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: subtext
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font: {
|
||||
const f = Qt.font(subtext.font);
|
||||
f.pointSize = Appearance.font.size.small;
|
||||
return f;
|
||||
}
|
||||
text: root.subtext
|
||||
visible: root.subtext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: stateLayer.press(stateLayer.mouseX, stateLayer.mouseY)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias imgHeight: imgWrapper.implicitHeight
|
||||
property alias radius: imgWrapper.radius
|
||||
property alias source: img.source
|
||||
|
||||
signal clicked
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomClippingRect {
|
||||
id: imgWrapper
|
||||
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: width
|
||||
radius: Appearance.rounding.large
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
retainWhileLoading: true
|
||||
sourceSize: {
|
||||
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
|
||||
return Qt.size(width * dpr, height * dpr);
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
anchors.bottomMargin: layout.implicitHeight - imgWrapper.implicitHeight
|
||||
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import ZShell.Components
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: wrapper
|
||||
|
||||
property int buttonRowHeight: Appearance.padding.large * 3
|
||||
property bool changesMade: false
|
||||
readonly property var currentScreen: screens.length > selectedScreenIndex ? screens[selectedScreenIndex] : null
|
||||
property var screens: []
|
||||
property int selectedScreenIndex: 0
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function applyCrop(): void {
|
||||
if (!cropRectLoader.item || !currentScreen)
|
||||
return;
|
||||
|
||||
const cropRect = cropRectLoader.item;
|
||||
|
||||
const cropXPercent = (cropRect.x - cropRect.imageX) / scaledImg.paintedWidth;
|
||||
const cropYPercent = (cropRect.y - cropRect.imageY) / scaledImg.paintedHeight;
|
||||
const cropWidthPercent = cropRect.width / scaledImg.paintedWidth;
|
||||
const cropHeightPercent = cropRect.height / scaledImg.paintedHeight;
|
||||
|
||||
Wallpapers.setCrop(currentScreen.name, Qt.rect(cropXPercent, cropYPercent, cropWidthPercent, cropHeightPercent), cropRect.zoom);
|
||||
}
|
||||
|
||||
function refreshScreens(): void {
|
||||
screens = [...Quickshell.screens].sort((a, b) => a.x - b.x);
|
||||
selectedScreenIndex = 0;
|
||||
}
|
||||
|
||||
function selectScreen(index: int): void {
|
||||
if (index < 0 || index >= screens.length || index === selectedScreenIndex)
|
||||
return;
|
||||
|
||||
selectedScreenIndex = index;
|
||||
|
||||
if (cropRectLoader.item)
|
||||
Qt.callLater(syncCropToScreen);
|
||||
}
|
||||
|
||||
function syncCropToScreen(): void {
|
||||
if (cropRectLoader.item)
|
||||
cropRectLoader.item.restoreFromData();
|
||||
}
|
||||
|
||||
function zoomClipRect(zoom: real): void {
|
||||
if (!cropRectLoader.item)
|
||||
return;
|
||||
|
||||
const cropRect = cropRectLoader.item;
|
||||
|
||||
const centerX = cropRect.x + cropRect.width * 0.5;
|
||||
const centerY = cropRect.y + cropRect.height * 0.5;
|
||||
|
||||
cropRect.zoom = zoom;
|
||||
|
||||
cropRect.x = centerX - cropRect.width * 0.5;
|
||||
cropRect.y = centerY - cropRect.height * 0.5;
|
||||
|
||||
cropRect.clampToBounds();
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? 430 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
refreshScreens();
|
||||
Qt.callLater(syncCropToScreen);
|
||||
}
|
||||
onSelectedScreenIndexChanged: {
|
||||
Qt.callLater(syncCropToScreen);
|
||||
}
|
||||
|
||||
IconButton {
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
icon: "check"
|
||||
opacity: wrapper.changesMade ? 1 : 0
|
||||
scale: wrapper.changesMade ? 1 : 0
|
||||
z: 2
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
wrapper.applyCrop();
|
||||
wrapper.changesMade = false;
|
||||
}
|
||||
}
|
||||
|
||||
ButtonRow {
|
||||
id: screenSelector
|
||||
|
||||
anchors.bottom: scaledImg.top
|
||||
anchors.bottomMargin: Appearance.spacing.normal
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Appearance.padding.extraLarge * 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Appearance.padding.extraLarge * 2
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
Repeater {
|
||||
model: wrapper.screens
|
||||
|
||||
delegate: TextButton {
|
||||
required property int index
|
||||
readonly property bool isCurrent: wrapper.selectedScreenIndex === index
|
||||
required property var modelData
|
||||
|
||||
fillWidth: true
|
||||
inactiveColor: isCurrent ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3surfaceContainer, 0.7)
|
||||
inactiveOnColor: isCurrent ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.7)
|
||||
isRound: true
|
||||
shapeMorph: true
|
||||
text: modelData.name
|
||||
|
||||
onClicked: wrapper.selectScreen(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: sliderLayout
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: 30
|
||||
|
||||
CustomSlider {
|
||||
id: zoomSlider
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.padding.normal
|
||||
Layout.preferredHeight: Appearance.padding.larger * 3
|
||||
Layout.rightMargin: Appearance.padding.normal
|
||||
from: 1.0
|
||||
implicitHeight: Appearance.padding.larger * 3
|
||||
insetIcon: "crop"
|
||||
to: 5.0
|
||||
value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0
|
||||
|
||||
onInteraction: value => {
|
||||
wrapper.zoomClipRect(1 + (value * 4));
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: scaledImg
|
||||
|
||||
property var displayData
|
||||
property real monitorScale: 1.0
|
||||
|
||||
anchors.bottom: sliderLayout.top
|
||||
anchors.bottomMargin: Appearance.spacing.normal
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: wrapper.buttonRowHeight + Appearance.spacing.normal
|
||||
// anchors.top: screenSelector.bottom
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
retainWhileLoading: true
|
||||
source: Wallpapers.current
|
||||
sourceSize.height: parent.height
|
||||
sourceSize.width: parent.width
|
||||
|
||||
onPaintedWidthChanged: {
|
||||
if (paintedWidth > 0 && cropRectLoader.item) {
|
||||
if (!wrapper.screens.length)
|
||||
wrapper.refreshScreens();
|
||||
|
||||
wrapper.syncCropToScreen();
|
||||
}
|
||||
}
|
||||
onSourceChanged: {
|
||||
if (cropRectLoader.item) {
|
||||
if (!wrapper.screens.length)
|
||||
wrapper.refreshScreens();
|
||||
|
||||
wrapper.syncCropToScreen();
|
||||
}
|
||||
}
|
||||
onStatusChanged: {
|
||||
if (scaledImg.status == Image.Ready && cropRectLoader.item) {
|
||||
if (!wrapper.screens.length)
|
||||
wrapper.refreshScreens();
|
||||
|
||||
wrapper.syncCropToScreen();
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: cropRectLoader
|
||||
|
||||
active: scaledImg.status === Image.Ready
|
||||
|
||||
sourceComponent: Component {
|
||||
CustomRect {
|
||||
id: cropRect
|
||||
|
||||
property real aspectRatio: wrapper.currentScreen ? wrapper.currentScreen.width / wrapper.currentScreen.height : 1
|
||||
readonly property real baseHeight: baseWidth / aspectRatio
|
||||
readonly property real baseWidth: {
|
||||
let fittedHeight = scaledImg.paintedHeight;
|
||||
let fittedWidth = fittedHeight * aspectRatio;
|
||||
|
||||
if (fittedWidth > scaledImg.paintedWidth) {
|
||||
fittedWidth = scaledImg.paintedWidth;
|
||||
fittedHeight = fittedWidth / aspectRatio;
|
||||
}
|
||||
|
||||
return fittedWidth;
|
||||
}
|
||||
readonly property real imageX: (scaledImg.width - scaledImg.paintedWidth) / 2
|
||||
readonly property real imageY: (scaledImg.height - scaledImg.paintedHeight) / 2
|
||||
property real imgAspectRatio: scaledImg.paintedWidth / scaledImg.paintedHeight
|
||||
property real zoom: 1.0
|
||||
|
||||
function centerInImage() {
|
||||
x = imageX + (scaledImg.paintedWidth - width) / 2;
|
||||
y = imageY + (scaledImg.paintedHeight - height) / 2;
|
||||
}
|
||||
|
||||
function clampToBounds() {
|
||||
x = Math.max(imageX, Math.min(x, imageX + scaledImg.paintedWidth - width));
|
||||
y = Math.max(imageY, Math.min(y, imageY + scaledImg.paintedHeight - height));
|
||||
}
|
||||
|
||||
function restoreFromData() {
|
||||
let data = Wallpapers.getCrop(wrapper.currentScreen.name);
|
||||
|
||||
console.log(data.x, data.y);
|
||||
|
||||
if (data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001)) {
|
||||
zoom = data.zoom > 0 ? data.zoom : 1.0;
|
||||
x = imageX + (data.x * scaledImg.paintedWidth);
|
||||
y = imageY + (data.y * scaledImg.paintedHeight);
|
||||
|
||||
clampToBounds();
|
||||
} else {
|
||||
zoom = 1.0;
|
||||
centerInImage();
|
||||
}
|
||||
}
|
||||
|
||||
border.color: DynamicColors.palette.m3primary
|
||||
border.width: 2
|
||||
height: baseHeight / zoom
|
||||
opacity: 1
|
||||
width: baseWidth / zoom
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
restoreFromData();
|
||||
}
|
||||
onHeightChanged: clampToBounds()
|
||||
onWidthChanged: clampToBounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
function updateCrop(mouseX, mouseY) {
|
||||
if (!cropRectLoader.item)
|
||||
return;
|
||||
|
||||
const cropRect = cropRectLoader.item;
|
||||
|
||||
let nx = mouseX - cropRect.width * 0.5;
|
||||
let ny = mouseY - cropRect.height * 0.5;
|
||||
|
||||
nx = Math.max(cropRect.imageX, Math.min(nx, cropRect.imageX + scaledImg.paintedWidth - cropRect.width));
|
||||
ny = Math.max(cropRect.imageY, Math.min(ny, cropRect.imageY + scaledImg.paintedHeight - cropRect.height));
|
||||
|
||||
cropRect.x = nx;
|
||||
cropRect.y = ny;
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (pressed) {
|
||||
updateCrop(mouse.x, mouse.y);
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
}
|
||||
onPressed: mouse => {
|
||||
updateCrop(mouse.x, mouse.y);
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
onReleased: {
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
-369
@@ -1,398 +1,76 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ZShell.Blobs
|
||||
import qs.Components
|
||||
import qs.Modules as Modules
|
||||
import qs.Modules.Settings.Categories as Cat
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
CustomClippingRect {
|
||||
id: root
|
||||
|
||||
property string currentCategory: "general"
|
||||
property int currentIndex: 0
|
||||
property int lastIndex: 0
|
||||
readonly property real nonAnimHeight: Math.floor(screen.height / 1.5) + viewWrapper.anchors.margins * 2
|
||||
readonly property real nonAnimWidth: view.implicitWidth + Math.floor(screen.width / 2) + viewWrapper.anchors.margins * 2
|
||||
property string pendingSection: ""
|
||||
property string pendingSetting: ""
|
||||
required property ShellScreen screen
|
||||
required property PersistentProperties visibilities
|
||||
property color blobColor: DynamicColors.tPalette.m3surfaceContainerLow
|
||||
readonly property real ratio: 16.0 / 9.0
|
||||
readonly property SettingsState sState: SettingsState {
|
||||
id: sState
|
||||
|
||||
function scrollToSetting(section: string, settingName: string) {
|
||||
root.pendingSection = section;
|
||||
root.pendingSetting = settingName;
|
||||
scrollTimer.restart();
|
||||
onClose: root.close()
|
||||
}
|
||||
|
||||
function setCategory(category: string, index: int): void {
|
||||
currentIndex = index;
|
||||
currentCategory = category;
|
||||
}
|
||||
signal close
|
||||
|
||||
implicitHeight: nonAnimHeight
|
||||
implicitWidth: nonAnimWidth
|
||||
implicitHeight: sState.screen.height * 0.7
|
||||
implicitWidth: implicitHeight * ratio
|
||||
radius: Appearance.rounding.large + Appearance.padding.normal
|
||||
|
||||
Timer {
|
||||
id: scrollTimer
|
||||
|
||||
interval: 50
|
||||
|
||||
onTriggered: {
|
||||
if (root.pendingSection && stack.currentItem) {
|
||||
stack.currentItem.scrollToSectionAndHighlight(root.pendingSection, root.pendingSetting);
|
||||
root.pendingSection = "";
|
||||
root.pendingSetting = "";
|
||||
}
|
||||
Behavior on blobColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onCurrentCategoryChanged() {
|
||||
if (root.currentCategory === "general") {
|
||||
stack.replaceCurrentItem(general, [], StackView.PopTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "wallpaper") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(background, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(background, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "bar") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(bar, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(bar, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "appearance") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(appearance, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(appearance, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "lockscreen") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(lockscreen, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(lockscreen, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "services") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(services, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(services, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "notifications") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(notifications, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(notifications, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "sidebar") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(sidebar, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(sidebar, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "utilities") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(utilities, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(utilities, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "dashboard") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(dashboard, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(dashboard, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "osd") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(osd, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(osd, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "launcher") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(launcher, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(launcher, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "screenshot") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(screenshot, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(screenshot, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
} else if (root.currentCategory === "updates") {
|
||||
if (root.currentIndex < root.lastIndex)
|
||||
stack.replaceCurrentItem(updates, [], StackView.PopTransition);
|
||||
else
|
||||
stack.replaceCurrentItem(updates, [], StackView.PushTransition);
|
||||
root.lastIndex = root.currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
target: root
|
||||
TapHandler {
|
||||
onTapped: root.focus = true
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: viewWrapper
|
||||
BlobGroup {
|
||||
id: blobGroup
|
||||
|
||||
color: root.blobColor
|
||||
smoothing: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
BlobInvertedRect {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
radius: Appearance.rounding.large - Appearance.padding.smaller
|
||||
|
||||
SettingsSearch {
|
||||
id: searchBar
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
|
||||
onSettingSelected: (category, section, settingName) => {
|
||||
layout.selectCategory(category);
|
||||
root.scrollToSetting(section, settingName);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: view
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.top: searchBar.bottom
|
||||
anchors.topMargin: Appearance.spacing.smaller
|
||||
implicitWidth: layout.implicitWidth
|
||||
|
||||
Categories {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
content: root
|
||||
|
||||
onSettingSelected: (category, section, settingName) => {
|
||||
root.scrollToSetting(section, settingName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: categoryContent
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: view.right
|
||||
anchors.leftMargin: Appearance.spacing.smaller
|
||||
anchors.right: parent.right
|
||||
anchors.top: searchBar.bottom
|
||||
anchors.topMargin: Appearance.spacing.smaller
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
StackView {
|
||||
id: stack
|
||||
|
||||
anchors.fill: parent
|
||||
initialItem: general
|
||||
|
||||
popEnter: Transition {
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
duration: 0
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.expressiveEffects
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: -50
|
||||
property: "y"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
popExit: Transition {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: 1
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: 0
|
||||
property: "y"
|
||||
to: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
pushEnter: Transition {
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
duration: 0
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.expressiveEffects
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: 50
|
||||
property: "y"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pushExit: Transition {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: 1
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
from: 0
|
||||
property: "y"
|
||||
to: -50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
borderBottom: Appearance.padding.normal
|
||||
borderLeft: navPane.width + navPane.anchors.margins * 2
|
||||
borderRight: Appearance.padding.normal
|
||||
borderTop: Appearance.padding.normal
|
||||
group: blobGroup
|
||||
opacity: root.blobColor.a
|
||||
radius: Appearance.rounding.large
|
||||
}
|
||||
|
||||
Component {
|
||||
id: general
|
||||
NavPane {
|
||||
id: navPane
|
||||
|
||||
Cat.General {
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.top: parent.top
|
||||
sState: root.sState
|
||||
width: Math.min(600, Math.round(root.width / 3))
|
||||
}
|
||||
|
||||
Component {
|
||||
id: background
|
||||
|
||||
Cat.Background {
|
||||
screen: root.screen
|
||||
}
|
||||
Pages {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: navPane.right
|
||||
anchors.leftMargin: navPane.anchors.margins + anchors.margins
|
||||
anchors.margins: Appearance.padding.extraLarge
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
sState: root.sState
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appearance
|
||||
|
||||
Cat.Appearance {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: bar
|
||||
|
||||
Cat.Bar {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: lockscreen
|
||||
|
||||
Cat.Lockscreen {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: services
|
||||
|
||||
Cat.Services {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: notifications
|
||||
|
||||
Cat.Notifications {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: sidebar
|
||||
|
||||
Cat.Sidebar {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: utilities
|
||||
|
||||
Cat.Utilities {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: dashboard
|
||||
|
||||
Cat.Dashboard {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: osd
|
||||
|
||||
Cat.Osd {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: launcher
|
||||
|
||||
Cat.Launcher {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: screenshot
|
||||
|
||||
Cat.Screenshot {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: updates
|
||||
|
||||
Cat.SystemUpdates {
|
||||
}
|
||||
PopupOverlay {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,660 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property string endTime: {
|
||||
var d = new Date(0, 0, 0, 0, 0, 0, 0);
|
||||
d.setMinutes(object[settings[2]]);
|
||||
return Qt.formatTime(d, "hh:mm AP");
|
||||
}
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property list<string> settings
|
||||
property bool shouldBeActive: true
|
||||
readonly property string startTime: {
|
||||
var d = new Date(0, 0, 0, 0, 0, 0, 0);
|
||||
d.setMinutes(object[settings[1]]);
|
||||
return Qt.formatTime(d, "hh:mm AP");
|
||||
}
|
||||
|
||||
function commitChoice(choice: int, setting: string): void {
|
||||
root.object[setting] = choice;
|
||||
Config.save();
|
||||
Hyprsunset.checkStartup();
|
||||
}
|
||||
|
||||
function convertHour(timeValue: int): int {
|
||||
return Math.floor(timeValue / 60);
|
||||
}
|
||||
|
||||
function convertMinute(timeValue: int): int {
|
||||
return timeValue % 60;
|
||||
}
|
||||
|
||||
function convertToMinutes(hour: int, minute: int): int {
|
||||
return hour * 60 + minute;
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.preferredWidth: Math.min(contentWidth, optionLayout.x - Appearance.spacing.normal)
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(root.startTime).arg(root.endTime)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: optionLayout
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
CustomText {
|
||||
Layout.preferredWidth: spacer.x + spacer.width
|
||||
text: qsTr("Start")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: endMinuteRect.width + endHourRect.width
|
||||
text: qsTr("End")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
text: qsTr("Enabled: ")
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
id: enabledSwitch
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
checked: root.object[root.settings[0]]
|
||||
|
||||
onToggled: {
|
||||
root.object[root.settings[0]] = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillHeight: true
|
||||
|
||||
CustomRect {
|
||||
id: startHourRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: startHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: startHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: startHourField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: startHourField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertHour(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[1])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (startHourField.text.length >= 2) {
|
||||
startHourField.text = "0" + startHourField.text[0];
|
||||
} else if (startHourField.text.length === 1) {
|
||||
startHourField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
startHourField.text = setConfigText(root.settings[1]);
|
||||
startHourField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
startHourField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
startMinuteField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
endMinuteField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = startHourField.text.length;
|
||||
|
||||
if (textLen >= 2 && startHourField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(startHourField.text + digit);
|
||||
} else {
|
||||
val = parseInt(startHourField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
startHourField.text = "0" + val;
|
||||
} else {
|
||||
startHourField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (startHourField.text === "")
|
||||
return;
|
||||
var val = parseInt(startHourField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== startHourField.text)
|
||||
startHourField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: startSeparator
|
||||
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: startMinuteRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: startMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: startMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: startMinuteField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: startMinuteField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertMinute(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[1])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (startMinuteField.text.length >= 2) {
|
||||
startMinuteField.text = "0" + startMinuteField.text[0];
|
||||
} else if (startMinuteField.text.length === 1) {
|
||||
startMinuteField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
startMinuteField.text = setConfigText(root.settings[1]);
|
||||
startMinuteField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
startMinuteField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
endHourField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
startHourField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = startMinuteField.text.length;
|
||||
|
||||
if (textLen >= 2 && startMinuteField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(startMinuteField.text + digit);
|
||||
} else {
|
||||
val = parseInt(startMinuteField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(59, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
startMinuteField.text = "0" + val;
|
||||
} else {
|
||||
startMinuteField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (startMinuteField.text === "")
|
||||
return;
|
||||
var val = parseInt(startMinuteField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== startMinuteField.text)
|
||||
startMinuteField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: spacer
|
||||
|
||||
Layout.preferredWidth: Appearance.spacing.large
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: endHourRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: endHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: endHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: endHourField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: endHourField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertHour(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[2])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (endHourField.text.length >= 2) {
|
||||
endHourField.text = "0" + endHourField.text[0];
|
||||
} else if (endHourField.text.length === 1) {
|
||||
endHourField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
endHourField.text = setConfigText(root.settings[2]);
|
||||
endHourField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
endHourField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
endMinuteField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
startMinuteField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = endHourField.text.length;
|
||||
|
||||
if (textLen >= 2 && endHourField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(endHourField.text + digit);
|
||||
} else {
|
||||
val = parseInt(endHourField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
endHourField.text = "0" + val;
|
||||
} else {
|
||||
endHourField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (endHourField.text === "")
|
||||
return;
|
||||
var val = parseInt(endHourField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== endHourField.text)
|
||||
endHourField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: endSeparator
|
||||
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: endMinuteRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: endMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: endMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: endMinuteField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: endMinuteField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertMinute(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[2])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (endMinuteField.text.length >= 2) {
|
||||
endMinuteField.text = "0" + endMinuteField.text[0];
|
||||
} else if (endMinuteField.text.length === 1) {
|
||||
endMinuteField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
endMinuteField.text = setConfigText(root.settings[2]);
|
||||
endMinuteField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
endMinuteField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
startHourField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
endHourField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = endMinuteField.text.length;
|
||||
|
||||
if (textLen >= 2 && endMinuteField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(endMinuteField.text + digit);
|
||||
} else {
|
||||
val = parseInt(endMinuteField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(59, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
endMinuteField.text = "0" + val;
|
||||
} else {
|
||||
endMinuteField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (endMinuteField.text === "")
|
||||
return;
|
||||
var val = parseInt(endMinuteField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== endMinuteField.text)
|
||||
endMinuteField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
id: startHour
|
||||
|
||||
Layout.preferredWidth: startSeparator.x + startSeparator.width
|
||||
text: qsTr("Hour")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: startMinute
|
||||
|
||||
Layout.preferredWidth: spacer.x + spacer.width - x
|
||||
text: qsTr("Minute")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: endSeparator.x + endSeparator.width - x
|
||||
text: qsTr("Hour")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: qsTr("Minute")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property string name
|
||||
required property var object
|
||||
property alias row: row
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
required property list<var> stringList
|
||||
|
||||
signal optionSet(option: string)
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.height : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
|
||||
Layout.preferredWidth: 500
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale
|
||||
|
||||
CustomRect {
|
||||
id: searchBox
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 42
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Appearance.padding.large
|
||||
anchors.top: parent.top
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: "search"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: textSearch
|
||||
|
||||
anchors.left: searchIcon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
placeholderText: "Search..."
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: searchBox.bottom
|
||||
bottomLeftRadius: 21
|
||||
bottomRightRadius: 21
|
||||
|
||||
CustomListView {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
delegate: CustomRect {
|
||||
id: delegate
|
||||
|
||||
required property string modelData
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: 42
|
||||
radius: Appearance.rounding.smallest
|
||||
|
||||
CustomText {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
text: modelData
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: "check_circle"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: root.object[root.setting] === delegate.modelData
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
onClicked: {
|
||||
root.object[root.setting] = delegate.modelData;
|
||||
root.optionSet(delegate.modelData);
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const values = root.stringList;
|
||||
const search = textSearch.text;
|
||||
var regex = new RegExp(search, "i");
|
||||
|
||||
return values.filter(n => regex.test(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
property bool shouldBeActive: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.tPalette.m3outlineVariant
|
||||
implicitHeight: shouldBeActive ? 1 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function addAction() {
|
||||
const list = [...root.object[root.setting]];
|
||||
list.push({
|
||||
name: "New Action",
|
||||
icon: "bolt",
|
||||
description: "",
|
||||
command: [],
|
||||
enabled: true,
|
||||
dangerous: false
|
||||
});
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function removeAction(index) {
|
||||
const list = [...root.object[root.setting]];
|
||||
list.splice(index, 1);
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function updateAction(index, key, value) {
|
||||
const list = [...root.object[root.setting]];
|
||||
const entry = list[index];
|
||||
entry[key] = value;
|
||||
list[index] = entry;
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: shouldBeActive ? implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
spacing: Appearance.spacing.smaller
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
z: -1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [...root.object[root.setting]]
|
||||
|
||||
CustomRect {
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: layout.implicitHeight + Appearance.padding.normal * 2
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: modelData.name ?? qsTr("Action")
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "delete"
|
||||
type: IconButton.Tonal
|
||||
|
||||
onClicked: root.removeAction(index)
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Name")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: 350
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
text: modelData.name ?? ""
|
||||
|
||||
onEditingFinished: root.updateAction(index, "name", text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Icon")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: 350
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
text: modelData.icon ?? ""
|
||||
|
||||
onEditingFinished: root.updateAction(index, "icon", text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Description")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: 350
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
text: modelData.description ?? ""
|
||||
|
||||
onEditingFinished: root.updateAction(index, "description", text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringListEditor {
|
||||
Layout.fillWidth: true
|
||||
addLabel: qsTr("Add command argument")
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
values: [...(modelData.command ?? [])]
|
||||
|
||||
onListEdited: function (values) {
|
||||
root.updateAction(index, "command", values);
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Enabled")
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
checked: modelData.enabled ?? true
|
||||
|
||||
onToggled: root.updateAction(index, "enabled", checked)
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Dangerous")
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
checked: modelData.dangerous ?? false
|
||||
|
||||
onToggled: root.updateAction(index, "dangerous", checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "add"
|
||||
|
||||
onClicked: root.addAction()
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Add action")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function addAlias() {
|
||||
const list = [...root.object[root.setting]];
|
||||
list.push({
|
||||
from: "",
|
||||
to: ""
|
||||
});
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function removeAlias(index) {
|
||||
const list = [...root.object[root.setting]];
|
||||
list.splice(index, 1);
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function updateAlias(index, key, value) {
|
||||
const list = [...root.object[root.setting]];
|
||||
const entry = [...list[index]];
|
||||
entry[key] = value;
|
||||
list[index] = entry;
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: shouldBeActive ? layout.implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
z: -1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQml.Models
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool dragActive: false
|
||||
property real dragHeight: 0
|
||||
property real dragStartX: 0
|
||||
property real dragStartY: 0
|
||||
property real dragX: 0
|
||||
property real dragY: 0
|
||||
property var draggedModelData: null
|
||||
property string draggedUid: ""
|
||||
property bool dropAnimating: false
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
property var pendingCommitEntries: []
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
property int uidCounter: 0
|
||||
property var visualEntries: []
|
||||
|
||||
function ensureVisualEntries() {
|
||||
if (!root.dragActive && !root.dropAnimating)
|
||||
root.rebuildVisualEntries();
|
||||
}
|
||||
|
||||
function iconForId(id) {
|
||||
switch (id) {
|
||||
case "workspaces":
|
||||
return "dashboard";
|
||||
case "audio":
|
||||
return "volume_up";
|
||||
case "media":
|
||||
return "play_arrow";
|
||||
case "resources":
|
||||
return "monitoring";
|
||||
case "updates":
|
||||
return "system_update";
|
||||
case "dash":
|
||||
return "space_dashboard";
|
||||
case "spacer":
|
||||
return "horizontal_rule";
|
||||
case "activeWindow":
|
||||
return "web_asset";
|
||||
case "tray":
|
||||
return "widgets";
|
||||
case "upower":
|
||||
return "battery_full";
|
||||
case "network":
|
||||
return "wifi";
|
||||
case "clock":
|
||||
return "schedule";
|
||||
case "notifBell":
|
||||
return "notifications";
|
||||
default:
|
||||
return "drag_indicator";
|
||||
}
|
||||
}
|
||||
|
||||
function indexForUid(uid) {
|
||||
for (let i = 0; i < root.visualEntries.length; i++) {
|
||||
if (root.visualEntries[i].uid === uid)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function labelForId(id) {
|
||||
switch (id) {
|
||||
case "workspaces":
|
||||
return qsTr("Workspaces");
|
||||
case "audio":
|
||||
return qsTr("Audio");
|
||||
case "media":
|
||||
return qsTr("Media");
|
||||
case "resources":
|
||||
return qsTr("Resources");
|
||||
case "updates":
|
||||
return qsTr("Updates");
|
||||
case "dash":
|
||||
return qsTr("Dash");
|
||||
case "spacer":
|
||||
return qsTr("Spacer");
|
||||
case "activeWindow":
|
||||
return qsTr("Title");
|
||||
case "tray":
|
||||
return qsTr("Tray");
|
||||
case "upower":
|
||||
return qsTr("Power");
|
||||
case "network":
|
||||
return qsTr("Network");
|
||||
case "clock":
|
||||
return qsTr("Clock");
|
||||
case "notifBell":
|
||||
return qsTr("Notifs");
|
||||
case "hyprsunset":
|
||||
return qsTr("Night light");
|
||||
default:
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildVisualEntries() {
|
||||
const entries = root.object[root.setting] ?? [];
|
||||
const next = [];
|
||||
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
let existing = null;
|
||||
|
||||
for (let j = 0; j < root.visualEntries.length; j++) {
|
||||
if (root.visualEntries[j].entry === entry) {
|
||||
existing = root.visualEntries[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existing)
|
||||
next.push(existing);
|
||||
else
|
||||
next.push({
|
||||
uid: `entry-${root.uidCounter++}`,
|
||||
entry
|
||||
});
|
||||
}
|
||||
|
||||
root.visualEntries = next;
|
||||
}
|
||||
|
||||
function updateEntry(index, value) {
|
||||
const list = [...root.object[root.setting]];
|
||||
const entry = list[index];
|
||||
entry.enabled = value;
|
||||
list[index] = entry;
|
||||
root.object[root.setting] = list;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? layout.implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: root.rebuildVisualEntries()
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
z: -1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
// spacing: Appearance.spacing.smaller
|
||||
|
||||
DelegateModel {
|
||||
id: visualModel
|
||||
|
||||
delegate: entryDelegate
|
||||
|
||||
model: ScriptModel {
|
||||
objectProp: "uid"
|
||||
values: root.visualEntries
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
delegate: entryDelegate
|
||||
model: visualModel
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: entryDelegate
|
||||
|
||||
IconButton {
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
|
||||
checked: modelData.entry.enabled ?? true
|
||||
font: Appearance.font.family.sans
|
||||
// icon: root.iconForId(modelData.entry.id)
|
||||
icon: root.labelForId(modelData.entry.id)
|
||||
inactiveColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
isToggle: true
|
||||
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
|
||||
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
|
||||
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
visible: !["spacer", "upower", "dash", "audio"].some(prefix => modelData.entry.id.startsWith(prefix))
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: root.updateEntry(index, internalChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property list<string> settings
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function commitChoice(choice: int, setting: string): void {
|
||||
root.object[setting] = choice;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function formattedValue(setting: string): string {
|
||||
const value = root.object[setting];
|
||||
|
||||
if (value === null || value === undefined)
|
||||
return "";
|
||||
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function hourToAmPm(hour) {
|
||||
var h = Number(hour) % 24;
|
||||
var d = new Date(2000, 0, 1, h, 0, 0);
|
||||
return Qt.formatTime(d, "h AP");
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(root.hourToAmPm(root.object[root.settings[0]])).arg(root.hourToAmPm(root.object[root.settings[1]]))
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: optionLayout
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 100
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: optionLayout.width
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Enabled: ")
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
id: enabledSwitch
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
checked: root.object[root.settings[2]]
|
||||
|
||||
onToggled: {
|
||||
root.object[root.settings[2]] = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: optionLayout.width
|
||||
z: setting2.expanded ? -1 : 1
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Start: ")
|
||||
}
|
||||
|
||||
SpinnerButton {
|
||||
id: setting1
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
|
||||
Layout.preferredWidth: height * 2
|
||||
currentIndex: root.object[root.settings[0]]
|
||||
enabled: enabledSwitch.checked
|
||||
text: root.formattedValue(root.settings[0])
|
||||
|
||||
menu.onItemSelected: item => {
|
||||
root.commitChoice(item, root.settings[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: optionLayout.width
|
||||
z: setting1.expanded ? -1 : 1
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("End: ")
|
||||
}
|
||||
|
||||
SpinnerButton {
|
||||
id: setting2
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
|
||||
Layout.preferredWidth: height * 2
|
||||
currentIndex: root.object[root.settings[1]]
|
||||
enabled: enabledSwitch.checked
|
||||
text: root.formattedValue(root.settings[1])
|
||||
|
||||
menu.onItemSelected: item => {
|
||||
root.commitChoice(item, root.settings[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: optionLayout.width
|
||||
z: -2
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Temp: ")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: rect
|
||||
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: Math.max(Math.min(textField.contentWidth + Appearance.padding.normal * 2, 200), 50)
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
id: textField
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 200)
|
||||
text: root.formattedValue(root.settings[3])
|
||||
|
||||
onEditingFinished: {
|
||||
root.object[root.settings[3]] = textField.text;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function formattedValue(): string {
|
||||
const value = root.object[root.setting];
|
||||
|
||||
if (value === null || value === undefined)
|
||||
return "";
|
||||
|
||||
return String(value);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: rect
|
||||
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: Math.max(Math.min(textField.contentWidth + Appearance.padding.large * 2, 550), 50)
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomTextField {
|
||||
id: textField
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, 550)
|
||||
text: root.formattedValue()
|
||||
|
||||
onEditingFinished: {
|
||||
root.object[root.setting] = textField.text;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property int index
|
||||
required property var modelData
|
||||
property bool shouldBeActive: true
|
||||
|
||||
signal addActiveActionRequested
|
||||
signal deleteRequested(int index)
|
||||
signal fieldEdited(string key, var value)
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
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: root.index !== 0
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
Item {
|
||||
id: nameCell
|
||||
|
||||
property string draftName: ""
|
||||
property bool editing: false
|
||||
|
||||
function beginEdit() {
|
||||
draftName = root.modelData.name ?? "";
|
||||
editing = true;
|
||||
nameEditor.forceActiveFocus();
|
||||
}
|
||||
|
||||
function cancelEdit() {
|
||||
draftName = root.modelData.name ?? "";
|
||||
editing = false;
|
||||
}
|
||||
|
||||
function commitEdit() {
|
||||
editing = false;
|
||||
|
||||
if (draftName !== (root.modelData.name ?? "")) {
|
||||
root.fieldEdited("name", draftName);
|
||||
}
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: root.width / 2
|
||||
|
||||
HoverHandler {
|
||||
id: nameHover
|
||||
}
|
||||
|
||||
HoverIconButton {
|
||||
id: editButton
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "edit"
|
||||
shouldBeVisible: nameHover.hovered && !nameCell.editing
|
||||
|
||||
onClicked: nameCell.beginEdit()
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: nameHover.hovered ? editButton.width + Appearance.spacing.smaller * 2 : 0
|
||||
anchors.right: deleteButton.left
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight // enable if CustomText supports it
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.modelData.name
|
||||
visible: !nameCell.editing
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HoverIconButton {
|
||||
id: deleteButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "delete"
|
||||
shouldBeVisible: nameHover.hovered && !nameCell.editing
|
||||
|
||||
onClicked: root.deleteRequested(root.index)
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
implicitHeight: nameEditor.implicitHeight + (Appearance.padding.normal * 2)
|
||||
implicitWidth: Math.min(nameEditor.contentWidth + (Appearance.padding.normal * 2), parent.width - Appearance.padding.normal)
|
||||
radius: Appearance.rounding.small
|
||||
visible: nameCell.editing
|
||||
|
||||
CustomTextField {
|
||||
id: nameEditor
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
implicitWidth: Math.min(contentWidth + Appearance.padding.normal * 2, nameCell.width - Appearance.padding.normal)
|
||||
text: nameCell.draftName
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
nameCell.cancelEdit();
|
||||
}
|
||||
onEditingFinished: {
|
||||
nameCell.commitEdit();
|
||||
}
|
||||
onTextEdited: {
|
||||
nameCell.draftName = nameEditor.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VSeparator {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: cLayout
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Timeout")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: Math.max(Math.min(timeField.contentWidth + Appearance.padding.normal * 3, 200), 50)
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomTextField {
|
||||
id: timeField
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: String(root.modelData.timeout ?? "")
|
||||
|
||||
onEditingFinished: {
|
||||
root.fieldEdited("timeout", Number(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Idle Action")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: Math.max(Math.min(idleField.contentWidth + Appearance.padding.normal * 3, 200), 50)
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomTextField {
|
||||
id: idleField
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.modelData.idleAction ?? ""
|
||||
|
||||
onEditingFinished: {
|
||||
root.fieldEdited("idleAction", text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: activeActionRow.visible ? activeActionRow.implicitHeight : addButtonRow.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: activeActionRow
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
visible: root.modelData.activeAction !== undefined
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Active Action")
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredHeight: 33
|
||||
Layout.preferredWidth: Math.max(Math.min(actionField.contentWidth + Appearance.padding.normal * 3, 200), 50)
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomTextField {
|
||||
id: actionField
|
||||
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.modelData.activeAction ?? ""
|
||||
|
||||
onEditingFinished: {
|
||||
root.fieldEdited("activeAction", text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: addButtonRow
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
visible: root.modelData.activeAction === undefined
|
||||
|
||||
IconButton {
|
||||
id: button
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "add"
|
||||
|
||||
onClicked: root.addActiveActionRequested()
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("Add active action")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property string name
|
||||
required property var object
|
||||
property alias row: row
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
required property list<var> stringList
|
||||
|
||||
signal optionSet
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.height : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: fontArea
|
||||
|
||||
Layout.preferredHeight: 42 * 6 + Appearance.padding.normal * 2 + Appearance.spacing.small * 5
|
||||
Layout.preferredWidth: 500
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: (21 + Appearance.padding.normal) * Appearance.rounding.scale
|
||||
|
||||
CustomRect {
|
||||
id: searchBox
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 42
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Appearance.padding.large
|
||||
anchors.top: parent.top
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: "search"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: fontSearch
|
||||
|
||||
anchors.left: searchIcon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
placeholderText: "Search..."
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: searchBox.bottom
|
||||
bottomLeftRadius: 21
|
||||
bottomRightRadius: 21
|
||||
|
||||
CustomListView {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
delegate: CustomRect {
|
||||
id: fontDelegate
|
||||
|
||||
required property string modelData
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: 42
|
||||
radius: Appearance.rounding.smallest
|
||||
|
||||
CustomText {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
text: modelData
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: "check_circle"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: root.object[root.setting] === fontDelegate.modelData
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
onClicked: {
|
||||
root.object[root.setting] = fontDelegate.modelData;
|
||||
root.optionSet();
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const values = root.stringList;
|
||||
const search = fontSearch.text;
|
||||
var regex = new RegExp(search, "i");
|
||||
|
||||
return values.filter(n => regex.test(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
property bool shouldBeActive: true
|
||||
required property string value
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: root.value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
property real max: Infinity
|
||||
property real min: -Infinity
|
||||
required property string name
|
||||
required property var object
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
property real step: 1
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomSpinBox {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
max: root.max
|
||||
min: root.min
|
||||
step: root.step
|
||||
value: Number(root.object[root.setting] ?? 0)
|
||||
|
||||
onValueModified: function (value) {
|
||||
root.object[root.setting] = value;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property list<string> settings
|
||||
property bool shouldBeActive: true
|
||||
|
||||
function commitChoice(choice: int, setting: string): void {
|
||||
root.object[setting] = choice;
|
||||
Config.save();
|
||||
}
|
||||
|
||||
function formattedValue(setting: string): string {
|
||||
const value = root.object[setting];
|
||||
|
||||
if (value === null || value === undefined)
|
||||
return "";
|
||||
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function hourToAmPm(hour) {
|
||||
var h = Number(hour) % 24;
|
||||
var d = new Date(2000, 0, 1, h, 0, 0);
|
||||
return Qt.formatTime(d, "h AP");
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(root.hourToAmPm(root.object[root.settings[0]])).arg(root.hourToAmPm(root.object[root.settings[1]]))
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: optionLayout
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 100
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: optionLayout.width
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Enabled: ")
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
id: enabledSwitch
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
checked: root.object[root.settings[2]]
|
||||
|
||||
onToggled: {
|
||||
root.object[root.settings[2]] = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: optionLayout.width
|
||||
z: setting2.expanded ? -1 : 1
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Start: ")
|
||||
}
|
||||
|
||||
SpinnerButton {
|
||||
id: setting1
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
|
||||
Layout.preferredWidth: height * 2
|
||||
currentIndex: root.object[root.settings[0]]
|
||||
enabled: enabledSwitch.checked
|
||||
text: root.formattedValue(root.settings[0])
|
||||
|
||||
menu.onItemSelected: item => {
|
||||
root.commitChoice(item, root.settings[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: optionLayout.width
|
||||
z: setting1.expanded ? -1 : 1
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("End: ")
|
||||
}
|
||||
|
||||
SpinnerButton {
|
||||
id: setting2
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
Layout.preferredHeight: Appearance.font.size.large + Appearance.padding.smaller * 2
|
||||
Layout.preferredWidth: height * 2
|
||||
currentIndex: root.object[root.settings[1]]
|
||||
enabled: enabledSwitch.checked
|
||||
text: root.formattedValue(root.settings[1])
|
||||
|
||||
menu.onItemSelected: item => {
|
||||
root.commitChoice(item, root.settings[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string addLabel: qsTr("Add entry")
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? layout.implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
StringListEditor {
|
||||
Layout.fillWidth: true
|
||||
addLabel: root.addLabel
|
||||
anchors.left: undefined
|
||||
anchors.right: undefined
|
||||
anchors.verticalCenter: undefined
|
||||
values: [...(root.object[root.setting] ?? [])]
|
||||
|
||||
onListEdited: function (values) {
|
||||
root.object[root.setting] = values;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property string setting
|
||||
property bool shouldBeActive: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
id: cswitch
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
checked: root.object[root.setting]
|
||||
|
||||
onToggled: {
|
||||
root.object[root.setting] = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
required property string name
|
||||
property bool shouldBeActive: true
|
||||
|
||||
implicitHeight: 60
|
||||
implicitWidth: 200
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
CustomText {
|
||||
anchors.fill: parent
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.large * 2
|
||||
text: root.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Paths
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias button: iButton
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
property bool shouldBeActive: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: iButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
icon: "download"
|
||||
|
||||
onClicked: {
|
||||
const lockBg = `${Paths.state}/lockscreen_bg.png`;
|
||||
const scheme = `${Paths.state}/scheme.json`;
|
||||
const face = `${Paths.home}/.face`;
|
||||
const destination = "/etc/zshell-greeter/images";
|
||||
Quickshell.execDetached(["pkexec", "sh", "-c", `mkdir -p ${destination}; cp ${lockBg} ${destination}; cp ${scheme} /etc/zshell-greeter; cp ${face} ${destination}`]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
default property alias contentData: clayout.data
|
||||
|
||||
function scrollToSectionAndHighlight(sectionId: string, settingName: string): bool {
|
||||
for (let i = 0; i < clayout.children.length; i++) {
|
||||
const section = clayout.children[i];
|
||||
if (section.sectionId === sectionId) {
|
||||
const targetY = section.y - Appearance.padding.normal;
|
||||
flickable.contentY = Math.max(0, Math.min(targetY, flickable.contentHeight - flickable.height));
|
||||
|
||||
SettingsHighlight.highlight(settingName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CustomFlickable {
|
||||
id: flickable
|
||||
|
||||
anchors.fill: parent
|
||||
// for future:
|
||||
// anchors.leftMargin: Appearance.padding.extraLarge
|
||||
// anchors.rightMargin: Appearance.padding.extraLarge
|
||||
clip: true
|
||||
contentHeight: clayout.implicitHeight + clayout.anchors.margins * 2
|
||||
|
||||
CustomScrollBar.vertical: CustomScrollBar {
|
||||
flickable: flickable
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
|
||||
onTapped: function (eventPoint) {
|
||||
const menu = SettingsDropdowns.activeMenu;
|
||||
if (!menu)
|
||||
return;
|
||||
|
||||
const p = eventPoint.scenePosition;
|
||||
|
||||
if (SettingsDropdowns.hit(SettingsDropdowns.activeTrigger, p))
|
||||
return;
|
||||
|
||||
if (SettingsDropdowns.hit(menu, p))
|
||||
return;
|
||||
|
||||
SettingsDropdowns.closeActive();
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: clayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.extraSmall
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
// move: Transition {
|
||||
// Anim {
|
||||
// properties: "y"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
default property alias contentData: layout.data
|
||||
property real contentPadding: Appearance.padding.large
|
||||
property string sectionId: ""
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: layout.height + contentPadding * 2
|
||||
radius: Appearance.rounding.normal - Appearance.padding.extraSmall
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: root.contentPadding
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Behavior on height {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
property alias currentIndex: menu.currentIndex
|
||||
property bool enabled
|
||||
property alias expanded: menu.expanded
|
||||
property alias label: label
|
||||
property alias menu: menu
|
||||
property bool shouldBeActive: true
|
||||
property alias text: label.text
|
||||
|
||||
color: enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
radius: Appearance.rounding.full
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
z: expanded ? 100 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3onSurface, 2)
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
visible: root.enabled
|
||||
|
||||
onClicked: {
|
||||
SettingsDropdowns.toggle(menu, root);
|
||||
}
|
||||
}
|
||||
|
||||
PathViewMenu {
|
||||
id: menu
|
||||
|
||||
anchors.centerIn: parent
|
||||
from: 1
|
||||
implicitWidth: root.width
|
||||
itemHeight: root.height
|
||||
to: 24
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property string addLabel: qsTr("Add entry")
|
||||
property bool shouldBeActive: true
|
||||
property var values: []
|
||||
|
||||
signal listEdited(var values)
|
||||
|
||||
function addValue() {
|
||||
const list = [...root.values];
|
||||
list.push("");
|
||||
root.listEdited(list);
|
||||
}
|
||||
|
||||
function removeValue(index) {
|
||||
const list = [...root.values];
|
||||
list.splice(index, 1);
|
||||
root.listEdited(list);
|
||||
}
|
||||
|
||||
function updateValue(index, value) {
|
||||
const list = [...root.values];
|
||||
list[index] = value;
|
||||
root.listEdited(list);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: shouldBeActive ? implicitHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
spacing: Appearance.spacing.smaller
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [...root.values]
|
||||
|
||||
Item {
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: row.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
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
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: String(modelData ?? "")
|
||||
|
||||
onEditingFinished: root.updateValue(index, text)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "delete"
|
||||
type: IconButton.Tonal
|
||||
|
||||
onClicked: root.removeValue(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "add"
|
||||
|
||||
onClicked: root.addValue()
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: root.addLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,661 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property string endTime: {
|
||||
var d = new Date(0, 0, 0, 0, 0, 0, 0);
|
||||
d.setMinutes(object[settings[2]]);
|
||||
return Qt.formatTime(d, "hh:mm AP");
|
||||
}
|
||||
readonly property bool highlighted: SettingsHighlight.highlightedSetting === name
|
||||
required property string name
|
||||
required property var object
|
||||
required property list<string> settings
|
||||
property bool shouldBeActive: true
|
||||
readonly property string startTime: {
|
||||
var d = new Date(0, 0, 0, 0, 0, 0, 0);
|
||||
d.setMinutes(object[settings[1]]);
|
||||
return Qt.formatTime(d, "hh:mm AP");
|
||||
}
|
||||
|
||||
function commitChoice(choice: int, setting: string): void {
|
||||
root.object[setting] = choice;
|
||||
Config.save();
|
||||
ModeScheduler.checkStartup();
|
||||
}
|
||||
|
||||
function convertHour(timeValue: int): int {
|
||||
return Math.floor(timeValue / 60);
|
||||
}
|
||||
|
||||
function convertMinute(timeValue: int): int {
|
||||
return timeValue % 60;
|
||||
}
|
||||
|
||||
function convertToMinutes(hour: int, minute: int): int {
|
||||
return hour * 60 + minute;
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Appearance.padding.smaller
|
||||
color: DynamicColors.palette.m3primaryContainer
|
||||
opacity: root.highlighted ? 0.5 : 0
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.preferredWidth: Math.min(contentWidth, optionLayout.x - Appearance.spacing.normal)
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(root.startTime).arg(root.endTime)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: optionLayout
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
CustomText {
|
||||
Layout.preferredWidth: spacer.x + spacer.width
|
||||
text: qsTr("Start")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: endMinuteRect.width + endHourRect.width
|
||||
text: qsTr("End")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter
|
||||
text: qsTr("Enabled: ")
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
id: enabledSwitch
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignHCenter
|
||||
checked: root.object[root.settings[0]]
|
||||
|
||||
onToggled: {
|
||||
root.object[root.settings[0]] = checked;
|
||||
Config.save();
|
||||
ModeScheduler.checkStartup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillHeight: true
|
||||
|
||||
CustomRect {
|
||||
id: startHourRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: startHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: startHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: startHourField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: startHourField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertHour(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[1])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (startHourField.text.length >= 2) {
|
||||
startHourField.text = "0" + startHourField.text[0];
|
||||
} else if (startHourField.text.length === 1) {
|
||||
startHourField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
startHourField.text = setConfigText(root.settings[1]);
|
||||
startHourField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
startHourField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
startMinuteField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
endMinuteField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = startHourField.text.length;
|
||||
|
||||
if (textLen >= 2 && startHourField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(startHourField.text + digit);
|
||||
} else {
|
||||
val = parseInt(startHourField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
startHourField.text = "0" + val;
|
||||
} else {
|
||||
startHourField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (startHourField.text === "")
|
||||
return;
|
||||
var val = parseInt(startHourField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== startHourField.text)
|
||||
startHourField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: startSeparator
|
||||
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: startMinuteRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: startMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: startMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: startMinuteField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: startMinuteField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertMinute(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[1])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (startMinuteField.text.length >= 2) {
|
||||
startMinuteField.text = "0" + startMinuteField.text[0];
|
||||
} else if (startMinuteField.text.length === 1) {
|
||||
startMinuteField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
startMinuteField.text = setConfigText(root.settings[1]);
|
||||
startMinuteField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
startMinuteField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
endHourField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
startHourField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = startMinuteField.text.length;
|
||||
|
||||
if (textLen >= 2 && startMinuteField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(startMinuteField.text + digit);
|
||||
} else {
|
||||
val = parseInt(startMinuteField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(59, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
startMinuteField.text = "0" + val;
|
||||
} else {
|
||||
startMinuteField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(startHourField.text), parseInt(startMinuteField.text)), root.settings[1]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (startMinuteField.text === "")
|
||||
return;
|
||||
var val = parseInt(startMinuteField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== startMinuteField.text)
|
||||
startMinuteField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: spacer
|
||||
|
||||
Layout.preferredWidth: Appearance.spacing.large
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: endHourRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: endHourField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: endHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: endHourField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: endHourField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertHour(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[2])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (endHourField.text.length >= 2) {
|
||||
endHourField.text = "0" + endHourField.text[0];
|
||||
} else if (endHourField.text.length === 1) {
|
||||
endHourField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
endHourField.text = setConfigText(root.settings[2]);
|
||||
endHourField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
endHourField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
endMinuteField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
startMinuteField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = endHourField.text.length;
|
||||
|
||||
if (textLen >= 2 && endHourField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(endHourField.text + digit);
|
||||
} else {
|
||||
val = parseInt(endHourField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
endHourField.text = "0" + val;
|
||||
} else {
|
||||
endHourField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (endHourField.text === "")
|
||||
return;
|
||||
var val = parseInt(endHourField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== endHourField.text)
|
||||
endHourField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: endSeparator
|
||||
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: endMinuteRect
|
||||
|
||||
Layout.preferredHeight: 72
|
||||
Layout.preferredWidth: 96
|
||||
color: endMinuteField.focus ? DynamicColors.palette.m3onPrimaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
implicitHeight: 72
|
||||
implicitWidth: 96
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
border.color: endMinuteField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainerHighest
|
||||
border.width: endMinuteField.focus ? 2 : 0
|
||||
radius: parent.radius - border.width
|
||||
|
||||
Behavior on border.width {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: endMinuteField
|
||||
|
||||
function setConfigText(setting: string): string {
|
||||
var val = root.convertMinute(root.object[setting]);
|
||||
if (val === 0) {
|
||||
return "00";
|
||||
}
|
||||
return String(val);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
clip: true
|
||||
color: focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3onSurface
|
||||
cursorHeight: height - Appearance.padding.normal * 2
|
||||
font.family: "Roboto"
|
||||
font.letterSpacing: -0.25
|
||||
font.pixelSize: 56
|
||||
font.weight: 400
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
text: setConfigText(root.settings[2])
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
event.accepted = true;
|
||||
if (endMinuteField.text.length >= 2) {
|
||||
endMinuteField.text = "0" + endMinuteField.text[0];
|
||||
} else if (endMinuteField.text.length === 1) {
|
||||
endMinuteField.text = "0";
|
||||
}
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
endMinuteField.text = setConfigText(root.settings[2]);
|
||||
endMinuteField.focus = false;
|
||||
} else if (event.key === Qt.Key_Return) {
|
||||
endMinuteField.focus = false;
|
||||
return;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
startHourField.focus = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
endHourField.focus = true;
|
||||
}
|
||||
|
||||
if (event.text.length === 1 && event.text >= "0" && event.text <= "9") {
|
||||
event.accepted = true;
|
||||
var digit = event.text;
|
||||
var textLen = endMinuteField.text.length;
|
||||
|
||||
if (textLen >= 2 && endMinuteField.text[0] !== '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
var val = 0;
|
||||
if (textLen === 0) {
|
||||
val = parseInt(digit);
|
||||
} else if (textLen === 1) {
|
||||
val = parseInt(endMinuteField.text + digit);
|
||||
} else {
|
||||
val = parseInt(endMinuteField.text[1] + digit);
|
||||
}
|
||||
|
||||
val = Math.max(0, Math.min(59, val));
|
||||
|
||||
if (textLen >= 2 && val < 10) {
|
||||
endMinuteField.text = "0" + val;
|
||||
} else {
|
||||
endMinuteField.text = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
event.accepted = true;
|
||||
}
|
||||
onCursorPositionChanged: cursorPosition = 2
|
||||
onEditingFinished: {
|
||||
root.commitChoice(root.convertToMinutes(parseInt(endHourField.text), parseInt(endMinuteField.text)), root.settings[2]);
|
||||
}
|
||||
onTextEdited: {
|
||||
if (endMinuteField.text === "")
|
||||
return;
|
||||
var val = parseInt(endMinuteField.text);
|
||||
if (isNaN(val))
|
||||
return;
|
||||
val = Math.max(0, Math.min(23, val));
|
||||
var newText = val.toString();
|
||||
if (newText !== endMinuteField.text)
|
||||
endMinuteField.text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
id: startHour
|
||||
|
||||
Layout.preferredWidth: startSeparator.x + startSeparator.width
|
||||
text: qsTr("Hour")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: startMinute
|
||||
|
||||
Layout.preferredWidth: spacer.x + spacer.width - x
|
||||
text: qsTr("Minute")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: endSeparator.x + endSeparator.width - x
|
||||
text: qsTr("Hour")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: qsTr("Minute")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 1
|
||||
color: DynamicColors.tPalette.m3outlineVariant
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import ZShell.Internal
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: wrapper
|
||||
|
||||
property bool changesMade: false
|
||||
property bool shouldBeActive: true
|
||||
|
||||
signal requestCrop
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: shouldBeActive ? 400 : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
icon: "check"
|
||||
opacity: wrapper.changesMade ? 1 : 0
|
||||
scale: wrapper.changesMade ? 1 : 0
|
||||
z: 2
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
wrapper.requestCrop();
|
||||
wrapper.changesMade = false;
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: [...Quickshell.screens].sort((a, b) => {
|
||||
return a.x - b.x;
|
||||
})
|
||||
}
|
||||
|
||||
Item {
|
||||
id: delegate
|
||||
|
||||
required property ShellScreen modelData
|
||||
|
||||
function applyCrop(): void {
|
||||
if (!cropRectLoader.item)
|
||||
return;
|
||||
const cropRect = cropRectLoader.item;
|
||||
|
||||
// We need to calculate the exact percentage coordinates that map perfectly
|
||||
// to our C++ backend, regardless of current display scaling
|
||||
const cropXPercent = (cropRect.x - cropRect.imageX) / scaledImg.paintedWidth;
|
||||
const cropYPercent = (cropRect.y - cropRect.imageY) / scaledImg.paintedHeight;
|
||||
const cropWidthPercent = cropRect.width / scaledImg.paintedWidth;
|
||||
const cropHeightPercent = cropRect.height / scaledImg.paintedHeight;
|
||||
|
||||
const finalRect = Qt.rect(cropXPercent, cropYPercent, cropWidthPercent, cropHeightPercent);
|
||||
|
||||
// We just pass the percentages directly to the backend
|
||||
Wallpapers.setCrop(delegate.modelData.name, finalRect, cropRect.zoom);
|
||||
}
|
||||
|
||||
function zoomClipRect(zoom: real): void {
|
||||
if (!cropRectLoader.item)
|
||||
return;
|
||||
const cropRect = cropRectLoader.item;
|
||||
|
||||
let oldCenterX = cropRect.x + cropRect.width * 0.5;
|
||||
let oldCenterY = cropRect.y + cropRect.height * 0.5;
|
||||
|
||||
cropRect.zoom = zoom;
|
||||
|
||||
cropRect.x = oldCenterX - cropRect.width * 0.5;
|
||||
cropRect.y = oldCenterY - cropRect.height * 0.5;
|
||||
|
||||
cropRect.clampToBounds();
|
||||
}
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
Connections {
|
||||
function onRequestCrop(): void {
|
||||
delegate.applyCrop();
|
||||
}
|
||||
|
||||
target: wrapper
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: sliderLayout
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: 30
|
||||
|
||||
CustomSlider {
|
||||
id: zoomSlider
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.padding.normal
|
||||
Layout.preferredHeight: Appearance.padding.larger * 3
|
||||
Layout.rightMargin: Appearance.padding.normal
|
||||
from: 1.0
|
||||
implicitHeight: Appearance.padding.larger * 3
|
||||
insetIcon: "crop"
|
||||
to: 5.0
|
||||
value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0
|
||||
|
||||
onInteraction: value => {
|
||||
delegate.zoomClipRect(1 + (value * 4));
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
id: scaledImg
|
||||
|
||||
property var displayData
|
||||
property real monitorScale: 1.0
|
||||
|
||||
anchors.bottom: sliderLayout.top
|
||||
anchors.bottomMargin: Appearance.spacing.normal
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
retainWhileLoading: true
|
||||
source: Wallpapers.current
|
||||
sourceSize.height: parent.height
|
||||
sourceSize.width: parent.width
|
||||
|
||||
onPaintedWidthChanged: {
|
||||
if (paintedWidth > 0 && cropRectLoader.item) {
|
||||
cropRectLoader.item.restoreFromData();
|
||||
}
|
||||
}
|
||||
onSourceChanged: {
|
||||
if (cropRectLoader.item) {
|
||||
cropRectLoader.item.restoreFromData();
|
||||
}
|
||||
}
|
||||
onStatusChanged: {
|
||||
if (scaledImg.status == Image.Ready && cropRectLoader.item) {
|
||||
cropRectLoader.item.restoreFromData();
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: monitorId
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: Qt.alpha(DynamicColors.palette.m3surface, 0.85)
|
||||
font.pointSize: Appearance.font.size.large * 4
|
||||
style: Text.Outline
|
||||
styleColor: DynamicColors.palette.m3onSurface
|
||||
text: delegate.modelData.name
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: cropRectLoader
|
||||
|
||||
active: scaledImg.paintedWidth > 0
|
||||
|
||||
sourceComponent: Component {
|
||||
CustomRect {
|
||||
id: cropRect
|
||||
|
||||
property real aspectRatio: delegate.modelData.width / delegate.modelData.height
|
||||
readonly property real baseHeight: baseWidth / aspectRatio
|
||||
readonly property real baseWidth: {
|
||||
let fittedHeight = scaledImg.paintedHeight;
|
||||
let fittedWidth = fittedHeight * aspectRatio;
|
||||
|
||||
if (fittedWidth > scaledImg.paintedWidth) {
|
||||
fittedWidth = scaledImg.paintedWidth;
|
||||
fittedHeight = fittedWidth / aspectRatio;
|
||||
}
|
||||
|
||||
return fittedWidth;
|
||||
}
|
||||
readonly property real imageX: (scaledImg.width - scaledImg.paintedWidth) / 2
|
||||
readonly property real imageY: (scaledImg.height - scaledImg.paintedHeight) / 2
|
||||
property real imgAspectRatio: scaledImg.paintedWidth / scaledImg.paintedHeight
|
||||
property real zoom: 1.0
|
||||
|
||||
function centerInImage() {
|
||||
x = imageX + (scaledImg.paintedWidth - width) / 2;
|
||||
y = imageY + (scaledImg.paintedHeight - height) / 2;
|
||||
}
|
||||
|
||||
function clampToBounds() {
|
||||
x = Math.max(imageX, Math.min(x, imageX + scaledImg.paintedWidth - width));
|
||||
|
||||
y = Math.max(imageY, Math.min(y, imageY + scaledImg.paintedHeight - height));
|
||||
}
|
||||
|
||||
function restoreFromData() {
|
||||
let data = Wallpapers.getCrop(delegate.modelData.name);
|
||||
|
||||
if (data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001)) {
|
||||
zoom = data.zoom > 0 ? data.zoom : 1.0;
|
||||
x = imageX + (data.x * scaledImg.paintedWidth);
|
||||
y = imageY + (data.y * scaledImg.paintedHeight);
|
||||
|
||||
clampToBounds();
|
||||
} else {
|
||||
zoom = 1.0;
|
||||
centerInImage();
|
||||
}
|
||||
}
|
||||
|
||||
border.color: DynamicColors.palette.m3primary
|
||||
border.width: 2
|
||||
height: baseHeight / zoom
|
||||
opacity: 1
|
||||
width: baseWidth / zoom
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
restoreFromData();
|
||||
}
|
||||
onHeightChanged: clampToBounds()
|
||||
onWidthChanged: clampToBounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
function updateCrop(mouseX, mouseY) {
|
||||
if (!cropRectLoader.item)
|
||||
return;
|
||||
const cropRect = cropRectLoader.item;
|
||||
|
||||
let nx = mouseX - cropRect.width * 0.5;
|
||||
let ny = mouseY - cropRect.height * 0.5;
|
||||
|
||||
nx = Math.max(cropRect.imageX, Math.min(nx, cropRect.imageX + scaledImg.paintedWidth - cropRect.width));
|
||||
|
||||
ny = Math.max(cropRect.imageY, Math.min(ny, cropRect.imageY + scaledImg.paintedHeight - cropRect.height));
|
||||
|
||||
cropRect.x = nx;
|
||||
cropRect.y = ny;
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (pressed) {
|
||||
updateCrop(mouse.x, mouse.y);
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
}
|
||||
onPressed: mouse => {
|
||||
updateCrop(mouse.x, mouse.y);
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
onReleased: {
|
||||
wrapper.changesMade = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Models
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
GridView {
|
||||
id: root
|
||||
|
||||
readonly property int columnsCount: Math.max(1, Math.floor(width / minCellWidth))
|
||||
readonly property int minCellWidth: 200 + Appearance.spacing.normal
|
||||
property bool shouldBeActive: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
cellHeight: 140 + Appearance.spacing.normal
|
||||
cellWidth: width / columnsCount
|
||||
clip: true
|
||||
implicitHeight: shouldBeActive ? contentHeight : 0
|
||||
interactive: false
|
||||
model: Wallpapers.list
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
scale: shouldBeActive ? 1 : 0.8
|
||||
visible: opacity > 0
|
||||
|
||||
delegate: Item {
|
||||
required property int index
|
||||
readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent
|
||||
readonly property real itemMargin: Appearance.spacing.normal
|
||||
readonly property real itemRadius: Appearance.rounding.small
|
||||
required property var modelData
|
||||
|
||||
height: root.cellHeight
|
||||
width: root.cellWidth
|
||||
|
||||
CustomClippingRect {
|
||||
id: image
|
||||
|
||||
anchors.bottomMargin: itemMargin
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: itemMargin
|
||||
anchors.rightMargin: itemMargin
|
||||
anchors.topMargin: itemMargin
|
||||
antialiasing: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: itemRadius
|
||||
|
||||
CachingImage {
|
||||
id: cachingImage
|
||||
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
cache: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
path: modelData.path
|
||||
smooth: true
|
||||
sourceSize: Qt.size(width, height)
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 1000
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: fallbackImage
|
||||
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
asynchronous: true
|
||||
cache: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0
|
||||
smooth: true
|
||||
source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : ""
|
||||
sourceSize: Qt.size(width, height)
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 1000
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
border.color: DynamicColors.palette.m3primary
|
||||
border.width: isCurrent ? 2 : 0
|
||||
color: "transparent"
|
||||
radius: itemRadius + 2
|
||||
smooth: true
|
||||
|
||||
Behavior on border.width {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
anchors.margins: Appearance.padding.small
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: "check_circle"
|
||||
visible: isCurrent
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: fallbackTimer
|
||||
|
||||
property bool triggered: false
|
||||
|
||||
interval: 800
|
||||
running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null
|
||||
|
||||
onTriggered: triggered = true
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
anchors.bottomMargin: itemMargin
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: itemMargin
|
||||
anchors.rightMargin: itemMargin
|
||||
anchors.topMargin: itemMargin
|
||||
radius: itemRadius
|
||||
|
||||
onClicked: {
|
||||
Wallpapers.setWallpaper(modelData.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.NavPane
|
||||
import qs.Config
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property SettingsState sState
|
||||
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
SearchBar {
|
||||
id: searchField
|
||||
|
||||
Layout.fillWidth: true
|
||||
bg.border.color: DynamicColors.palette.m3outlineVariant
|
||||
bg.color: DynamicColors.tPalette.m3surfaceContainerLowest
|
||||
clearIcon.font.pointSize: Appearance.font.size.large
|
||||
clearIcon.padding: Appearance.padding.extraSmall
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
placeholderText: qsTr("Search settings")
|
||||
searchIcon.anchors.leftMargin: Appearance.padding.largeIncreased
|
||||
searchIcon.font.pointSize: Appearance.font.size.large
|
||||
|
||||
Behavior on bg.border.color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
property: "searchOpen"
|
||||
target: root.sState
|
||||
value: searchField.text.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
NavLocations {
|
||||
Layout.bottomMargin: -bottomMargin
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -topMargin
|
||||
sState: root.sState
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Modules.Settings
|
||||
|
||||
VerticalFadeFlickable {
|
||||
id: root
|
||||
|
||||
required property SettingsState sState
|
||||
|
||||
bottomMargin: Appearance.padding.large
|
||||
contentHeight: content.implicitHeight
|
||||
fadeAmount: 0.1
|
||||
topMargin: Appearance.padding.large
|
||||
|
||||
TapHandler {
|
||||
onTapped: root.focus = true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.spacing.extraSmall
|
||||
|
||||
Repeater {
|
||||
id: list
|
||||
|
||||
model: PageRegistry.pages
|
||||
|
||||
CustomRect {
|
||||
id: item
|
||||
|
||||
required property int index
|
||||
readonly property bool isCategoryEnd: index === list.model.length - 1 || PageRegistry.pages[index + 1].category !== modelData.category
|
||||
readonly property bool isCategoryStart: index === 0 || PageRegistry.pages[index - 1].category !== modelData.category
|
||||
readonly property bool isCurrentPage: index === root.sState.currentPageIdx
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: index !== 0 && isCategoryStart ? Appearance.spacing.small : 0
|
||||
bottomLeftRadius: stateLayer.pressed ? Appearance.rounding.medium : isCurrentPage ? Appearance.rounding.large : isCategoryEnd ? Appearance.rounding.normal : Appearance.rounding.extraSmall
|
||||
bottomRightRadius: stateLayer.pressed ? Appearance.rounding.medium : isCurrentPage ? Appearance.rounding.large : isCategoryEnd ? Appearance.rounding.normal : Appearance.rounding.extraSmall
|
||||
color: isCurrentPage ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: {
|
||||
const h = layout.implicitHeight + layout.anchors.margins * 2;
|
||||
return h % 2 === 0 ? h : h + 1;
|
||||
}
|
||||
topLeftRadius: stateLayer.pressed ? Appearance.rounding.medium : isCurrentPage ? Appearance.rounding.large : isCategoryStart ? Appearance.rounding.normal : Appearance.rounding.extraSmall
|
||||
topRightRadius: stateLayer.pressed ? Appearance.rounding.medium : isCurrentPage ? Appearance.rounding.large : isCategoryStart ? Appearance.rounding.normal : Appearance.rounding.extraSmall
|
||||
|
||||
RadiusBehavior on bottomLeftRadius {
|
||||
}
|
||||
RadiusBehavior on bottomRightRadius {
|
||||
}
|
||||
RadiusBehavior on topLeftRadius {
|
||||
}
|
||||
RadiusBehavior on topRightRadius {
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
anchors.fill: parent
|
||||
bottomLeftRadius: parent.bottomLeftRadius
|
||||
bottomRightRadius: parent.bottomRightRadius
|
||||
topLeftRadius: parent.topLeftRadius
|
||||
topRightRadius: parent.topRightRadius
|
||||
|
||||
onClicked: root.sState.currentPageIdx = item.index
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomRect {
|
||||
Layout.bottomMargin: -1
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: -1
|
||||
color: item.isCurrentPage ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
|
||||
implicitWidth: height
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 1
|
||||
color: item.isCurrentPage ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
|
||||
fill: item.modelData.noFill ? 0 : 1
|
||||
font.pointSize: Appearance.font.size.large
|
||||
grade: 25
|
||||
text: item.modelData.icon
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: item.modelData.name
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
text: item.modelData.description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component RadiusBehavior: Behavior {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Modules.Settings.Common
|
||||
import qs.Modules.Settings.Pages
|
||||
import qs.Modules.Settings.Pages.Wallpaper
|
||||
import qs.Modules.Settings.Pages.Audio
|
||||
import qs.Modules.Settings.Pages.Apps
|
||||
import qs.Modules.Settings.Pages.Panels
|
||||
import qs.Modules.Settings.Pages.Panels.Bar
|
||||
import qs.Modules.Settings.Pages.Services
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
readonly property list<Component> pageComps: [
|
||||
// Appearance
|
||||
Component {
|
||||
// Wallpaper & style
|
||||
StackPage {
|
||||
Component {
|
||||
WallpaperPage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
WallpaperSelect {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Component {
|
||||
// Screenshot
|
||||
StackPage {
|
||||
Component {
|
||||
ScreenshotPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Connectivity
|
||||
Component {
|
||||
PlaceholderComp {
|
||||
}
|
||||
},
|
||||
Component {
|
||||
PlaceholderComp {
|
||||
}
|
||||
},
|
||||
Component {
|
||||
// Audio
|
||||
StackPage {
|
||||
Component {
|
||||
AudioPage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
AppVolumes {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Shell
|
||||
Component {
|
||||
StackPage {
|
||||
Component {
|
||||
PanelsPage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
BarPanel {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
DashboardPanel {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
ResourcesPanel {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
LauncherPanel {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
SidebarPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Bar sub pages
|
||||
Component {
|
||||
BarTray {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
BarStatusIcons {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
BarClock {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Apps
|
||||
Component {
|
||||
StackPage {
|
||||
Component {
|
||||
AppsPage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
AllApps {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
AppInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Services
|
||||
Component {
|
||||
StackPage {
|
||||
Component {
|
||||
ServicesPage {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
NotificationsPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// About
|
||||
Component {
|
||||
StackPage {
|
||||
Component {
|
||||
AboutPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
readonly property Component placeholderComp: Component {
|
||||
PlaceholderComp {
|
||||
}
|
||||
}
|
||||
|
||||
component PlaceholderComp: Item {
|
||||
property SettingsState sState // To avoid the warning from non-existent property
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.padding.extraSmall
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: "handyman"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
text: qsTr("Page under construction")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
text: qsTr("This page will be available in a future update.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property list<var> pages: [
|
||||
// Appearance
|
||||
{
|
||||
name: qsTr("Appearance"),
|
||||
icon: "colors",
|
||||
description: qsTr("Colors, animations, font, wallpaper"),
|
||||
category: "appearance"
|
||||
},
|
||||
{
|
||||
name: qsTr("Screenshot"),
|
||||
icon: "screenshot_region",
|
||||
description: qsTr("Set screenshot effects"),
|
||||
category: "appearance"
|
||||
},
|
||||
|
||||
// Connectivity
|
||||
{
|
||||
name: qsTr("Network"),
|
||||
icon: "wifi",
|
||||
description: qsTr("Wi-Fi, ethernet"),
|
||||
category: "connectivity"
|
||||
},
|
||||
{
|
||||
name: qsTr("Connected devices"),
|
||||
icon: "devices_other",
|
||||
description: qsTr("Bluetooth, pairing"),
|
||||
category: "connectivity"
|
||||
},
|
||||
{
|
||||
name: qsTr("Audio"),
|
||||
icon: "volume_up",
|
||||
description: qsTr("App volumes, sound devices"),
|
||||
category: "connectivity"
|
||||
},
|
||||
|
||||
// Shell
|
||||
{
|
||||
name: qsTr("Panels"),
|
||||
icon: "dock_to_bottom",
|
||||
description: qsTr("Bar, dashboard, launcher, sidebar"),
|
||||
category: "shell"
|
||||
},
|
||||
{
|
||||
name: qsTr("Apps"),
|
||||
icon: "apps",
|
||||
description: qsTr("Default apps, favorites, hidden apps"),
|
||||
category: "shell"
|
||||
},
|
||||
{
|
||||
name: qsTr("Services"),
|
||||
icon: "build",
|
||||
description: qsTr("Poll intervals, audio and brightness increments"),
|
||||
category: "shell"
|
||||
},
|
||||
{
|
||||
name: qsTr("About"),
|
||||
icon: "info",
|
||||
description: qsTr("System information"),
|
||||
category: "about"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import QtQuick
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int animOff
|
||||
property Item currentItem
|
||||
property int lastPageIdx
|
||||
required property SettingsState sState
|
||||
|
||||
function loadPage(idx: int): void {
|
||||
if (currentItem)
|
||||
currentItem.destroy();
|
||||
|
||||
const comp = PageCompRegistry.pageComps[idx] ?? PageCompRegistry.placeholderComp;
|
||||
const incubator = comp.incubateObject(container, {
|
||||
sState
|
||||
});
|
||||
|
||||
const attach = () => {
|
||||
incubator.object.anchors.fill = container;
|
||||
currentItem = incubator.object;
|
||||
};
|
||||
|
||||
if (incubator.status === Component.Ready)
|
||||
attach();
|
||||
else
|
||||
incubator.onStatusChanged = status => {
|
||||
if (status === Component.Ready)
|
||||
attach();
|
||||
};
|
||||
}
|
||||
|
||||
Item {
|
||||
id: container
|
||||
|
||||
anchors.fill: parent
|
||||
layer.enabled: opacity < 1
|
||||
objectName: "PageContainer"
|
||||
|
||||
Component.onCompleted: root.loadPage(root.sState.currentPageIdx)
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onCurrentPageIdxChanged(): void {
|
||||
switchAnim.complete();
|
||||
root.animOff = Appearance.padding.normal * (root.sState.currentPageIdx > root.lastPageIdx ? 1 : -1);
|
||||
switchAnim.start();
|
||||
root.lastPageIdx = root.sState.currentPageIdx;
|
||||
}
|
||||
|
||||
target: root.sState
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: switchAnim
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: container
|
||||
to: 0
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
|
||||
ScriptAction {
|
||||
script: root.loadPage(root.sState.currentPageIdx)
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
property: "topMargin"
|
||||
target: container.anchors
|
||||
value: root.animOff
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
property: "bottomMargin"
|
||||
target: container.anchors
|
||||
value: -root.animOff
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: container
|
||||
to: 1
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
|
||||
Anim {
|
||||
properties: "topMargin,bottomMargin"
|
||||
target: container.anchors
|
||||
to: 0
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import ZShell
|
||||
import qs.Modules.Settings.Common
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
import qs.Components
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
property string cliVersion
|
||||
readonly property int pluginCount: 0
|
||||
property string quickshellVersion
|
||||
|
||||
title: qsTr("About")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
Process {
|
||||
command: ["quickshell", "--version"]
|
||||
running: true
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: root.quickshellVersion = text.trim().split(" ")[1] ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
ConnectedRect {
|
||||
Layout.fillWidth: true
|
||||
first: true
|
||||
implicitHeight: hero.implicitHeight + Appearance.padding.extraLarge * 2
|
||||
last: true
|
||||
|
||||
ColumnLayout {
|
||||
id: hero
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.small
|
||||
width: parent.width - Appearance.padding.largeIncreased * 2
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Appearance.spacing.small
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: "ZShell"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.medium
|
||||
text: ZUtils.version ? `v${ZUtils.version}` : "…"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// System
|
||||
SectionHeader {
|
||||
text: qsTr("System")
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
first: true
|
||||
label: qsTr("Hostname")
|
||||
value: SystemInfo.hostname
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("Device")
|
||||
value: SystemInfo.device
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("Distro")
|
||||
value: SystemInfo.osPrettyName || SystemInfo.osName
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("Kernel")
|
||||
value: SystemInfo.kernel
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("Firmware")
|
||||
last: true
|
||||
value: SystemInfo.firmware
|
||||
}
|
||||
|
||||
// Software
|
||||
SectionHeader {
|
||||
text: qsTr("Software")
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
first: true
|
||||
label: qsTr("Shell")
|
||||
value: ZUtils.version || "…"
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("Quickshell")
|
||||
value: root.quickshellVersion || "…"
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("Qt")
|
||||
last: true
|
||||
value: ZUtils.qtVersion || "…"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Settings.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.Settings.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 bottom 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.Settings.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"
|
||||
status: Config.general.apps.terminal.join(" ")
|
||||
text: qsTr("Terminal")
|
||||
|
||||
onSelected: app => Config.general.apps.terminal = app.command
|
||||
}
|
||||
|
||||
DefaultRow {
|
||||
icon: "volume_up"
|
||||
status: Config.general.apps.audio.join(" ")
|
||||
text: qsTr("Audio")
|
||||
|
||||
onSelected: app => Config.general.apps.audio = app.command
|
||||
}
|
||||
|
||||
DefaultRow {
|
||||
icon: "play_circle"
|
||||
status: Config.general.apps.playback.join(" ")
|
||||
text: qsTr("Media playback")
|
||||
|
||||
onSelected: app => Config.general.apps.playback = app.command
|
||||
}
|
||||
|
||||
DefaultRow {
|
||||
icon: "folder"
|
||||
last: true
|
||||
status: Config.general.apps.explorer.join(" ")
|
||||
text: qsTr("File manager")
|
||||
|
||||
onSelected: app => Config.general.apps.explorer = app.command
|
||||
}
|
||||
|
||||
// Library
|
||||
SectionHeader {
|
||||
text: qsTr("Library")
|
||||
}
|
||||
|
||||
NavRow {
|
||||
first: true
|
||||
icon: "apps"
|
||||
last: true
|
||||
status: qsTr("Browse installed apps, set favorites and hidden")
|
||||
text: qsTr("All apps")
|
||||
|
||||
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: 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.Settings.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.Settings.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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
readonly property list<MenuItem> clockFormats: [
|
||||
MenuItem {
|
||||
text: qsTr("Long format")
|
||||
value: "dddd d MMM - hh:mm"
|
||||
},
|
||||
MenuItem {
|
||||
text: qsTr("Short format")
|
||||
value: "ddd d MMM - hh:mm"
|
||||
},
|
||||
MenuItem {
|
||||
text: qsTr("Time only")
|
||||
value: "hh:mm"
|
||||
},
|
||||
MenuItem {
|
||||
text: qsTr("Long format seconds")
|
||||
value: "dddd d MMM - hh:mm:ss"
|
||||
},
|
||||
MenuItem {
|
||||
text: qsTr("Short format seconds")
|
||||
value: "ddd d MMM - hh:mm:ss"
|
||||
},
|
||||
MenuItem {
|
||||
text: qsTr("Time only seconds")
|
||||
value: "hh:mm:ss"
|
||||
}
|
||||
]
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Clock")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
SelectRow {
|
||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||
active: root.clockFormats.find(item => item.value === Config.general.dateFormat)
|
||||
first: true
|
||||
last: true
|
||||
menuItems: root.clockFormats
|
||||
subtext: qsTr("Change how time is displayed in the widget")
|
||||
text: qsTr("Time format")
|
||||
|
||||
onSelected: item => {
|
||||
Config.general.dateFormat = item.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("System icons")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// Visible icons
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("Visible icons")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.bar.tray.showAudio
|
||||
first: true
|
||||
text: qsTr("Speakers")
|
||||
|
||||
onToggled: Config.bar.tray.showAudio = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.bar.tray.showMicrophone
|
||||
text: qsTr("Microphone")
|
||||
|
||||
onToggled: Config.bar.tray.showMicrophone = checked
|
||||
}
|
||||
|
||||
//////// FOR LATER:
|
||||
// ToggleRow {
|
||||
// checked: Config.bar.tray.showNetwork
|
||||
// text: qsTr("Network")
|
||||
//
|
||||
// onToggled: Config.bar.tray.showNetwork = checked
|
||||
// }
|
||||
//
|
||||
// ToggleRow {
|
||||
// checked: Config.bar.tray.showWifi
|
||||
// text: qsTr("Wi-Fi")
|
||||
//
|
||||
// onToggled: Config.bar.tray.showWifi = checked
|
||||
// }
|
||||
//
|
||||
// ToggleRow {
|
||||
// checked: Config.bar.tray.showBluetooth
|
||||
// text: qsTr("Bluetooth")
|
||||
//
|
||||
// onToggled: Config.bar.tray.showBluetooth = checked
|
||||
// }
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.bar.tray.showPower
|
||||
last: true
|
||||
text: qsTr("Battery")
|
||||
|
||||
onToggled: Config.bar.tray.showPower = checked
|
||||
}
|
||||
|
||||
// Behaviour
|
||||
SectionHeader {
|
||||
text: qsTr("Behavior")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.bar.popouts.audio
|
||||
first: true
|
||||
subtext: qsTr("Show a details popout when hovering the audio icons")
|
||||
text: qsTr("Audio popout on hover")
|
||||
|
||||
onToggled: Config.bar.popouts.audio = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.bar.popouts.upower
|
||||
last: true
|
||||
subtext: qsTr("Show a details popout when hovering the power icon")
|
||||
text: qsTr("Power popout on hover")
|
||||
|
||||
onToggled: Config.bar.popouts.upower = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Tray")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
SpinRow {
|
||||
first: true
|
||||
from: 12
|
||||
last: true
|
||||
stepSize: 1
|
||||
text: qsTr("Icon size")
|
||||
to: 24
|
||||
value: Config.bar.tray.trayIconSize
|
||||
|
||||
onMoved: value => Config.bar.tray.trayIconSize = value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Taskbar")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// Behavior
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("Behavior")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.bar.autoHide
|
||||
first: true
|
||||
last: true
|
||||
subtext: qsTr("Hide the bar, reveal on hover")
|
||||
text: qsTr("Auto hide")
|
||||
|
||||
onToggled: Config.bar.autoHide = checked
|
||||
}
|
||||
|
||||
// Components
|
||||
SectionHeader {
|
||||
text: qsTr("Components")
|
||||
}
|
||||
|
||||
NavRow {
|
||||
first: true
|
||||
icon: "widgets"
|
||||
status: qsTr("System tray icons")
|
||||
text: qsTr("Tray")
|
||||
|
||||
onClicked: root.sState.openSubPage(6)
|
||||
}
|
||||
|
||||
NavRow {
|
||||
icon: "signal_cellular_alt"
|
||||
status: qsTr("Visible indicators")
|
||||
text: qsTr("Status icons")
|
||||
|
||||
onClicked: root.sState.openSubPage(7)
|
||||
}
|
||||
|
||||
NavRow {
|
||||
icon: "schedule"
|
||||
last: true
|
||||
status: qsTr("Date, icon, background")
|
||||
text: qsTr("Clock")
|
||||
|
||||
onClicked: root.sState.openSubPage(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Dashboard")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// General
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("General")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.enabled
|
||||
first: true
|
||||
last: true
|
||||
text: qsTr("Enabled")
|
||||
|
||||
onToggled: Config.dashboard.enabled = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Launcher")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// General
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("General")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.launcher.enabled
|
||||
first: true
|
||||
last: true
|
||||
text: qsTr("Enabled")
|
||||
|
||||
onToggled: Config.launcher.enabled = checked
|
||||
}
|
||||
|
||||
// Display
|
||||
SectionHeader {
|
||||
text: qsTr("Display")
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
first: true
|
||||
from: 1
|
||||
stepSize: 1
|
||||
text: qsTr("Max items shown")
|
||||
to: 20
|
||||
value: Config.launcher.maxAppsShown
|
||||
|
||||
onMoved: v => Config.launcher.maxShown = v
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
from: 1
|
||||
stepSize: 1
|
||||
text: qsTr("Max wallpapers")
|
||||
to: 30
|
||||
value: Config.launcher.maxWallpapers
|
||||
|
||||
onMoved: v => Config.launcher.maxWallpapers = v
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
from: 0
|
||||
last: true
|
||||
stepSize: 5
|
||||
subtext: qsTr("Pixels dragged before the launcher opens")
|
||||
text: qsTr("Drag threshold")
|
||||
to: 200
|
||||
value: Config.launcher.dragThreshold
|
||||
|
||||
onMoved: v => Config.launcher.dragThreshold = v
|
||||
}
|
||||
|
||||
// Behavior
|
||||
SectionHeader {
|
||||
text: qsTr("Behavior")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.launcher.enableDangerousActions
|
||||
first: true
|
||||
last: true
|
||||
subtext: qsTr("Allow actions that shut down or log out")
|
||||
text: qsTr("Enable dangerous actions")
|
||||
|
||||
onToggled: Config.launcher.enableDangerousActions = checked
|
||||
}
|
||||
|
||||
// Fuzzy search
|
||||
SectionHeader {
|
||||
text: qsTr("Fuzzy search")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.launcher.useFuzzy.apps
|
||||
first: true
|
||||
text: qsTr("Apps")
|
||||
|
||||
onToggled: Config.launcher.useFuzzy.apps = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.launcher.useFuzzy.actions
|
||||
text: qsTr("Actions")
|
||||
|
||||
onToggled: Config.launcher.useFuzzy.actions = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.launcher.useFuzzy.schemes
|
||||
text: qsTr("Schemes")
|
||||
|
||||
onToggled: Config.launcher.useFuzzy.schemes = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.launcher.useFuzzy.variants
|
||||
text: qsTr("Variants")
|
||||
|
||||
onToggled: Config.launcher.useFuzzy.variants = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.launcher.useFuzzy.wallpapers
|
||||
last: true
|
||||
text: qsTr("Wallpapers")
|
||||
|
||||
onToggled: Config.launcher.useFuzzy.wallpapers = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Resources")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// General
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("General")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.performance.enabled
|
||||
first: true
|
||||
last: true
|
||||
text: qsTr("Enabled")
|
||||
|
||||
onToggled: Config.dashboard.performance.enabled = checked
|
||||
}
|
||||
|
||||
// Performance widgets
|
||||
SectionHeader {
|
||||
text: qsTr("Performance widgets")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.performance.showBattery
|
||||
first: true
|
||||
text: qsTr("Battery")
|
||||
|
||||
onToggled: Config.dashboard.performance.showBattery = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.performance.showGpu
|
||||
text: qsTr("GPU")
|
||||
|
||||
onToggled: Config.dashboard.performance.showGpu = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.performance.showCpu
|
||||
text: qsTr("CPU")
|
||||
|
||||
onToggled: Config.dashboard.performance.showCpu = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.performance.showMemory
|
||||
text: qsTr("Memory")
|
||||
|
||||
onToggled: Config.dashboard.performance.showMemory = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.performance.showStorage
|
||||
text: qsTr("Storage")
|
||||
|
||||
onToggled: Config.dashboard.performance.showStorage = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.dashboard.performance.showNetwork
|
||||
last: true
|
||||
text: qsTr("Network")
|
||||
|
||||
onToggled: Config.dashboard.performance.showNetwork = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Sidebar")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("General")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.sidebar.enabled
|
||||
first: true
|
||||
text: qsTr("Enabled")
|
||||
|
||||
onToggled: Config.sidebar.enabled = checked
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
from: 0
|
||||
last: true
|
||||
stepSize: 5
|
||||
subtext: qsTr("Pixels dragged before the sidebar opens")
|
||||
text: qsTr("Drag threshold")
|
||||
to: 200
|
||||
value: Config.sidebar.dragThreshold
|
||||
|
||||
onMoved: v => Config.sidebar.dragThreshold = v
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
title: qsTr("Panels")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
NavRow {
|
||||
first: true
|
||||
icon: "dock_to_bottom"
|
||||
status: !Config.bar.autoHide ? qsTr("Always visible") : qsTr("Reveal on hover")
|
||||
text: qsTr("Bar")
|
||||
|
||||
onClicked: root.sState.openSubPage(1)
|
||||
}
|
||||
|
||||
NavRow {
|
||||
icon: "dashboard"
|
||||
status: Config.dashboard.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
text: qsTr("Dashboard")
|
||||
|
||||
onClicked: root.sState.openSubPage(2)
|
||||
}
|
||||
|
||||
NavRow {
|
||||
icon: "insert_chart"
|
||||
status: Config.dashboard.performance.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
text: qsTr("Resources")
|
||||
|
||||
onClicked: root.sState.openSubPage(3)
|
||||
}
|
||||
|
||||
NavRow {
|
||||
icon: "apps"
|
||||
status: Config.launcher.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
text: qsTr("Launcher")
|
||||
|
||||
onClicked: root.sState.openSubPage(4)
|
||||
}
|
||||
|
||||
NavRow {
|
||||
icon: "dock_to_right"
|
||||
last: true
|
||||
status: Config.sidebar.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
text: qsTr("Sidebar")
|
||||
|
||||
onClicked: root.sState.openSubPage(5)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Components
|
||||
import qs.Helpers
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Modules.Settings
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
title: qsTr("Screenshot effects")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("Effects")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.screenshot.enable_pp
|
||||
first: true
|
||||
text: qsTr("Enable effects")
|
||||
|
||||
onToggled: Config.screenshot.enable_pp = checked
|
||||
}
|
||||
|
||||
SelectRow {
|
||||
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
|
||||
enabled: Config.screenshot.enable_pp
|
||||
last: true
|
||||
subtext: qsTr("Automatic or manual effect values")
|
||||
text: qsTr("Effects mode")
|
||||
|
||||
menuItems: [
|
||||
MenuItem {
|
||||
icon: "build"
|
||||
text: qsTr("Manual")
|
||||
value: "manual"
|
||||
},
|
||||
MenuItem {
|
||||
icon: "rotate_auto"
|
||||
text: qsTr("Auto")
|
||||
value: "auto"
|
||||
}
|
||||
]
|
||||
|
||||
onSelected: item => {
|
||||
Config.screenshot.mode = item.value;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Rounded corners")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.screenshot.rounding
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
||||
first: true
|
||||
text: qsTr("Enable rounded corners")
|
||||
|
||||
onToggled: Config.screenshot.rounding = checked
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
||||
from: 0
|
||||
last: true
|
||||
stepSize: 1
|
||||
text: qsTr("Corner radius")
|
||||
to: 50
|
||||
value: Config.screenshot.radius
|
||||
|
||||
onMoved: value => {
|
||||
const newVal = Math.floor(value);
|
||||
Config.screenshot.radius = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Shadow")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.screenshot.shadow
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
||||
first: true
|
||||
text: qsTr("Enable shadow")
|
||||
|
||||
onToggled: Config.screenshot.shadow = checked
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
from: 0
|
||||
stepSize: 1
|
||||
text: qsTr("Shadow blur amount")
|
||||
to: 100
|
||||
value: Config.screenshot.shadow_blur
|
||||
|
||||
onMoved: value => {
|
||||
const newVal = Math.floor(value);
|
||||
Config.screenshot.shadow_blur = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
from: -100
|
||||
stepSize: 10
|
||||
text: qsTr("Shadow horizontal offset")
|
||||
to: 100
|
||||
value: Config.screenshot.shadow_offset_x
|
||||
|
||||
onMoved: value => {
|
||||
const newVal = Math.floor(value);
|
||||
Config.screenshot.shadow_offset_x = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||
from: -100
|
||||
last: true
|
||||
stepSize: 10
|
||||
text: qsTr("Shadow vertical offset")
|
||||
to: 100
|
||||
value: Config.screenshot.shadow_offset_y
|
||||
|
||||
onMoved: value => {
|
||||
const newVal = Math.floor(value);
|
||||
Config.screenshot.shadow_offset_y = newVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Modules.Settings.Common
|
||||
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
isSubPage: true
|
||||
title: qsTr("Notifications")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.extraSmall / 2
|
||||
width: root.cappedWidth
|
||||
|
||||
// Notifications
|
||||
SectionHeader {
|
||||
first: true
|
||||
text: qsTr("Notifications")
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.notifs.showInFullscreen
|
||||
first: true
|
||||
subtext: qsTr("Whether notifications appear over fullscreen apps")
|
||||
text: qsTr("Show in fullscreen")
|
||||
|
||||
onToggled: Config.notifs.showInFullscreen = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.notifs.expire
|
||||
subtext: qsTr("Dismiss notifications after their timeout")
|
||||
text: qsTr("Expire automatically")
|
||||
|
||||
onToggled: Config.notifs.expire = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.notifs.openExpanded
|
||||
subtext: qsTr("Show notifications expanded by default")
|
||||
text: qsTr("Open expanded")
|
||||
|
||||
onToggled: Config.notifs.openExpanded = checked
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
from: 1000
|
||||
stepSize: 500
|
||||
subtext: qsTr("Time before a notification dismisses (ms)")
|
||||
text: qsTr("Default timeout")
|
||||
to: 60000
|
||||
value: Config.notifs.defaultExpireTimeout
|
||||
|
||||
onMoved: v => Config.notifs.defaultExpireTimeout = Math.round(v)
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
from: 1
|
||||
last: true
|
||||
stepSize: 1
|
||||
subtext: qsTr("Notifications shown per group before collapsing")
|
||||
text: qsTr("Group preview count")
|
||||
to: 10
|
||||
value: Config.notifs.groupPreviewNum
|
||||
|
||||
onMoved: v => Config.notifs.groupPreviewNum = Math.round(v)
|
||||
}
|
||||
|
||||
// Toasts
|
||||
SectionHeader {
|
||||
text: qsTr("Toasts")
|
||||
}
|
||||
|
||||
SpinRow {
|
||||
first: true
|
||||
from: 1
|
||||
stepSize: 1
|
||||
subtext: qsTr("Maximum number of toasts shown at once")
|
||||
text: qsTr("Visible toasts")
|
||||
to: 10
|
||||
value: Config.utilities.maxToasts
|
||||
|
||||
onMoved: v => Config.utilities.maxToasts = Math.round(v)
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.utilities.toasts.chargingChanged
|
||||
text: qsTr("Charging changes")
|
||||
|
||||
onToggled: Config.utilities.toasts.chargingChanged = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.utilities.toasts.gameModeChanged
|
||||
text: qsTr("Game mode changes")
|
||||
|
||||
onToggled: Config.utilities.toasts.gameModeChanged = checked
|
||||
}
|
||||
|
||||
// ToggleRow {
|
||||
// checked: Config.utilities.toasts.dndChanged
|
||||
// text: qsTr("Do not disturb changes")
|
||||
//
|
||||
// onToggled: Config.utilities.toasts.dndChanged = checked
|
||||
// }
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.utilities.toasts.audioOutputChanged
|
||||
text: qsTr("Audio output changes")
|
||||
|
||||
onToggled: Config.utilities.toasts.audioOutputChanged = checked
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
checked: Config.utilities.toasts.audioInputChanged
|
||||
last: true
|
||||
text: qsTr("Audio input changes")
|
||||
|
||||
onToggled: Config.utilities.toasts.audioInputChanged = checked
|
||||
}
|
||||
|
||||
// ToggleRow {
|
||||
// checked: Config.utilities.toasts.capsLockChanged
|
||||
// text: qsTr("Caps lock changes")
|
||||
//
|
||||
// onToggled: Config.utilities.toasts.capsLockChanged = checked
|
||||
// }
|
||||
//
|
||||
// ToggleRow {
|
||||
// checked: Config.utilities.toasts.numLockChanged
|
||||
// text: qsTr("Num lock changes")
|
||||
//
|
||||
// onToggled: Config.utilities.toasts.numLockChanged = checked
|
||||
// }
|
||||
//
|
||||
// ToggleRow {
|
||||
// checked: Config.utilities.toasts.kbLayoutChanged
|
||||
// text: qsTr("Keyboard layout changes")
|
||||
//
|
||||
// onToggled: Config.utilities.toasts.kbLayoutChanged = checked
|
||||
// }
|
||||
//
|
||||
// ToggleRow {
|
||||
// checked: Config.utilities.toasts.vpnChanged
|
||||
// text: qsTr("VPN changes")
|
||||
//
|
||||
// onToggled: Config.utilities.toasts.vpnChanged = checked
|
||||
// }
|
||||
//
|
||||
// ToggleRow {
|
||||
// checked: Config.utilities.toasts.nowPlaying
|
||||
// last: true
|
||||
// text: qsTr("Now playing")
|
||||
//
|
||||
// onToggled: Config.utilities.toasts.nowPlaying = checked
|
||||
// }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user