✨ chore: control-center and center-window widgets to gtk4 and ags v3
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
import { bind, Variable } from "astal";
|
||||
import { Tile, TileProps } from "./Tile";
|
||||
import AstalBluetooth from "gi://AstalBluetooth";
|
||||
import { BluetoothPage } from "../pages/Bluetooth";
|
||||
import { TilesPages } from "../Tiles";
|
||||
|
||||
|
||||
export const TileBluetooth = () => {
|
||||
const icon: Variable<string> = Variable.derive([
|
||||
bind(AstalBluetooth.get_default(), "isPowered"),
|
||||
bind(AstalBluetooth.get_default(), "isConnected")
|
||||
],
|
||||
(powered: boolean, isConnected: boolean) =>
|
||||
powered ? ( isConnected ?
|
||||
"bluetooth-active-symbolic"
|
||||
: "bluetooth-symbolic"
|
||||
) : "bluetooth-disabled-symbolic"
|
||||
);
|
||||
return Tile({
|
||||
title: "Bluetooth",
|
||||
visible: bind(AstalBluetooth.get_default(), "adapter").as(Boolean),
|
||||
description: bind(AstalBluetooth.get_default(), "isConnected").as((connected) => {
|
||||
const connectedDev = AstalBluetooth.get_default().devices.filter(dev => dev.connected)?.[0];
|
||||
return connected && connectedDev ? connectedDev.get_alias() : ""
|
||||
}),
|
||||
onDestroy: () => icon.drop(),
|
||||
onToggledOn: () => AstalBluetooth.get_default().adapter?.set_powered(true),
|
||||
onToggledOff: () => AstalBluetooth.get_default().adapter?.set_powered(false),
|
||||
onClickMore: () => TilesPages?.toggle(BluetoothPage()),
|
||||
enableOnClickMore: true,
|
||||
icon: icon(),
|
||||
iconSize: 16,
|
||||
toggleState: bind(AstalBluetooth.get_default(), "isPowered")
|
||||
} as TileProps)();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Tile } from "./Tile";
|
||||
import AstalBluetooth from "gi://AstalBluetooth";
|
||||
import { BluetoothPage } from "../pages/Bluetooth";
|
||||
import { TilesPages } from "../Tiles";
|
||||
import { createBinding, createComputed } from "ags";
|
||||
|
||||
|
||||
export const TileBluetooth = () =>
|
||||
<Tile title={"Bluetooth"} visible={
|
||||
createBinding(AstalBluetooth.get_default(), "adapter").as(Boolean)
|
||||
} description={createBinding(AstalBluetooth.get_default(), "isConnected").as((connected) => {
|
||||
const connectedDev = AstalBluetooth.get_default().devices.filter(dev => dev.connected)?.[0];
|
||||
return connected && connectedDev ? connectedDev.get_alias() : ""
|
||||
})} onToggledOn={() => AstalBluetooth.get_default().adapter?.set_powered(true)}
|
||||
onToggledOff={() => AstalBluetooth.get_default().adapter?.set_powered(false)}
|
||||
onClickMore={() => TilesPages?.toggle(BluetoothPage())}
|
||||
enableOnClickMore={true} iconSize={16}
|
||||
toggleState={createBinding(AstalBluetooth.get_default(), "isPowered")}
|
||||
icon={createComputed([
|
||||
createBinding(AstalBluetooth.get_default(), "isPowered"),
|
||||
createBinding(AstalBluetooth.get_default(), "isConnected")
|
||||
],
|
||||
(powered: boolean, isConnected: boolean) =>
|
||||
powered ? ( isConnected ?
|
||||
"bluetooth-active-symbolic"
|
||||
: "bluetooth-symbolic"
|
||||
) : "bluetooth-disabled-symbolic")}
|
||||
/>;
|
||||
@@ -1,15 +0,0 @@
|
||||
import { bind } from "astal";
|
||||
import { Notifications } from "../../../scripts/notifications";
|
||||
import { Tile } from "./Tile";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
|
||||
export const TileDND = Tile({
|
||||
title: tr("control_center.tiles.dnd.title"),
|
||||
description: bind(Notifications.getDefault().getNotifd(), "dontDisturb").as(
|
||||
(dnd: boolean) => dnd ? tr("control_center.tiles.enabled") : tr("control_center.tiles.disabled")),
|
||||
onToggledOff: () => Notifications.getDefault().getNotifd().dontDisturb = false,
|
||||
onToggledOn: () => Notifications.getDefault().getNotifd().dontDisturb = true,
|
||||
icon: "minus-circle-filled-symbolic",
|
||||
iconSize: 16,
|
||||
toggleState: Notifications.getDefault().getNotifd().dontDisturb
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Notifications } from "../../../scripts/notifications";
|
||||
import { Tile } from "./Tile";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { createBinding } from "ags";
|
||||
|
||||
export const TileDND = () =>
|
||||
<Tile title={tr("control_center.tiles.dnd.title")}
|
||||
description={createBinding(Notifications.getDefault().getNotifd(), "dontDisturb").as(
|
||||
(dnd: boolean) => dnd ? tr("control_center.tiles.enabled") : tr("control_center.tiles.disabled"))}
|
||||
onToggledOff={() => Notifications.getDefault().getNotifd().dontDisturb = false}
|
||||
onToggledOn={() => Notifications.getDefault().getNotifd().dontDisturb = true}
|
||||
icon={"minus-circle-filled-symbolic"}
|
||||
iconSize={16}
|
||||
toggleState={Notifications.getDefault().getNotifd().dontDisturb}
|
||||
/>;
|
||||
@@ -1,86 +0,0 @@
|
||||
import { bind, execAsync, Variable } from "astal";
|
||||
import { Tile, TileProps } from "./Tile";
|
||||
import AstalNetwork from "gi://AstalNetwork";
|
||||
import { Widget } from "astal/gtk3";
|
||||
import { PageNetwork } from "../pages/Network";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { TilesPages } from "../Tiles";
|
||||
|
||||
export const TileNetwork = () => new Widget.Box({
|
||||
child: Variable.derive([
|
||||
bind(AstalNetwork.get_default(), "primary"),
|
||||
bind(AstalNetwork.get_default(), "wired"),
|
||||
bind(AstalNetwork.get_default(), "wifi")
|
||||
],
|
||||
(primary: AstalNetwork.Primary, wired: AstalNetwork.Wired, wifi: AstalNetwork.Wifi) => {
|
||||
if(primary === AstalNetwork.Primary.WIFI) {
|
||||
return Tile({
|
||||
title: tr("control_center.tiles.network.wireless"),
|
||||
description: Variable.derive(
|
||||
[ bind(wifi, "ssid"), bind(wifi, "internet") ],
|
||||
(ssid: string, internet: AstalNetwork.Internet) =>
|
||||
ssid ? ssid : (() => {
|
||||
switch(internet) {
|
||||
case AstalNetwork.Internet.CONNECTED:
|
||||
return tr("connected");
|
||||
case AstalNetwork.Internet.DISCONNECTED:
|
||||
return tr("disconnected");
|
||||
case AstalNetwork.Internet.CONNECTING:
|
||||
return tr("connecting") + "...";
|
||||
}
|
||||
})()
|
||||
)(),
|
||||
onToggledOn: () => wifi.set_enabled(true),
|
||||
onToggledOff: () => wifi.set_enabled(false),
|
||||
onClickMore: () => TilesPages?.toggle(PageNetwork()),
|
||||
icon: "network-wireless-signal-excellent-symbolic",
|
||||
toggleState: bind(wifi, "enabled")
|
||||
} as TileProps)();
|
||||
|
||||
} else if(primary === AstalNetwork.Primary.WIRED) {
|
||||
return Tile({
|
||||
title: tr("control_center.tiles.network.wired") || "Wired",
|
||||
description: bind(wired, "internet").as((internet: AstalNetwork.Internet) => {
|
||||
switch(internet) {
|
||||
case AstalNetwork.Internet.CONNECTED:
|
||||
return tr("connected");
|
||||
case AstalNetwork.Internet.DISCONNECTED:
|
||||
return tr("disconnected");
|
||||
case AstalNetwork.Internet.CONNECTING:
|
||||
return tr("connecting") + "...";
|
||||
}
|
||||
}),
|
||||
onToggledOn: () => execAsync("nmcli n on"),
|
||||
onToggledOff: () => execAsync("nmcli n off"),
|
||||
onClickMore: () => TilesPages?.toggle(PageNetwork()),
|
||||
icon: bind(wired, "internet").as((internet: AstalNetwork.Internet) => {
|
||||
switch(internet) {
|
||||
case AstalNetwork.Internet.CONNECTED:
|
||||
return "network-wired-symbolic";
|
||||
case AstalNetwork.Internet.DISCONNECTED:
|
||||
return "network-wired-disconnected-symbolic";
|
||||
}
|
||||
|
||||
return "network-wired-no-route-symbolic";
|
||||
}),
|
||||
iconSize: 16,
|
||||
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
|
||||
internet === AstalNetwork.Internet.CONNECTING
|
||||
|| internet === AstalNetwork.Internet.CONNECTED
|
||||
)
|
||||
} as TileProps)();
|
||||
}
|
||||
|
||||
return Tile({
|
||||
title: tr("control_center.tiles.network.network"),
|
||||
description: tr("disconnected"),
|
||||
onToggledOn: () => execAsync("nmcli n on"),
|
||||
onToggledOff: () => execAsync("nmcli n off"),
|
||||
onClickMore: () => TilesPages?.toggle(PageNetwork()),
|
||||
icon: "network-wired-disconnected-symbolic",
|
||||
iconSize: 16,
|
||||
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
|
||||
internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED)
|
||||
} as TileProps)();
|
||||
})()
|
||||
} as Widget.BoxProps);
|
||||
@@ -0,0 +1,85 @@
|
||||
import { execAsync } from "ags/process";
|
||||
import { Tile } from "./Tile";
|
||||
import AstalNetwork from "gi://AstalNetwork";
|
||||
import { PageNetwork } from "../pages/Network";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { TilesPages } from "../Tiles";
|
||||
import { Gtk } from "ags/gtk4";
|
||||
import { createBinding, createComputed, With } from "ags";
|
||||
|
||||
export const TileNetwork = () => <Gtk.Box>
|
||||
<With value={createComputed([
|
||||
createBinding(AstalNetwork.get_default(), "primary"),
|
||||
createBinding(AstalNetwork.get_default(), "wired"),
|
||||
createBinding(AstalNetwork.get_default(), "wifi")
|
||||
])}>
|
||||
|
||||
{([primary, wired, wifi]: [AstalNetwork.Primary, AstalNetwork.Wired, AstalNetwork.Wifi]) => {
|
||||
if(primary === AstalNetwork.Primary.WIFI) {
|
||||
return <Tile title={tr("control_center.tiles.network.wireless")}
|
||||
description={createComputed([
|
||||
createBinding(wifi, "ssid"), createBinding(wifi, "internet")
|
||||
], (ssid, internet) => ssid ? ssid : (() => {
|
||||
switch(internet) {
|
||||
case AstalNetwork.Internet.CONNECTED:
|
||||
return tr("connected");
|
||||
case AstalNetwork.Internet.DISCONNECTED:
|
||||
return tr("disconnected");
|
||||
case AstalNetwork.Internet.CONNECTING:
|
||||
return tr("connecting") + "...";
|
||||
}
|
||||
})()
|
||||
)} onToggledOn={() => wifi.set_enabled(true)}
|
||||
onToggledOff={() => wifi.set_enabled(false)}
|
||||
onClickMore={() => TilesPages?.toggle(PageNetwork())}
|
||||
icon={"network-wireless-signal-excellent-symbolic"}
|
||||
toggleState={createBinding(wifi, "enabled")}
|
||||
/>
|
||||
|
||||
} else if(primary === AstalNetwork.Primary.WIRED) {
|
||||
return <Tile title={tr("control_center.tiles.network.wired")}
|
||||
description={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) => {
|
||||
switch(internet) {
|
||||
case AstalNetwork.Internet.CONNECTED:
|
||||
return tr("connected");
|
||||
case AstalNetwork.Internet.DISCONNECTED:
|
||||
return tr("disconnected");
|
||||
case AstalNetwork.Internet.CONNECTING:
|
||||
return tr("connecting") + "...";
|
||||
}
|
||||
})}
|
||||
onToggledOn={() => execAsync("nmcli n on")}
|
||||
onToggledOff={() => execAsync("nmcli n off")}
|
||||
onClickMore={() => TilesPages?.toggle(PageNetwork())}
|
||||
icon={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) => {
|
||||
switch(internet) {
|
||||
case AstalNetwork.Internet.CONNECTED:
|
||||
return "network-wired-symbolic";
|
||||
case AstalNetwork.Internet.DISCONNECTED:
|
||||
return "network-wired-disconnected-symbolic";
|
||||
}
|
||||
|
||||
return "network-wired-no-route-symbolic";
|
||||
})}
|
||||
iconSize={16}
|
||||
toggleState={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) =>
|
||||
internet === AstalNetwork.Internet.CONNECTING
|
||||
|| internet === AstalNetwork.Internet.CONNECTED
|
||||
)}
|
||||
/>
|
||||
}
|
||||
|
||||
return <Tile
|
||||
title={tr("control_center.tiles.network.network")}
|
||||
description={tr("disconnected")}
|
||||
onToggledOn={() => execAsync("nmcli n on")}
|
||||
onToggledOff={() => execAsync("nmcli n off")}
|
||||
onClickMore={() => TilesPages?.toggle(PageNetwork())}
|
||||
icon={"network-wired-disconnected-symbolic"}
|
||||
iconSize={16}
|
||||
toggleState={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) =>
|
||||
internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED)}
|
||||
/>
|
||||
}}
|
||||
</With>
|
||||
</Gtk.Box> as Gtk.Box;
|
||||
@@ -1,26 +0,0 @@
|
||||
import { bind, Variable } from "astal";
|
||||
import { Tile, TileProps } from "./Tile";
|
||||
import { NightLight } from "../../../scripts/nightlight";
|
||||
import { PageNightLight } from "../pages/NightLight";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { TilesPages } from "../Tiles";
|
||||
import { isInstalled } from "../../../scripts/utils";
|
||||
import { Widget } from "astal/gtk3";
|
||||
|
||||
export const TileNightLight = () => isInstalled("hyprsunset") ? Tile({
|
||||
title: tr("control_center.tiles.night_light.title"),
|
||||
icon: "weather-clear-night-symbolic",
|
||||
description: Variable.derive([
|
||||
bind(NightLight.getDefault(), "temperature"),
|
||||
bind(NightLight.getDefault(), "gamma")
|
||||
], (temp, gamma) => `${temp === NightLight.getDefault().identityTemperature ?
|
||||
tr("control_center.tiles.night_light.default_desc") : `${temp}K`} ${
|
||||
gamma < NightLight.getDefault().maxGamma ? `(${gamma}%)` : ""}`
|
||||
)(),
|
||||
onToggledOff: () => NightLight.getDefault().identity = true,
|
||||
onToggledOn: () => NightLight.getDefault().identity = false,
|
||||
enableOnClickMore: true,
|
||||
onClickMore: () => TilesPages?.toggle(PageNightLight()),
|
||||
toggleState: bind(NightLight.getDefault(), "identity").as(identity => !identity)
|
||||
} as TileProps)()
|
||||
: new Widget.Box({ visible: false } as Widget.BoxProps);
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Tile } from "./Tile";
|
||||
import { NightLight } from "../../../scripts/nightlight";
|
||||
import { PageNightLight } from "../pages/NightLight";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { TilesPages } from "../Tiles";
|
||||
import { isInstalled } from "../../../scripts/utils";
|
||||
import { createBinding, createComputed } from "ags";
|
||||
|
||||
export const TileNightLight = () =>
|
||||
<Tile title={tr("control_center.tiles.night_light.title")}
|
||||
icon={"weather-clear-night-symbolic"}
|
||||
description={createComputed([
|
||||
createBinding(NightLight.getDefault(), "temperature"),
|
||||
createBinding(NightLight.getDefault(), "gamma")
|
||||
], (temp, gamma) => `${temp === NightLight.getDefault().identityTemperature ?
|
||||
tr("control_center.tiles.night_light.default_desc") : `${temp}K`} ${
|
||||
gamma < NightLight.getDefault().maxGamma ? `(${gamma}%)` : ""}`
|
||||
)}
|
||||
visible={isInstalled("hyprsunset")}
|
||||
onToggledOff={() => NightLight.getDefault().identity = true}
|
||||
onToggledOn={() => NightLight.getDefault().identity = false}
|
||||
enableOnClickMore={true}
|
||||
onClickMore={() => TilesPages?.toggle(PageNightLight())}
|
||||
toggleState={createBinding(NightLight.getDefault(), "identity").as(identity => !identity)}
|
||||
/>
|
||||
@@ -1,38 +0,0 @@
|
||||
import { Tile, TileProps } from "./Tile";
|
||||
import { Recording } from "../../../scripts/recording";
|
||||
import { bind, Variable } from "astal";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { getDateTime } from "../../../scripts/time";
|
||||
import { isInstalled } from "../../../scripts/utils";
|
||||
|
||||
const wfRecorderInstalled = isInstalled("wf-recorder");
|
||||
|
||||
export const TileRecording = () => {
|
||||
const description: Variable<string> = Variable.derive([
|
||||
bind(Recording.getDefault(), "recording"),
|
||||
getDateTime()
|
||||
], (recording, dateTime) => {
|
||||
if(!recording || !Recording.getDefault().startedAt)
|
||||
return tr("control_center.tiles.recording.disabled_desc") || "Start recording";
|
||||
|
||||
const startedAtSeconds = dateTime.to_unix() - Recording.getDefault().startedAt!.to_unix();
|
||||
if(startedAtSeconds <= 0) return "00:00";
|
||||
|
||||
const minutes = Math.floor(startedAtSeconds / 60);
|
||||
const seconds = Math.floor(startedAtSeconds % 60);
|
||||
|
||||
return `${ minutes < 10 ? `0${minutes}` : minutes }:${ seconds < 10 ? `0${seconds}` : seconds }`;
|
||||
});
|
||||
|
||||
return Tile({
|
||||
title: tr("control_center.tiles.recording.title") || "Screen Recording",
|
||||
description: description(),
|
||||
icon: "media-record-symbolic",
|
||||
visible: wfRecorderInstalled,
|
||||
onDestroy: () => description.drop(),
|
||||
onToggledOff: () => Recording.getDefault().stopRecording(),
|
||||
onToggledOn: () => Recording.getDefault().startRecording(),
|
||||
toggleState: bind(Recording.getDefault(), "recording"),
|
||||
iconSize: 16
|
||||
} as TileProps)();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Tile } from "./Tile";
|
||||
import { Recording } from "../../../scripts/recording";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { isInstalled, time } from "../../../scripts/utils";
|
||||
import { createBinding, createComputed } from "ags";
|
||||
import { Gtk } from "ags/gtk4";
|
||||
|
||||
|
||||
export const TileRecording = () =>
|
||||
<Tile title={tr("control_center.tiles.recording.title")}
|
||||
description={createComputed([
|
||||
createBinding(Recording.getDefault(), "recording"),
|
||||
time
|
||||
], (recording, dateTime) => {
|
||||
if(!recording || !Recording.getDefault().startedAt)
|
||||
return tr("control_center.tiles.recording.disabled_desc") || "Start recording";
|
||||
|
||||
const startedAtSeconds = dateTime.to_unix() - Recording.getDefault().startedAt!;
|
||||
if(startedAtSeconds <= 0) return "00:00";
|
||||
|
||||
const minutes = Math.floor(startedAtSeconds / 60);
|
||||
const seconds = Math.floor(startedAtSeconds % 60);
|
||||
|
||||
return `${ minutes < 10 ? `0${minutes}` : minutes }:${ seconds < 10 ? `0${seconds}` : seconds }`;
|
||||
})}
|
||||
icon={"media-record-symbolic"}
|
||||
visible={isInstalled("wf-recorder")}
|
||||
onToggledOff={() => Recording.getDefault().stopRecording()}
|
||||
onToggledOn={() => Recording.getDefault().startRecording()}
|
||||
toggleState={createBinding(Recording.getDefault(), "recording")}
|
||||
iconSize={16}
|
||||
/> as Gtk.Widget;
|
||||
@@ -1,129 +0,0 @@
|
||||
import { Binding, Variable } from "astal";
|
||||
import { Gtk, Widget } from "astal/gtk3";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
|
||||
export type TileProps = {
|
||||
className?: string | Binding<string | undefined>;
|
||||
icon?: string | Binding<string | undefined>;
|
||||
visible?: boolean | Binding<boolean | undefined>;
|
||||
iconSize?: number | Binding<number | undefined>;
|
||||
title: string | Binding<string | undefined>;
|
||||
description?: string | Binding<string | undefined>;
|
||||
toggleState?: boolean | Binding<boolean | undefined>;
|
||||
enableOnClickMore?: boolean | Binding<boolean | undefined>;
|
||||
onDestroy?: () => void;
|
||||
onToggledOn: () => void;
|
||||
onToggledOff: () => void;
|
||||
onClickMore?: () => void;
|
||||
}
|
||||
|
||||
export function Tile(props: TileProps): (() => Gtk.Widget) {
|
||||
const subs: Array<() => void> = [];
|
||||
const toggled = new Variable<boolean>(((props.toggleState instanceof Binding) ?
|
||||
props.toggleState.get()
|
||||
: props.toggleState) ?? false);
|
||||
|
||||
if(props?.toggleState instanceof Binding)
|
||||
subs.push(props.toggleState.subscribe((state) =>
|
||||
toggled.set(state ?? false)
|
||||
));
|
||||
|
||||
return () => new Widget.Box({
|
||||
className: (props.className instanceof Binding) ?
|
||||
Variable.derive([
|
||||
props.className,
|
||||
toggled()
|
||||
], (className, isToggled) =>
|
||||
`tile ${className} ${isToggled ? "toggled" : ""} ${
|
||||
props.onClickMore ? "has-more" : ""
|
||||
}`
|
||||
)()
|
||||
: toggled().as((state: boolean) =>
|
||||
`tile${state ? " toggled" : ""}${
|
||||
props.onClickMore ? " has-more" : ""
|
||||
}`
|
||||
),
|
||||
expand: true,
|
||||
visible: props.visible,
|
||||
onDestroy: () => {
|
||||
subs.map(sub => sub?.());
|
||||
props.onDestroy?.();
|
||||
},
|
||||
children: [
|
||||
new Widget.Button({
|
||||
className: "toggle-button",
|
||||
onClick: () => {
|
||||
if(toggled.get()) {
|
||||
toggled.set(false);
|
||||
props.onToggledOff && props.onToggledOff();
|
||||
return;
|
||||
}
|
||||
|
||||
toggled.set(true);
|
||||
props.onToggledOn && props.onToggledOn();
|
||||
},
|
||||
child: new Widget.Box({
|
||||
className: "content",
|
||||
expand: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
new Widget.Icon({
|
||||
className: "icon",
|
||||
icon: props.icon,
|
||||
visible: (props.icon instanceof Binding) ?
|
||||
props.icon.as(Boolean)
|
||||
: Boolean(props.icon),
|
||||
css: `font-size: ${props.iconSize ?? 16}px;`
|
||||
} as Widget.IconProps),
|
||||
new Widget.Box({
|
||||
className: "text",
|
||||
orientation: Gtk.Orientation.VERTICAL,
|
||||
vexpand: true,
|
||||
hexpand: true,
|
||||
valign: Gtk.Align.CENTER,
|
||||
children: [
|
||||
new Widget.Label({
|
||||
className: "title",
|
||||
xalign: 0,
|
||||
halign: Gtk.Align.START,
|
||||
truncate: true,
|
||||
label: props.title
|
||||
} as Widget.LabelProps),
|
||||
new Widget.Label({
|
||||
className: "description",
|
||||
visible: (props.description instanceof Binding) ?
|
||||
props.description.as(Boolean)
|
||||
: Boolean(props.description),
|
||||
halign: Gtk.Align.START,
|
||||
truncate: true,
|
||||
xalign: 0,
|
||||
label: (props.description instanceof Binding) ?
|
||||
props.description.as((desc) => desc ? desc : "")
|
||||
: (props.description || "")
|
||||
} as Widget.LabelProps)
|
||||
]
|
||||
} as Widget.BoxProps)
|
||||
]
|
||||
} as Widget.BoxProps)
|
||||
} as Widget.ButtonProps),
|
||||
new Widget.Button({
|
||||
className: "more icon",
|
||||
visible: props.onClickMore !== undefined,
|
||||
halign: Gtk.Align.END,
|
||||
tooltipText: tr("control_center.tiles.more") || "More",
|
||||
image: new Widget.Icon({
|
||||
icon: "go-next-symbolic",
|
||||
css: "icon { font-size: 16px; }"
|
||||
}),
|
||||
onClick: () => {
|
||||
((props.enableOnClickMore instanceof Binding) ?
|
||||
props.enableOnClickMore.get()
|
||||
: props.enableOnClickMore) && props?.onToggledOn();
|
||||
|
||||
props.onClickMore && props?.onClickMore()
|
||||
},
|
||||
widthRequest: 32
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { Gdk, Gtk } from "ags/gtk4";
|
||||
import { tr } from "../../../i18n/intl";
|
||||
import { Accessor, createComputed, createState } from "ags";
|
||||
import GObject from "gi://GObject?version=2.0";
|
||||
import Pango from "gi://Pango?version=1.0";
|
||||
import { variableToBoolean } from "../../../scripts/utils";
|
||||
|
||||
export type TileProps = {
|
||||
class?: string | Accessor<string>;
|
||||
icon?: string | Accessor<string>;
|
||||
visible?: boolean | Accessor<boolean>;
|
||||
iconSize?: number | Accessor<number>;
|
||||
title: string | Accessor<string>;
|
||||
description?: string | Accessor<string>;
|
||||
toggleState?: boolean | Accessor<boolean>;
|
||||
enableOnClickMore?: boolean | Accessor<boolean>;
|
||||
onDestroy?: () => void;
|
||||
onToggledOn: () => void;
|
||||
onToggledOff: () => void;
|
||||
onClickMore?: () => void;
|
||||
}
|
||||
|
||||
export function Tile(props: TileProps): Gtk.Widget {
|
||||
const subs: Array<() => void> = [];
|
||||
const [toggled, setToggled] = createState(((props.toggleState instanceof Accessor) ?
|
||||
props.toggleState.get()
|
||||
: props.toggleState) ?? false);
|
||||
|
||||
|
||||
(props.toggleState instanceof Accessor) && subs.push(
|
||||
props.toggleState.subscribe(() =>
|
||||
setToggled((props.toggleState as Accessor<boolean>).get() ?? false))
|
||||
);
|
||||
|
||||
return <Gtk.Box class={
|
||||
(props.class instanceof Accessor) ?
|
||||
createComputed([props.class, toggled], (clss, isToggled) =>
|
||||
`tile ${clss} ${isToggled ? "toggled" : ""} ${
|
||||
props.onClickMore ? "has-more" : ""
|
||||
}`
|
||||
)
|
||||
: toggled.as(isToggled =>
|
||||
`tile ${props.class ? props.class : ""} ${isToggled ? "toggled" : ""} ${
|
||||
props.onClickMore ? "has-more" : ""
|
||||
}`
|
||||
)
|
||||
} hexpand={true} visible={props.visible} onDestroy={(_) => {
|
||||
subs.forEach(sub => sub());
|
||||
props.onDestroy?.();
|
||||
}}>
|
||||
|
||||
<Gtk.Button class={"toggle-button"} $={(self) => {
|
||||
const gestureClick = Gtk.GestureClick.new();
|
||||
const conns: Map<GObject.Object, number> = new Map();
|
||||
|
||||
self.add_controller(gestureClick);
|
||||
|
||||
conns.set(gestureClick, gestureClick.connect("released", (gesture) => {
|
||||
if(gesture.get_current_button() === Gdk.BUTTON_PRIMARY) {
|
||||
if(toggled.get()) {
|
||||
setToggled(false);
|
||||
props.onToggledOff?.();
|
||||
return;
|
||||
}
|
||||
|
||||
setToggled(true);
|
||||
props.onToggledOn?.();
|
||||
}
|
||||
}));
|
||||
}}>
|
||||
|
||||
<Gtk.Box class={"content"} hexpand={true} vexpand={true}>
|
||||
{props.icon && <Gtk.Image class={"icon"} iconName={props.icon} css={
|
||||
(props.iconSize instanceof Accessor) ?
|
||||
props.iconSize.as(size => `font-size: ${size}px;`)
|
||||
: (props.iconSize ?
|
||||
`font-size: ${props.iconSize ?? 16}px;`
|
||||
: undefined)
|
||||
} />}
|
||||
|
||||
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} class={"text"} vexpand={true} hexpand={true}
|
||||
valign={Gtk.Align.CENTER}>
|
||||
|
||||
<Gtk.Label class={"title"} xalign={0} halign={Gtk.Align.START} ellipsize={Pango.EllipsizeMode.END}
|
||||
label={props.title} />
|
||||
|
||||
{props.description && <Gtk.Label class={"description"} ellipsize={Pango.EllipsizeMode.END}
|
||||
visible={variableToBoolean(props.description)} xalign={0} label={
|
||||
(props.description instanceof Accessor) ?
|
||||
props.description.as(str => str ?? "")
|
||||
: (props.description ?? "")
|
||||
} halign={Gtk.Align.START}
|
||||
/>}
|
||||
|
||||
</Gtk.Box>
|
||||
</Gtk.Box>
|
||||
</Gtk.Button>
|
||||
|
||||
<Gtk.Button class={"more icon"} iconName={"go-next-symbolic"} widthRequest={32}
|
||||
visible={Boolean(props.onClickMore)} halign={Gtk.Align.END} onClicked={() => {
|
||||
((props.enableOnClickMore instanceof Accessor) ?
|
||||
props.enableOnClickMore.get()
|
||||
: props.enableOnClickMore) && props.onToggledOn?.();
|
||||
|
||||
props.onClickMore?.();
|
||||
}} tooltipText={tr("control_center.tiles.more")} />
|
||||
</Gtk.Box> as Gtk.Widget;
|
||||
}
|
||||
Reference in New Issue
Block a user