initial commit, you are welcome odette

This commit is contained in:
gnat
2024-09-06 16:55:55 -07:00
commit af9d9e3451
37 changed files with 2858 additions and 0 deletions

141
windows/bar.js Normal file
View File

@ -0,0 +1,141 @@
import { battery_dial } from '../widgets/battery.js'
import { volume_dial } from '../widgets/volume.js'
import { brightness_dial } from '../widgets/brightness.js'
import { hyprworkspaces } from '../widgets/hyprworkspaces.js'
import { mpd_bar_controls } from '../widgets/mpd.js'
// import { systray } from '../widgets/systray.js'
import { bar_clock} from '../widgets/clock.js'
import { MenuWidget } from './menu.js'
import { reveal_launcher, launcher } from './launcher.js'
export const FakeBar = Widget.Window({
name: 'FakeBar',
exclusivity: 'exclusive',
anchor: ['left'],
margins: [0, 35],
child: Widget.Box({css: 'min-width: 1px;'})
})
let reveal_menu = Variable(false)
let reveal_menu_button = Variable(false)
const MenuRevealButton = Widget.Box({
children: [
Widget.Button({
vexpand: false,
vpack: 'center',
className: 'menu-visibility-button',
'on-primary-click': (self) => reveal_menu.value = !reveal_menu.value,
child: Widget.Icon({
icon: reveal_menu.bind().as(reveal => !reveal ? 'go-next-symbolic' : 'go-previous-symbolic')
})
})
]
})
const BarTopWidget = Widget.Box({
vertical: true,
vpack: 'start',
children: [
Widget.Box({
className: 'dial-container',
vertical: true,
spacing: 8,
children: [
battery_dial,
volume_dial,
brightness_dial
]
}),
Widget.Box({
css: 'margin-left: 35px; margin-right: 35px'
})
]
})
const BarMiddleWidget = Widget.Box({
children: [
Widget.EventBox({
'on-hover': () => reveal_menu_button.value = true,
child: Widget.Box({
css: 'min-width: 1px; min-height: 40px;'
})
}),
hyprworkspaces
]
})
const BarEndWidget = Widget.Box({
vertical: true,
vpack: 'end',
children: [
// systray,
mpd_bar_controls,
bar_clock
]
})
const BarWidget = Widget.CenterBox({
homogeneous: false,
vertical: true,
spacing: 10,
startWidget: BarTopWidget,
centerWidget: BarMiddleWidget,
endWidget: BarEndWidget
})
const MenuButtonRevealer = Widget.Revealer({
revealChild: reveal_menu_button.bind(),
transition: 'slide_right',
child: MenuRevealButton
})
const MenuRevealer = Widget.Revealer({
revealChild: reveal_menu.bind(),
transition: 'slide_right',
transitionDuration: 500,
child: MenuWidget,
})
const LauncherRevealer = Widget.Revealer({
revealChild: reveal_launcher.bind(),
transition: 'slide_right',
transitionDuration: 500,
child: launcher,
})
export const Bar = Widget.Window({
name: 'status-bar',
exclusivity: 'ignore',
keymode: reveal_launcher.bind().as(v => v ? 'exclusive' : 'on-demand'),
anchor: ['top', 'left', 'bottom'],
child: Widget.Overlay({
'pass-through': true,
overlay: Widget.Box({
children: [
Widget.EventBox({
'on-hover': () => reveal_menu_button.value = true,
'on-hover-lost': () => reveal_menu_button.value = false,
child: Widget.Box({
css: 'margin-right: 4px;',
children: [MenuButtonRevealer]
})
}),
]
}),
child: Widget.Box({
children: [
LauncherRevealer,
MenuRevealer,
BarWidget
]
})
})
})
export {
reveal_menu,
reveal_launcher
}

93
windows/launcher.js Normal file
View File

@ -0,0 +1,93 @@
const Applications = await Service.import("applications")
const reveal_launcher = Variable(false)
function appItem(app) {
return Widget.Button({
className: 'entry',
onClicked: () => {
reveal_launcher.value = false
app.launch()
},
attribute: { app },
child: Widget.Box([
Widget.Icon({
icon: app.icon_name || '',
size: 42,
}),
Widget.Label({
className: 'app-title',
label: app.name,
xalign: 0,
vpack: 'center',
truncate: 'end'
})
])
})
}
function _launcher() {
let applications = Applications.query('').map(appItem)
const list = Widget.Box({
vertical: true,
children: applications,
spacing: 12
})
function repopulate() {
applications = Applications.query('').map(appItem)
list.children = applications
}
const entry = Widget.Box({
className: 'search',
children: [
Widget.Icon({
icon: 'edit-find-symbolic',
size: 42,
}),
Widget.Entry({
hexpand: true,
className: 'search',
on_accept: () => {
applications.filter((item) => item.visible)[0]?.attribute.app.launch()
reveal_launcher.value = false
},
on_change: ({ text }) => applications.forEach(item => {
item.visible = item.attribute.app.match(text ?? '')
})
})
]
})
return Widget.Box({
vertical: true,
className: 'launcher',
children: [
entry,
Widget.Scrollable({
hscroll: 'never',
vexpand: true,
hexpand: true,
child: list
})
],
setup: self => self.hook(reveal_launcher, () => {
entry.text = ''
if (reveal_launcher.value) {
repopulate()
console.log('nya')
entry.text = ''
entry.grab_focus()
}
}, "changed")
})
}
const launcher = _launcher()
export {
reveal_launcher,
launcher
}

