Settings menu now warns when a setting is unsaved. (#65)
* Settings menu now warns when a setting is unsaved. Additionally, font-weight increments properly now. * Remove font weight fix - another PR already open for fix.
This commit is contained in:
@@ -355,3 +355,9 @@ dialog {
|
|||||||
color: $bar-menus-label;
|
color: $bar-menus-label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unsaved-icon {
|
||||||
|
margin-right: 1em;
|
||||||
|
font-size: 1em;
|
||||||
|
color: $yellow;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const BarSettings = () => {
|
export const BarSettings = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "always",
|
vscroll: "always",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page paged-container",
|
class_name: "menu-theme-page paged-container",
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const DashboardMenuSettings = () => {
|
|||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
class_name: "bar-theme-page paged-container",
|
class_name: "bar-theme-page paged-container",
|
||||||
vscroll: "always",
|
vscroll: "always",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
overlayScrolling: true,
|
overlayScrolling: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const BarTheme = () => {
|
export const BarTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "always",
|
vscroll: "always",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "bar-theme-page paged-container",
|
class_name: "bar-theme-page paged-container",
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const BatteryMenuTheme = () => {
|
export const BatteryMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page battery paged-container",
|
class_name: "menu-theme-page battery paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const BluetoothMenuTheme = () => {
|
export const BluetoothMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page bluetooth paged-container",
|
class_name: "menu-theme-page bluetooth paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const ClockMenuTheme = () => {
|
export const ClockMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page clock paged-container",
|
class_name: "menu-theme-page clock paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const DashboardMenuTheme = () => {
|
export const DashboardMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "always",
|
vscroll: "always",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page dashboard paged-container",
|
class_name: "menu-theme-page dashboard paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const MenuTheme = () => {
|
export const MenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page paged-container",
|
class_name: "menu-theme-page paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const MediaMenuTheme = () => {
|
export const MediaMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page media paged-container",
|
class_name: "menu-theme-page media paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const NetworkMenuTheme = () => {
|
export const NetworkMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page network paged-container",
|
class_name: "menu-theme-page network paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const NotificationsMenuTheme = () => {
|
export const NotificationsMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page notifications paged-container",
|
class_name: "menu-theme-page notifications paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const SystrayMenuTheme = () => {
|
export const SystrayMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page systray paged-container",
|
class_name: "menu-theme-page systray paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const VolumeMenuTheme = () => {
|
export const VolumeMenuTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "menu-theme-page volume paged-container",
|
class_name: "menu-theme-page volume paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const NotificationsTheme = () => {
|
export const NotificationsTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "notifications-theme-page paged-container",
|
class_name: "notifications-theme-page paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import options from "options";
|
|||||||
export const OsdTheme = () => {
|
export const OsdTheme = () => {
|
||||||
return Widget.Scrollable({
|
return Widget.Scrollable({
|
||||||
vscroll: "automatic",
|
vscroll: "automatic",
|
||||||
hscroll: "never",
|
hscroll: "automatic",
|
||||||
class_name: "osd-theme-page paged-container",
|
class_name: "osd-theme-page paged-container",
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Opt } from "lib/option"
|
|||||||
import Gdk from "gi://Gdk"
|
import Gdk from "gi://Gdk"
|
||||||
import icons from "lib/icons"
|
import icons from "lib/icons"
|
||||||
import { RowProps } from "lib/types/options"
|
import { RowProps } from "lib/types/options"
|
||||||
|
import { Variable } from "types/variable";
|
||||||
|
|
||||||
const EnumSetter = (opt: Opt<string>, values: string[]) => {
|
const EnumSetter = (opt: Opt<string>, values: string[]) => {
|
||||||
const lbl = Widget.Label({ label: opt.bind().as(v => `${v}`) })
|
const lbl = Widget.Label({ label: opt.bind().as(v => `${v}`) })
|
||||||
@@ -34,33 +35,99 @@ export const Inputter = <T>({
|
|||||||
min = 0,
|
min = 0,
|
||||||
increment = 1
|
increment = 1
|
||||||
}: RowProps<T>,
|
}: RowProps<T>,
|
||||||
className: string
|
className: string,
|
||||||
|
isUnsaved: Variable<boolean>
|
||||||
) => {
|
) => {
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
class_name: "inputter-container",
|
class_name: "inputter-container",
|
||||||
setup: self => {
|
setup: self => {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "number": return self.child = Widget.SpinButton({
|
case "number": return self.children = [
|
||||||
|
Widget.Box({
|
||||||
|
class_name: "unsaved-icon-container",
|
||||||
|
child: isUnsaved.bind("value").as(unsvd => {
|
||||||
|
if (unsvd) {
|
||||||
|
return Widget.Icon({
|
||||||
|
class_name: "unsaved-icon",
|
||||||
|
icon: icons.ui.warning,
|
||||||
|
tooltipText: "Press 'Enter' to apply your changes."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Widget.Box();
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Widget.SpinButton({
|
||||||
setup(self) {
|
setup(self) {
|
||||||
self.set_range(min, max)
|
self.set_range(min, max)
|
||||||
self.set_increments(1 * increment, 5 * increment)
|
self.set_increments(1 * increment, 5 * increment)
|
||||||
self.on("value-changed", () => opt.value = self.value as T)
|
self.on("value-changed", () => {
|
||||||
self.hook(opt, () => self.value = opt.value as number)
|
opt.value = self.value as T;
|
||||||
|
})
|
||||||
|
self.hook(opt, () => {
|
||||||
|
self.value = opt.value as number;
|
||||||
|
isUnsaved.value = Number(self.text) !== opt.value as number;
|
||||||
|
})
|
||||||
|
self.connect("key-release-event", () => {
|
||||||
|
isUnsaved.value = Number(self.text) !== opt.value as number;
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
]
|
||||||
|
|
||||||
case "float":
|
case "float":
|
||||||
case "object": return self.child = Widget.Entry({
|
case "object": return self.children = [
|
||||||
|
Widget.Box({
|
||||||
|
class_name: "unsaved-icon-container",
|
||||||
|
child: isUnsaved.bind("value").as(unsvd => {
|
||||||
|
if (unsvd) {
|
||||||
|
return Widget.Icon({
|
||||||
|
class_name: "unsaved-icon",
|
||||||
|
icon: icons.ui.warning,
|
||||||
|
tooltipText: "Press 'Enter' to apply your changes."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Widget.Box();
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Widget.Entry({
|
||||||
class_name: className,
|
class_name: className,
|
||||||
|
on_change: self => isUnsaved.value = self.text !== JSON.stringify(opt.value),
|
||||||
on_accept: self => opt.value = JSON.parse(self.text || ""),
|
on_accept: self => opt.value = JSON.parse(self.text || ""),
|
||||||
setup: self => self.hook(opt, () => self.text = JSON.stringify(opt.value)),
|
setup: self => self.hook(opt, () => {
|
||||||
|
self.text = JSON.stringify(opt.value);
|
||||||
|
isUnsaved.value = self.text !== JSON.stringify(opt.value);
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
case "string": return self.child = Widget.Entry({
|
|
||||||
on_accept: self => opt.value = self.text as T,
|
case "string": return self.children = [
|
||||||
setup: self => self.hook(opt, () => self.text = opt.value as string),
|
Widget.Box({
|
||||||
|
class_name: "unsaved-icon-container",
|
||||||
|
child: isUnsaved.bind("value").as(unsvd => {
|
||||||
|
if (unsvd) {
|
||||||
|
return Widget.Icon({
|
||||||
|
class_name: "unsaved-icon",
|
||||||
|
icon: icons.ui.warning,
|
||||||
|
tooltipText: "Press 'Enter' to apply your changes."
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
return Widget.Box();
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Widget.Entry({
|
||||||
|
class_name: isUnsaved.bind("value").as(unsaved => unsaved ? "unsaved" : ""),
|
||||||
|
on_change: self => isUnsaved.value = self.text !== opt.value,
|
||||||
|
on_accept: self => {
|
||||||
|
opt.value = self.text as T;
|
||||||
|
},
|
||||||
|
setup: self => self.hook(opt, () => {
|
||||||
|
isUnsaved.value = self.text !== opt.value;
|
||||||
|
self.text = opt.value as string;
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
case "enum": return self.child = EnumSetter(opt as unknown as Opt<string>, enums!)
|
case "enum": return self.child = EnumSetter(opt as unknown as Opt<string>, enums!)
|
||||||
case "boolean": return self.child = Widget.Switch()
|
case "boolean": return self.child = Widget.Switch()
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ type Option = {
|
|||||||
subtitle: string,
|
subtitle: string,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Option = <T>(props: RowProps<T>, className: string = '') => {
|
export const Option = <T>(props: RowProps<T>, className: string = '') => {
|
||||||
|
const isUnsaved = Variable(false);
|
||||||
|
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
class_name: "option-item",
|
class_name: "option-item",
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
@@ -19,7 +22,7 @@ export const Option = <T>(props: RowProps<T>, className: string = '') => {
|
|||||||
hexpand: true,
|
hexpand: true,
|
||||||
child: Label(props.title, props.subtitle || ""),
|
child: Label(props.title, props.subtitle || ""),
|
||||||
}),
|
}),
|
||||||
Inputter(props, className),
|
Inputter(props, className, isUnsaved),
|
||||||
Widget.Button({
|
Widget.Button({
|
||||||
vpack: "center",
|
vpack: "center",
|
||||||
class_name: "reset-options",
|
class_name: "reset-options",
|
||||||
|
|||||||
Reference in New Issue
Block a user