114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
import { App, Astal, Gdk, Gtk } from "astal/gtk3"
|
|
import GLib from "gi://GLib"
|
|
import { Variable, bind } from "astal"
|
|
|
|
const DURATION = 180
|
|
|
|
interface DropdownProps {
|
|
gdkmonitor: Gdk.Monitor
|
|
target: Gtk.Widget
|
|
open: Variable<boolean>
|
|
width?: number
|
|
vertical?: boolean
|
|
className?: string
|
|
children: Array<JSX.Element>
|
|
}
|
|
|
|
export default function Dropdown({
|
|
gdkmonitor,
|
|
target,
|
|
open,
|
|
width = 260,
|
|
vertical = true,
|
|
className = "",
|
|
children,
|
|
}: DropdownProps) {
|
|
const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor
|
|
const GAP = 8
|
|
|
|
const child = (
|
|
<box
|
|
className={`dropdown ${className}`}
|
|
vertical={vertical}
|
|
widthRequest={width}
|
|
halign={Gtk.Align.START}
|
|
valign={Gtk.Align.START}
|
|
>
|
|
{children}
|
|
</box>
|
|
)
|
|
|
|
function reposition() {
|
|
const toplevel = target.get_toplevel()
|
|
const [, tx, ty] = target.translate_coordinates(toplevel, 0, 0)
|
|
const { width: bw, height: bh } = target.get_allocation()
|
|
const { width: sw } = gdkmonitor.get_geometry()
|
|
|
|
const x = Math.min(Math.max(tx + bw / 2 - width / 2, GAP), sw - width - GAP)
|
|
const y = ty + bh + GAP
|
|
|
|
child.marginStart = Math.round(x)
|
|
child.marginTop = Math.round(y)
|
|
}
|
|
|
|
// the window outlives the animation: it hides once the slide-out
|
|
// revealer finished, and hiding the window resolves to open=false
|
|
const winVisible = Variable(false)
|
|
let closeTimer: number | undefined
|
|
|
|
open.subscribe((v) => {
|
|
if (closeTimer !== undefined) {
|
|
GLib.source_remove(closeTimer)
|
|
closeTimer = undefined
|
|
}
|
|
if (v) {
|
|
reposition()
|
|
winVisible.set(true)
|
|
} else if (winVisible.get()) {
|
|
closeTimer = GLib.timeout_add(GLib.PRIORITY_DEFAULT, DURATION + 20, () => {
|
|
winVisible.set(false)
|
|
closeTimer = undefined
|
|
return GLib.SOURCE_REMOVE
|
|
})
|
|
}
|
|
})
|
|
|
|
return (
|
|
<window
|
|
className="popup-window"
|
|
application={App}
|
|
gdkmonitor={gdkmonitor}
|
|
namespace="ags-bar-popup"
|
|
layer={Astal.Layer.OVERLAY}
|
|
keymode={Astal.Keymode.EXCLUSIVE}
|
|
anchor={TOP | BOTTOM | LEFT | RIGHT}
|
|
exclusivity={Astal.Exclusivity.IGNORE}
|
|
visible={bind(winVisible)}
|
|
onNotifyVisible={(self) => {
|
|
if (!self.visible && open.get()) open.set(false)
|
|
}}
|
|
onButtonPressEvent={(self, e) => {
|
|
const event = e as unknown as Gdk.Event
|
|
const [, px, py] = event.get_coords()
|
|
const { x, y, width: w, height: h } = child.get_allocation()
|
|
const xOut = px < x || px > x + w
|
|
const yOut = py < y || py > y + h
|
|
|
|
if (xOut || yOut) open.set(false)
|
|
}}
|
|
onKeyPressEvent={(_, e) => {
|
|
const event = e as unknown as Gdk.Event
|
|
if (event.get_keyval()[1] === Gdk.KEY_Escape) open.set(false)
|
|
}}
|
|
>
|
|
<revealer
|
|
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
|
|
transitionDuration={DURATION}
|
|
revealChild={bind(open)}
|
|
>
|
|
{child}
|
|
</revealer>
|
|
</window>
|
|
)
|
|
}
|