76
windows/lock.js Normal file
View File

@ -0,0 +1,76 @@
import { show_notification_popups } from './notifications.js'
// TODO: const { Gdk, GtkSessionLock } = imports.gi;
const { authenticateUser, exec } = Utils
export const show_lock = Variable(false)
const lock_ready = Variable(false)
const username = Variable()
const password_entry = Widget.Entry({
visibility: false,
xalign: 0.5,
onAccept: (self) => {
authenticateUser(username.value, self.text)
.then(() => {
show_lock.value = false
exec('rm /tmp/lock-pixelated.png')
show_notification_popups.value = true
})
.catch(() => self.text = '')
self.text = ''
username_entry.text = ''
username_entry.grab_focus()
},
setup: self => self.text = ''
})
const username_entry = Widget.Entry({
xalign: 0.5,
onAccept: (self) => {
username.value = self.text
password_entry.grab_focus()
},
})
const login_container = Widget.Box({
child: Widget.Box({
className: lock_ready.bind().as(b => b ? 'lock-ready' : 'lock'),
vpack: 'center',
hpack: 'center',
hexpand: 'true',
vertical: true,
child: Widget.Box({
vertical: true,
children: [
Widget.Box({className: 'img'}),
Widget.Box({
vertical: true,
children: [
username_entry,
password_entry
]
})
]
})
}),
setup: self => self.hook(show_lock, () => {
self.css = 'min-width: 2560px; min-height: 1600px; background: url("/tmp/lock-pixelated.png");'
lock_ready.value = true
})
})
export const Lock = Widget.Window({
name: 'lock',
visible: show_lock.bind().as(b => {
if (b) {
exec(`bash -c "grim - | convert - -scale 12.5% -scale 800% -filter point /tmp/lock-pixelated.png"`)
show_notification_popups.value = false
}
return b
}),
exclusivity: 'ignore',
keymode: 'exclusive',
child: login_container,
})

49
windows/menu.js Normal file
View File

@ -0,0 +1,49 @@
import { mpd_menu_controls } from '../widgets/mpd.js'
import { resource_dial } from '../widgets/resourceDial.js'
export const MenuWidget = Widget.Box({
vertical: true,
className: 'Menu',
children: [
mpd_menu_controls,
Widget.Box({
children: [
resource_dial(
(v) => `cpu: ${v}%`,
'microchip-solid',
1000,
"bash -c \"top -bn1 | grep 'Cpu(s)' | awk '{print \$2 + \$4}'\"",
v => v/100
),
resource_dial(
(v) => `mem: ${Math.round(100*v/31396)}%`,
'memory-solid',
1000,
"bash -c \"free -m | grep Mem | awk '{print $3}'\"",
v => v/31396
),
resource_dial(
(v) => `igpu: ${v}%`,
'expansion-card',
1000,
"bash -c \"cat /sys/class/drm/card1/device/gpu_busy_percent\"",
v => v/100
),
resource_dial(
(v) => `fan: ${v}rpm`,
'fan',
1000,
"bash -c \"sudo ectool pwmgetfanrpm | awk '{a+=\$4} END {print a/2}'\"",
v => v/5000
),
resource_dial(
(v) => `temp (cpu): ${v}`,
'thermometer',
1000,
"bash -c \"sudo ectool temps all | grep -E 'cpu' | awk '{print \$5}'\"",
v => v/100
),
]
})
]
})

175
windows/notifications.js Normal file
View File

@ -0,0 +1,175 @@
const Notifications = await Service.import('notifications')
const { Pango } = imports.gi;
Notifications.popupTimeout = 3000;
function notification_icon({ app_entry, app_icon, image }) {
if (image) {
return Widget.Box({
css: `background-image: url("${image}");`
+ 'background-size: contain;'
+ 'background-repeat: no-repeat;'
+ 'background-position: center;',
})
}
let icon = 'dialog-information-symbolic'
if (Utils.lookUpIcon(app_icon))
icon = app_icon
if (app_entry && Utils.lookUpIcon(app_entry))
icon = app_entry
return Widget.Box({
child: Widget.Icon(icon),
})
}
const notification = (n) => {
const icon = Widget.Box({
vpack: 'start',
class_name: 'icon',
child: notification_icon(n),
})
const title = Widget.Label({
className: 'title',
label: n.summary,
xalign: 0,
justify: 'left',
})
const body = Widget.Label({
className: 'body',
label: n.body,
xalign: 0,
justification: 'left',
maxWidthChars: 26,
wrap: true,
wrapMode: Pango.WrapMode.WORD_CHAR,
useMarkup: true,
})
const actions = Widget.Box({
class_name: "actions",
children: n.actions.map(({ id, label }) => Widget.Button({
class_name: "action-button",
on_clicked: () => {
n.invoke(id)
n.dismiss()
},
hexpand: true,
child: Widget.Label(label),
})),
})
const buttons = Widget.Box({
hpack: 'end',
children: [
Widget.Button({
className: 'button',
onClicked: () => n.dismiss(),
child: Widget.Label('')
})
]
})
const timeout_progress = Widget.ProgressBar({
className: 'timeout-bar',
hexpand: true,
vpack: 'end',
value: 1,
setup: (self) => {
self.poll(n.timeout/100, () => {
if (self.value > 0.01) {
self.value = self.value - .01
}
else { n.dismiss() } // Notifications.forceTimeout doesn't work for notifications that are bugged.
})
}
})
const layout = Widget.Box({
className: `notification ${n.urgency}`,
vertical: true,
children: [
buttons,
Widget.Box([
icon,
Widget.Box({
vertical: true,
children: [
title, body
]
})
]),
actions,
timeout_progress
]
})
return Widget.Revealer({
className: 'revealer',
attribute: { id: n.id },
vpack: 'start',
transition: 'slide_down',
transitionDuration: 250,
setup: (self) => {
Utils.timeout(1, () => self.set_reveal_child(true));
self.on('notify::reveal-child', () => {
if (self.reveal_child) Utils.timeout(125, () => self.child.set_reveal_child(true))
})
},
child: Widget.Revealer({
className: 'revealer',
hpack: 'end',
transition: 'slide_left',
transitionDuration: 250,
setup: (self) => self.on('notify::reveal-child', () => {
if (!self.reveal_child) Utils.timeout(250, () => self.parent.set_reveal_child(false))
}),
child: layout
})
})
}
export const show_notification_popups = Variable(true)
export const NotificationPopups = Widget.Window({
visible: show_notification_popups.bind(),
name: 'notifications',
anchor: ['top', 'right'],
layer: 'overlay',
margins: [5, 0, 0, 0],
child: Widget.Box({
className: 'notifications',
vertical: true,
widthRequest: 2,
heightRequest: 2,
children: Notifications.popups.map(notification),
setup: (self) => self.hook(Notifications, (_, id) => {
if (!id || Notifications.dnd) return;
const n = Notifications.getNotification(id)
if (!n) return;
self.children = [...self.children, notification(n)]
}, 'notified')
.hook(Notifications, (_, id) => {
if (!id) return;
const n = self.children.find(
(child) => child.attribute.id === id,
)
if (!n) return;
n.child.set_reveal_child(false)
Utils.timeout(n.child.transition_duration, () => n.destroy())
}, 'dismissed')
})
})

70
windows/top-bar.js Normal file
View File

@ -0,0 +1,70 @@
import { hyprworkspaces_grid } from '../widgets/hyprworkspaces.js'
import { volume_slider } from '../widgets/volume.js'
import { brightness_slider } from '../widgets/brightness.js'
import { battery_dial } from '../widgets/battery.js'
import { horizontal_clock } from '../widgets/clock.js'
import { active_window } from '../widgets/hypractive.js'
import { media } from '../widgets/mpris.js'
export const FakeBar = Widget.Window({
name: 'FakeBar',
exclusivity: 'exclusive',
anchor: ['left', 'top', 'right'],
margins: [35, 0],
child: Widget.Box({css: 'min-height: 1px;'})
})
const left_box = Widget.Box({
className: 'left',
hpack: 'start',
children: [
hyprworkspaces_grid,
media
]
})
const middle_box = Widget.Box({
className: 'middle',
hpack: 'center',
children: [
active_window
]
})
const right_box = Widget.Box({
className: 'right',
hpack: 'end',
children: [
Widget.Box({
className: 'sliderbox',
vertical: true,
children: [
volume_slider,
brightness_slider
]
}),
Widget.Box({
className: 'battery-container',
children: [battery_dial]
}),
Widget.Separator({vertical: true}),
horizontal_clock
]
})
const bar = Widget.Box({
className: 'bar',
children: [
left_box,
middle_box,
right_box
]
})
export const Bar = Widget.Window({
name: 'status-bar',
exclusivity: 'ignore',
// margins: [5, 5, 5, 5],
anchor: ['left', 'top', 'right'],
child: bar
})