├── .github
├── README.md
└── assets
│ ├── banner.png
│ └── showcase.png
├── .gitmodules
├── binds
├── client
│ ├── init.lua
│ ├── keys.lua
│ └── mouse.lua
├── global
│ ├── init.lua
│ ├── keys.lua
│ └── mouse.lua
├── init.lua
└── mod.lua
├── config
├── apps.lua
├── auto.lua
├── rules.lua
└── user.lua
├── helpers.lua
├── module
└── json.lua
├── rc.lua
├── script
├── shooter.lua
├── tym-themer.lua
└── xresources.lua
├── signal
├── client.lua
├── init.lua
├── naughty.lua
├── screen.lua
├── system
│ ├── audio.lua
│ ├── battery.lua
│ ├── network.lua
│ ├── playerctl.lua
│ └── weather.lua
└── tag.lua
├── theme
├── assets
│ ├── default
│ │ └── pfp.png
│ ├── fonts
│ │ └── gwnce.ttf
│ ├── notif
│ │ ├── cancel.png
│ │ └── default.png
│ └── util
│ │ └── awesome.png
├── color
│ ├── init.lua
│ ├── lite-xl
│ │ ├── dark
│ │ │ └── init.lua
│ │ └── light
│ │ │ └── init.lua
│ ├── mardel
│ │ ├── dark
│ │ │ └── init.lua
│ │ └── light
│ │ │ └── init.lua
│ ├── oxocarbon
│ │ ├── dark
│ │ │ └── init.lua
│ │ └── light
│ │ │ └── init.lua
│ └── rose-pine
│ │ ├── dark
│ │ └── init.lua
│ │ └── light
│ │ └── init.lua
├── icons.lua
├── init.lua
└── theme.lua
├── ui
├── dash
│ ├── init.lua
│ └── module
│ │ ├── grid.lua
│ │ ├── init.lua
│ │ ├── player.lua
│ │ ├── random.lua
│ │ ├── slider.lua
│ │ ├── title.lua
│ │ └── user.lua
├── launcher
│ └── init.lua
├── menu
│ └── init.lua
├── notification
│ └── init.lua
├── osd
│ ├── init.lua
│ ├── player.lua
│ └── volume.lua
├── scratch
│ ├── init.lua
│ └── music.lua
├── time
│ ├── init.lua
│ └── module
│ │ ├── calendar.lua
│ │ ├── clock.lua
│ │ ├── init.lua
│ │ └── weather.lua
├── titlebar
│ ├── init.lua
│ └── normal.lua
└── wibar
│ ├── init.lua
│ └── module
│ ├── clock.lua
│ ├── dash.lua
│ ├── init.lua
│ ├── launcher.lua
│ ├── layoutbox.lua
│ ├── status.lua
│ ├── systray.lua
│ ├── taglist.lua
│ └── tasklist.lua
└── widget
├── init.lua
├── layoutbox.lua
└── textbox
├── colored.lua
├── init.lua
└── scrolling.lua
/.github/README.md:
--------------------------------------------------------------------------------
1 | 
2 | Welcome to this humble [AwesomeWM](https://awesomewm.org/) configuration that I made off
3 | my [modularized default rc.lua](https://github.com/Gwynsav/modular-awm-default), focused
4 | on being clean, simple and fast to use.
5 |
6 | > [!WARNING]
7 | > I have very bizarre ideas and am shameless enough to actually implement them here.
8 | Sometimes these ideas make the WM very slow or unstable, so please beware my incompetence.
9 |
10 | ## Installation
11 |
12 | ### Dependencies
13 | - `pactl` (usually provided by `pulseaudio-utils`) for audio widgets and keybinds. This
14 | does NOT mean that this setup only works with `pulseaudio`, you can also use `pipewire`
15 | by using `pipewire-pulse`.
16 | - `playerctl` (also usually `playerctl-{dev/devel}`) for music playback widgets and
17 | keybinds.
18 |
19 |
20 |
21 | As of right now, this is only a custom icon font and the AwesomeWM configuration, so to
22 | install it, just run:
23 | ```
24 | # Assuming ~/.config/ exists.
25 | git clone https://github.com/Gwynsav/gwileful.git ~/.config/awesome --recursive
26 | # Assuming ~/.local/share/fonts exists.
27 | cp ~/.config/awesome/theme/assets/fonts/* ~/.local/share/fonts
28 | fc-cache -f
29 | ```
30 | There are also some variables in the `config` directory, so make sure everything is
31 | defined correctly.
32 |
33 | ## Gallery
34 |
35 | 
36 |
37 | ## References and Acknowledgements
38 |
39 | All instances of me using others' code have a link to the original at the top of the file.
40 |
41 | Groups/projects:
42 | - Again, my [modularized default rc.lua](https://github.com/Gwynsav/modular-awm-default).
43 | - All projects used as submodules of this one, see `module/`.
44 | - [Feather Icons](https://feathericons.com/). Actually, I've moved away from these and
45 | made my own icons for everything here. But still, I used them in the past and as
46 | reference making my own icons, so I think they're still worth a mention.
47 | - [Fairfax](https://www.kreativekorp.com/software/fonts/fairfaxhd/), the beautiful font
48 | from KreativeKorp used for UI as well as terminal, in the past, that inspired the look of
49 | my own fonts currently in use in this rice.
50 |
51 | Individuals:
52 | - [sakuya](https://codeberg.org/moseni/bitmap-fonts). Creator of the `koishi` and `satori`
53 | fonts used in this rice previously, as well as help making my own icon font, used here.
54 | These fonts are no longer being distributed and so all current and future versions of this
55 | rice will not provide them nor display them. All fonts used from now on are created by
56 | myself and may become publically available in the future. Their rice also influenced the look
57 | of mine.
58 | - [Stardust-kyun](https://github.com/Stardust-kyun/dotfiles), references and some widgets.
59 | - [Kasper](https://github.com/Kasper24/KwesomeDE), used some of their daemons.
60 | - [Myagko](https://github.com/myagko/dotfiles), used their calendar.
61 | - [Crylia](https://github.com/Crylia/crylia-theme/), used some of their code.
62 | - [rxyhn](https://github.com/rxyhn/yoru), used calendar and some ideas.
63 | - The beautiful artwork I often display in the screenshots is by
64 | [みすたーおさる](https://www.pixiv.net/en/users/10770935) ("Mister Monkey" in english).
65 |
--------------------------------------------------------------------------------
/.github/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gwynsav/gwileful/a9bde5b98b9055fa432f74283a1ca9a33ae11b71/.github/assets/banner.png
--------------------------------------------------------------------------------
/.github/assets/showcase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gwynsav/gwileful/a9bde5b98b9055fa432f74283a1ca9a33ae11b71/.github/assets/showcase.png
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "module/bling"]
2 | path = module/bling
3 | url = https://github.com/BlingCorp/bling.git
4 | [submodule "module/awesome-battery_widget"]
5 | path = module/awesome-battery_widget
6 | url = https://github.com/Aire-One/awesome-battery_widget
7 | [submodule "module/rubato"]
8 | path = module/rubato
9 | url = https://github.com/andorlando/rubato
10 | [submodule "module/dbus_proxy"]
11 | path = module/dbus_proxy
12 | url = https://github.com/Gwynsav/dbus_proxy
13 |
--------------------------------------------------------------------------------
/binds/client/init.lua:
--------------------------------------------------------------------------------
1 | -- Returns all client mouse and keybinds.
2 | local require = require
3 | return {
4 | keys = require(... .. '.keys'),
5 | mouse = require(... .. '.mouse')
6 | }
7 |
--------------------------------------------------------------------------------
/binds/client/keys.lua:
--------------------------------------------------------------------------------
1 | local require, client = require, client
2 |
3 | local awful = require('awful')
4 |
5 | local mod = require('binds.mod')
6 | local modkey = mod.modkey
7 | local tabbed = require('module.bling').module.tabbed
8 |
9 | --- Client keybindings.
10 | client.connect_signal('request::default_keybindings', function()
11 | awful.keyboard.append_client_keybindings({
12 | -- Client state management.
13 | awful.key({ modkey, mod.shift }, 'q', function(c) c:kill() end,
14 | { description = 'close', group = 'client' }),
15 |
16 | awful.key({ modkey, }, 'space', awful.client.floating.toggle,
17 | { description = 'toggle floating', group = 'client' }),
18 | awful.key({ modkey, }, 't', function(c) c.ontop = not c.ontop end,
19 | { description = 'toggle keep on top', group = 'client' }),
20 | awful.key({ modkey, mod.ctrl }, 'space', function(c) c.sticky = not c.sticky end,
21 | { description = 'toggle sticky', group = 'client' }),
22 |
23 | awful.key({ modkey, }, 'n',
24 | function(c)
25 | -- The client currently has the input focus, so it cannot be
26 | -- minimized, since minimized clients can't have the focus.
27 | c.minimized = true
28 | end, { description = 'minimize', group = 'client' }),
29 | awful.key({ modkey, }, 'm',
30 | function(c)
31 | c.maximized = not c.maximized
32 | c:raise()
33 | end, { description = '(un)maximize', group = 'client' }),
34 | awful.key({ modkey, }, 'f', function(c)
35 | c.fullscreen = not c.fullscreen
36 | c:raise()
37 | end, { description = 'toggle fullscreen', group = 'client' }),
38 |
39 | -- Bling Tabbed management.
40 | awful.key({ modkey, mod.shift }, 'h', function()
41 | tabbed.pick_by_direction('up')
42 | end, { description = 'add client above focused to group', group = 'tabbing' }),
43 | awful.key({ modkey, mod.shift }, 'l', function()
44 | tabbed.pick_by_direction('down')
45 | end, { description = 'add client below focused to group', group = 'tabbing' }),
46 | awful.key({ modkey }, 'Escape', function() tabbed.pop() end,
47 | { description = 'remove client from tabbed group', group = 'tabbing' }),
48 | awful.key({ modkey }, 'Tab', function() tabbed.iter() end,
49 | { description = 'cycle tabbed client focus', group = 'tabbing' })
50 | })
51 | end)
52 |
--------------------------------------------------------------------------------
/binds/client/mouse.lua:
--------------------------------------------------------------------------------
1 | local require, client = require, client
2 |
3 | local awful = require('awful')
4 |
5 | local mod = require('binds.mod')
6 | local modkey = mod.modkey
7 |
8 | --- Client mouse bindings.
9 | client.connect_signal('request::default_mousebindings', function()
10 | awful.mouse.append_client_mousebindings({
11 | awful.button(nil, 1, function(c)
12 | c:activate({ context = 'mouse_click' })
13 | end),
14 | awful.button({ modkey }, 1, function(c)
15 | c:activate({ context = 'mouse_click', action = 'mouse_move' })
16 | end),
17 | awful.button({ modkey }, 3, function(c)
18 | c:activate({ context = 'mouse_click', action = 'mouse_resize' })
19 | end)
20 | })
21 | end)
22 |
--------------------------------------------------------------------------------
/binds/global/init.lua:
--------------------------------------------------------------------------------
1 | -- Returns all global WM mouse and keybinds.
2 | local require = require
3 | return {
4 | keys = require(... .. '.keys'),
5 | mouse = require(... .. '.mouse')
6 | }
7 |
--------------------------------------------------------------------------------
/binds/global/keys.lua:
--------------------------------------------------------------------------------
1 | local require, awesome, client = require, awesome, client
2 |
3 | local awful = require('awful')
4 |
5 | local mod = require('binds.mod')
6 | local modkey = mod.modkey
7 |
8 | local apps = require('config.apps')
9 | local audio = require('signal.system.audio')
10 | local pctl = require('signal.system.playerctl')
11 | local shooter = require('script.shooter')
12 | local scratch = require('ui.scratch')
13 |
14 | --- Global key bindings
15 | awful.keyboard.append_global_keybindings({
16 | -- AwesomeWM
17 | ------------
18 | -- General Awesome keys.
19 | awful.key({ modkey, }, 's', require('awful.hotkeys_popup').show_help,
20 | { description = 'show help', group = 'awesome' }),
21 | awful.key({ modkey, mod.ctrl }, 'r', awesome.restart,
22 | { description = 'reload awesome', group = 'awesome' }),
23 | awful.key({ modkey, }, 'Return', function() awful.spawn(apps.terminal) end,
24 | { description = 'open a terminal', group = 'launcher' }),
25 | awful.key({ modkey, }, 'p', function() awful.screen.focused().launcher:open() end,
26 | { description = 'show the launcher', group = 'launcher' }),
27 |
28 | -- Focus related keybindings.
29 | awful.key({ modkey, }, 'j', function() awful.client.focus.byidx( 1) end,
30 | { description = 'focus next by index', group = 'client' }),
31 | awful.key({ modkey, }, 'k', function() awful.client.focus.byidx(-1) end,
32 | { description = 'focus previous by index', group = 'client'}),
33 | awful.key({ modkey, mod.ctrl }, 'Left', function() awful.screen.focus_relative( 1) end,
34 | { description = 'focus the next screen', group = 'screen' }),
35 | awful.key({ modkey, mod.ctrl }, 'Right', function() awful.screen.focus_relative(-1) end,
36 | { description = 'focus the previous screen', group = 'screen' }),
37 |
38 | -- Layout related keybindings.
39 | awful.key({ modkey, mod.shift }, 'j', function() awful.client.swap.byidx( 1) end,
40 | { description = 'swap with next client by index', group = 'client' }),
41 | awful.key({ modkey, mod.shift }, 'k', function() awful.client.swap.byidx(-1) end,
42 | { description = 'swap with previous client by index', group = 'client' }),
43 | awful.key({ modkey, }, 'l', function() awful.tag.incmwfact( 0.05) end,
44 | { description = 'increase master width factor', group = 'layout' }),
45 | awful.key({ modkey, }, 'h', function() awful.tag.incmwfact(-0.05) end,
46 | { description = 'decrease master width factor', group = 'layout' }),
47 | awful.key({ modkey, }, 'equal', function() awful.tag.incnmaster( 1, nil, true) end,
48 | { description = 'increase the number of master clients', group = 'layout' }),
49 | awful.key({ modkey, }, 'minus', function() awful.tag.incnmaster(-1, nil, true) end,
50 | { description = 'decrease the number of master clients', group = 'layout' }),
51 | awful.key({ modkey, mod.alt }, 'k', function() awful.client.incwfact( 0.05) end,
52 | { description = 'increase client width factor', group = 'layout' }),
53 | awful.key({ modkey, mod.alt }, 'j', function() awful.client.incwfact(-0.05) end,
54 | { description = 'decrease client width factor', group = 'layout' }),
55 | awful.key({ modkey, mod.ctrl }, 'equal', function() awful.tag.incncol( 1, nil, true)
56 | end,{ description = 'increase the number of columns', group = 'layout' }),
57 | awful.key({ modkey, mod.ctrl }, 'minus', function() awful.tag.incncol(-1, nil, true)
58 | end, { description = 'decrease the number of columns', group = 'layout' }),
59 | awful.key({
60 | modifiers = { modkey },
61 | keygroup = 'numrow',
62 | description = 'only view tag',
63 | group = 'tag',
64 | on_press = function(index)
65 | local tag = awful.screen.focused().tags[index]
66 | if tag then tag:view_only() end
67 | end
68 | }),
69 | awful.key({
70 | modifiers = { modkey, mod.ctrl },
71 | keygroup = 'numrow',
72 | description = 'toggle tag',
73 | group = 'tag',
74 | on_press = function(index)
75 | local tag = awful.screen.focused().tags[index]
76 | if tag then awful.tag.viewtoggle(tag) end
77 | end
78 | }),
79 | awful.key({
80 | modifiers = { modkey, mod.shift },
81 | keygroup = 'numrow',
82 | description = 'move focused client to tag',
83 | group = 'tag',
84 | on_press = function(index)
85 | if client.focus then
86 | local tag = client.focus.screen.tags[index]
87 | if tag then client.focus:move_to_tag(tag) end
88 | end
89 | end
90 | }),
91 | awful.key({
92 | modifiers = { modkey, mod.ctrl, mod.shift },
93 | keygroup = 'numrow',
94 | description = 'toggle focused client on tag',
95 | group = 'tag',
96 | on_press = function(index)
97 | if client.focus then
98 | local tag = client.focus.screen.tags[index]
99 | if tag then client.focus:toggle_tag(tag) end
100 | end
101 | end
102 | }),
103 | awful.key({
104 | modifiers = { modkey, mod.alt },
105 | keygroup = 'numrow',
106 | description = 'select layout directly',
107 | group = 'layout',
108 | on_press = function(index)
109 | local t = awful.screen.focused().selected_tag
110 | if t then
111 | t.layout = t.layouts[index] or t.layout
112 | end
113 | end
114 | }),
115 |
116 | -- Miscelaneous
117 | ---------------
118 | -- Screenshot.
119 | awful.key({ }, 'Print', function() shooter.selection() end,
120 | { description = 'select a region to screenshot', group = 'screenshot' }),
121 | awful.key({ modkey }, 'Print', function() shooter.screen() end,
122 | { description = 'select the whole screen to screenshot', group = 'screenshot' }),
123 | awful.key({ mod.ctrl }, 'Print', function() shooter.delayed(5) end,
124 | { description = 'take a delayed fullscreen screenshot', group = 'screenshot' }),
125 |
126 | -- Audio.
127 | awful.key({ }, 'XF86AudioRaiseVolume', function() audio:default_sink_volume_up(2) end,
128 | { description = 'raises default audio device volume', group = 'audio' }),
129 | awful.key({ }, 'XF86AudioLowerVolume', function() audio:default_sink_volume_down(2) end,
130 | { description = 'lowers default audio device volume', group = 'audio' }),
131 | awful.key({ }, 'XF86AudioMute', function() audio:default_sink_toggle_mute() end,
132 | { description = 'toggles default audio device mute', group = 'audio' }),
133 |
134 | -- Music.
135 | awful.key({ }, 'XF86AudioPlay', function() pctl:play_pause() end,
136 | { description = 'toggles music playback', group = 'music' }),
137 | awful.key({ }, 'XF86AudioPrev', function() pctl:previous() end,
138 | { description = 'rewinds to previous song', group = 'music' }),
139 | awful.key({ }, 'XF86AudioNext', function() pctl:next() end,
140 | { description = 'skips to next song', group = 'music' }),
141 | awful.key({ modkey }, 'o', function() scratch.music:toggle() end,
142 | { description = 'toggles music scratchpad', group = 'music' })
143 | })
144 |
--------------------------------------------------------------------------------
/binds/global/mouse.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 |
5 | local menu = require('ui.menu')
6 |
7 | --- Global mouse bindings
8 | awful.mouse.append_global_mousebindings({
9 | awful.button(nil, 3, function() menu.main:toggle() end),
10 | -- Single most annoying pair of keybinds ever to be seen.
11 | -- awful.button(nil, 4, awful.tag.viewprev),
12 | -- awful.button(nil, 5, awful.tag.viewnext)
13 | })
14 |
--------------------------------------------------------------------------------
/binds/init.lua:
--------------------------------------------------------------------------------
1 | -- Returns all mouse and keybinds for both clients and the WM.
2 | local require = require
3 | return {
4 | global = require(... .. '.global'),
5 | client = require(... .. '.client')
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/binds/mod.lua:
--------------------------------------------------------------------------------
1 | return {
2 | alt = 'Mod1',
3 | super = 'Mod4',
4 | shift = 'Shift',
5 | ctrl = 'Control',
6 |
7 | -- Set Super as default modkey if none is present.
8 | modkey = require('config.user').mod or 'Mod4'
9 | }
10 |
--------------------------------------------------------------------------------
/config/apps.lua:
--------------------------------------------------------------------------------
1 | -- This is used later as the default terminal and editor to run.
2 | local apps = {}
3 |
4 | apps.terminal = 'tym'
5 | apps.terminal_cmd = apps.terminal .. ' -e '
6 |
7 | apps.editor = os.getenv('EDITOR') or 'vim'
8 | apps.editor_cmd = apps.terminal_cmd .. apps.editor
9 |
10 | apps.browser = 'firefox'
11 |
12 | -- Set the terminal for the menubar.
13 | require('menubar').utils.terminal = apps.terminal
14 |
15 | return apps
16 |
--------------------------------------------------------------------------------
/config/auto.lua:
--------------------------------------------------------------------------------
1 | local awful = require('awful')
2 |
3 | -- Checks for currently running programs with the same `Command`. If found does nothing,
4 | -- else runs `program`.
5 | local function spawn_if_not_running(program)
6 | awful.spawn.easy_async_with_shell('pgrep ' .. program, function(output)
7 | if output == nil or output == '' then
8 | print('"' .. program .. '" started with PID: ' .. awful.spawn(program))
9 | else
10 | -- Some programs run several processes, with different PIDs each.
11 | if output:match('\n.+') then
12 | print('"' .. program .. '" already running with PID: '
13 | .. output:gsub('\n', ','):sub(1, -2))
14 | else
15 | print('"' .. program .. '" already running with PID: ' .. output:gsub('\n', ''))
16 | end
17 | end
18 | end)
19 | end
20 |
21 | -- "Daemons", but not really daemonized, just programs running in the bg.
22 | -- spawn_if_not_running('mpd')
23 | -- spawn_if_not_running('mpDris2')
24 | -- spawn_if_not_running('playerctld')
25 | -- spawn_if_not_running('nm-applet')
26 | -- X stuff.
27 | -- awful.spawn.once('setxkbmap -option caps:super us')
28 |
--------------------------------------------------------------------------------
/config/rules.lua:
--------------------------------------------------------------------------------
1 | local require, screen = require, screen
2 |
3 | local awful = require('awful')
4 | local ruled = require('ruled')
5 |
6 | local user = require('config.user')
7 |
8 | --- Rules.
9 | -- Rules to apply to new clients.
10 | ruled.client.connect_signal('request::rules', function()
11 | -- All clients will match this rule.
12 | ruled.client.append_rule({
13 | id = 'global',
14 | rule = nil,
15 | properties = {
16 | focus = awful.client.focus.filter,
17 | raise = true,
18 | screen = awful.screen.preferred,
19 | placement = awful.placement.centered + awful.placement.no_offscreen,
20 | callback = awful.client.setslave,
21 | size_hints_honor = false
22 | }
23 | })
24 |
25 | -- Floating clients.
26 | ruled.client.append_rule({
27 | id = 'floating',
28 | rule_any = {
29 | instance = { 'copyq', 'pinentry' },
30 | class = {
31 | 'Arandr', 'Blueman-manager', 'Gpick', 'Kruler', 'Sxiv',
32 | 'Tor Browser', 'Wpa_gui', 'veromix', 'xtightvncviewer',
33 | 'Nsxiv', 'mpv'
34 | },
35 | -- Note that the name property shown in xprop might be set slightly after
36 | -- creation of the client and the name shown there might not match defined rules
37 | -- here.
38 | name = {
39 | 'Event Tester' -- xev.
40 | },
41 | role = {
42 | 'AlarmWindow', -- Thunderbird's calendar.
43 | 'ConfigManager', -- Thunderbird's about:config.
44 | 'pop-up' -- e.g. Google Chrome's (detached) Developer Tools.
45 | }
46 | },
47 | properties = { floating = true }
48 | })
49 |
50 | -- Add titlebars to normal clients and dialogs.
51 | ruled.client.append_rule({
52 | id = 'titlebars',
53 | rule_any = { type = { 'normal', 'dialog' } },
54 | properties = { titlebars_enabled = true }
55 | })
56 |
57 | -- Prevent certain clients from forcibly claiming focus.
58 | ruled.client.append_rule({
59 | rule_any = { class = { 'firefox', 'steam', 'discord' } },
60 | properties = { focus = false }
61 | })
62 |
63 | -- Map certain clients to certain workspaces.
64 | ruled.client.append_rule({
65 | rule_any = {
66 | class = { 'steamwebhelper', 'steam', 'Heroic' }
67 | },
68 | properties = {
69 | tag = screen[1].tags[user.tags],
70 | floating = true
71 | }
72 | })
73 | ruled.client.append_rule({
74 | rule_any = {
75 | class = { 'discord', 'vesktop' }
76 | },
77 | properties = { tag = screen[1].tags[user.tags - 1] }
78 | })
79 | end)
80 |
81 | -- Floating windows are `always on top` by default. Breaks fullscreen for some reason??
82 | -- client.connect_signal("property::floating", function(c) c.ontop = c.floating end)
83 |
--------------------------------------------------------------------------------
/config/user.lua:
--------------------------------------------------------------------------------
1 | local awful = require('awful')
2 | local dpi = require('beautiful.xresources').apply_dpi
3 |
4 | local HOME = os.getenv('HOME') .. '/'
5 |
6 | -- Specify user preferences for Awesome's behavior.
7 | return {
8 | -- Basics
9 | ---------
10 | -- Default modkey.
11 | -- Usually, Mod4 is the key with a logo between Control and Alt. If you do not like
12 | -- this or do not have such a key, I suggest you to remap Mod4 to another key using
13 | -- xmodmap or other tools. However, you can use another modifier like Mod1, but it
14 | -- may interact with others.
15 | mod = 'Mod4',
16 | -- Each screen has its own tag table. You can just define one and append it to all
17 | -- screens (default behavior).
18 | tags = 7,
19 | -- Table of layouts to cover with awful.layout.inc, ORDER MATTERS, the first layout
20 | -- in the table is your DEFAULT LAYOUT.
21 | layouts = {
22 | awful.layout.suit.tile,
23 | awful.layout.suit.tile.left,
24 | awful.layout.suit.tile.bottom,
25 | -- awful.layout.suit.tile.top,
26 | -- awful.layout.suit.fair,
27 | -- awful.layout.suit.fair.horizontal,
28 | -- awful.layout.suit.spiral,
29 | -- awful.layout.suit.spiral.dwindle,
30 | -- awful.layout.suit.max,
31 | -- awful.layout.suit.max.fullscreen,
32 | -- awful.layout.suit.magnifier,
33 | -- awful.layout.suit.corner.nw,
34 | awful.layout.suit.floating
35 | },
36 |
37 | -- Bling
38 | --------
39 | -- Sizes.
40 | gaps = dpi(2),
41 | tag_padding = dpi(6),
42 |
43 | -- Colors. Available options:
44 | -- lite-xl, mardel, oxocarbon, rose-pine.
45 | colorscheme = 'mardel',
46 | style = 'dark',
47 |
48 | -- Profile Picture.
49 | -- pfp = HOME .. 'Pictures/avatars/misuta-o-saru/gundamZOOM.jpg',
50 | -- pfp = HOME .. 'Pictures/avatars/misuta-o-saru/foxZOOM.jpg',
51 |
52 | -- Wallpaper.
53 | -- wallpaper = HOME .. 'Pictures/walls/flowers/YellowMacro.jpg',
54 | -- wallpaper = HOME .. 'Pictures/walls/abstract/GeometricalShape.jpg',
55 |
56 | -- Screenshots, they're only saved to this path when you select SAVE on the screenshot
57 | -- notification.
58 | screenshot_path = HOME .. 'Pictures/screenshots/',
59 |
60 | -- Power
61 | --------
62 | -- Most systemd distros need not prepend anything to the {poweroff,reboot,suspend}
63 | -- commands, but distros not using systemd must use a polkit or other methods to access
64 | -- these. Some systemd distros also require prepending `systemctl` as user.
65 | shutdown_cmd = 'loginctl poweroff',
66 | reboot_cmd = 'loginctl reboot',
67 | suspend_cmd = 'loginctl suspend',
68 | -- The battery name, actually refers to its path in `/org/freedesktop/UPower/devices/`.
69 | -- Most devices will just have 'BAT0' for the battery.
70 | -- battery_name = 'BAT0',
71 |
72 | -- Avoid loading non-vital assets to save resources.
73 | lite = true,
74 |
75 | -- Weather
76 | ----------
77 | -- area = "New+York",
78 | -- imperial = false
79 | }
80 |
--------------------------------------------------------------------------------
/helpers.lua:
--------------------------------------------------------------------------------
1 | local _H = {}
2 |
3 | -- I feel like YanDev saying "I wish there was a better way to do this"...
4 | -- Gets the suffix for any given day of the month.
5 | function _H.get_suffix(day)
6 | if day > 3 and day < 21 then
7 | return 'th'
8 | end
9 |
10 | if day % 10 == 1 then
11 | return 'st'
12 | elseif day % 10 == 2 then
13 | return 'nd'
14 | elseif day % 10 == 3 then
15 | return 'rd'
16 | else
17 | return 'th'
18 | end
19 | end
20 |
21 | -- Returns true when `e` is an entry in `table`.
22 | function _H.in_table(e, table)
23 | for _, v in table do
24 | if v == e then
25 | return true
26 | end
27 | end
28 | return false
29 | end
30 |
31 | function _H.exists(path)
32 | if path == nil or type(path) ~= 'string' then
33 | return false
34 | end
35 |
36 | return os.rename(path, path)
37 | end
38 |
39 | -- Check whether a file exists.
40 | function _H.file_exists(path)
41 | if not _H.exists(path) then return false end
42 |
43 | local file = io.open(path)
44 | if file then
45 | io.close(file)
46 | return true
47 | end
48 |
49 | return false
50 | end
51 |
52 | -- Check whether a directory exists.
53 | function _H.dir_exists(path)
54 | if path == nil or type(path) ~= 'string' then
55 | return false
56 | end
57 |
58 | if path:sub(-1, -1) ~= '/' then
59 | path = path .. '/'
60 | end
61 | return (_H.exists(path) and not _H.file_exists(path))
62 | end
63 |
64 | return _H
65 |
--------------------------------------------------------------------------------
/module/json.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- json.lua
3 | --
4 | -- Copyright (c) 2020 rxi
5 | --
6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | -- this software and associated documentation files (the "Software"), to deal in
8 | -- the Software without restriction, including without limitation the rights to
9 | -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10 | -- of the Software, and to permit persons to whom the Software is furnished to do
11 | -- so, subject to the following conditions:
12 | --
13 | -- The above copyright notice and this permission notice shall be included in all
14 | -- copies or substantial portions of the Software.
15 | --
16 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | -- SOFTWARE.
23 | --
24 |
25 | local json = { _version = "0.1.2" }
26 |
27 | -------------------------------------------------------------------------------
28 | -- Encode
29 | -------------------------------------------------------------------------------
30 |
31 | local encode
32 |
33 | local escape_char_map = {
34 | ["\\"] = "\\",
35 | ["\""] = "\"",
36 | ["\b"] = "b",
37 | ["\f"] = "f",
38 | ["\n"] = "n",
39 | ["\r"] = "r",
40 | ["\t"] = "t",
41 | }
42 |
43 | local escape_char_map_inv = { ["/"] = "/" }
44 | for k, v in pairs(escape_char_map) do
45 | escape_char_map_inv[v] = k
46 | end
47 |
48 |
49 | local function escape_char(c)
50 | return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
51 | end
52 |
53 |
54 | local function encode_nil(val)
55 | return "null"
56 | end
57 |
58 |
59 | local function encode_table(val, stack)
60 | local res = {}
61 | stack = stack or {}
62 |
63 | -- Circular reference?
64 | if stack[val] then error("circular reference") end
65 |
66 | stack[val] = true
67 |
68 | if rawget(val, 1) ~= nil or next(val) == nil then
69 | -- Treat as array -- check keys are valid and it is not sparse
70 | local n = 0
71 | for k in pairs(val) do
72 | if type(k) ~= "number" then
73 | error("invalid table: mixed or invalid key types")
74 | end
75 | n = n + 1
76 | end
77 | if n ~= #val then
78 | error("invalid table: sparse array")
79 | end
80 | -- Encode
81 | for i, v in ipairs(val) do
82 | table.insert(res, encode(v, stack))
83 | end
84 | stack[val] = nil
85 | return "[" .. table.concat(res, ",") .. "]"
86 | else
87 | -- Treat as an object
88 | for k, v in pairs(val) do
89 | if type(k) ~= "string" then
90 | error("invalid table: mixed or invalid key types")
91 | end
92 | table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
93 | end
94 | stack[val] = nil
95 | return "{" .. table.concat(res, ",") .. "}"
96 | end
97 | end
98 |
99 |
100 | local function encode_string(val)
101 | return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
102 | end
103 |
104 |
105 | local function encode_number(val)
106 | -- Check for NaN, -inf and inf
107 | if val ~= val or val <= -math.huge or val >= math.huge then
108 | error("unexpected number value '" .. tostring(val) .. "'")
109 | end
110 | return string.format("%.14g", val)
111 | end
112 |
113 |
114 | local type_func_map = {
115 | ["nil"] = encode_nil,
116 | ["table"] = encode_table,
117 | ["string"] = encode_string,
118 | ["number"] = encode_number,
119 | ["boolean"] = tostring,
120 | }
121 |
122 |
123 | encode = function(val, stack)
124 | local t = type(val)
125 | local f = type_func_map[t]
126 | if f then
127 | return f(val, stack)
128 | end
129 | error("unexpected type '" .. t .. "'")
130 | end
131 |
132 |
133 | function json.encode(val)
134 | return (encode(val))
135 | end
136 |
137 | -------------------------------------------------------------------------------
138 | -- Decode
139 | -------------------------------------------------------------------------------
140 |
141 | local parse
142 |
143 | local function create_set(...)
144 | local res = {}
145 | for i = 1, select("#", ...) do
146 | res[select(i, ...)] = true
147 | end
148 | return res
149 | end
150 |
151 | local space_chars = create_set(" ", "\t", "\r", "\n")
152 | local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
153 | local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
154 | local literals = create_set("true", "false", "null")
155 |
156 | local literal_map = {
157 | ["true"] = true,
158 | ["false"] = false,
159 | ["null"] = nil,
160 | }
161 |
162 |
163 | local function next_char(str, idx, set, negate)
164 | for i = idx, #str do
165 | if set[str:sub(i, i)] ~= negate then
166 | return i
167 | end
168 | end
169 | return #str + 1
170 | end
171 |
172 |
173 | local function decode_error(str, idx, msg)
174 | local line_count = 1
175 | local col_count = 1
176 | for i = 1, idx - 1 do
177 | col_count = col_count + 1
178 | if str:sub(i, i) == "\n" then
179 | line_count = line_count + 1
180 | col_count = 1
181 | end
182 | end
183 | error(string.format("%s at line %d col %d", msg, line_count, col_count))
184 | end
185 |
186 |
187 | local function codepoint_to_utf8(n)
188 | -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
189 | local f = math.floor
190 | if n <= 0x7f then
191 | return string.char(n)
192 | elseif n <= 0x7ff then
193 | return string.char(f(n / 64) + 192, n % 64 + 128)
194 | elseif n <= 0xffff then
195 | return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
196 | elseif n <= 0x10ffff then
197 | return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
198 | f(n % 4096 / 64) + 128, n % 64 + 128)
199 | end
200 | error(string.format("invalid unicode codepoint '%x'", n))
201 | end
202 |
203 |
204 | local function parse_unicode_escape(s)
205 | local n1 = tonumber(s:sub(1, 4), 16)
206 | local n2 = tonumber(s:sub(7, 10), 16)
207 | -- Surrogate pair?
208 | if n2 then
209 | return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
210 | else
211 | return codepoint_to_utf8(n1)
212 | end
213 | end
214 |
215 |
216 | local function parse_string(str, i)
217 | local res = ""
218 | local j = i + 1
219 | local k = j
220 |
221 | while j <= #str do
222 | local x = str:byte(j)
223 |
224 | if x < 32 then
225 | decode_error(str, j, "control character in string")
226 | elseif x == 92 then -- `\`: Escape
227 | res = res .. str:sub(k, j - 1)
228 | j = j + 1
229 | local c = str:sub(j, j)
230 | if c == "u" then
231 | local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
232 | or str:match("^%x%x%x%x", j + 1)
233 | or decode_error(str, j - 1, "invalid unicode escape in string")
234 | res = res .. parse_unicode_escape(hex)
235 | j = j + #hex
236 | else
237 | if not escape_chars[c] then
238 | decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
239 | end
240 | res = res .. escape_char_map_inv[c]
241 | end
242 | k = j + 1
243 | elseif x == 34 then -- `"`: End of string
244 | res = res .. str:sub(k, j - 1)
245 | return res, j + 1
246 | end
247 |
248 | j = j + 1
249 | end
250 |
251 | decode_error(str, i, "expected closing quote for string")
252 | end
253 |
254 |
255 | local function parse_number(str, i)
256 | local x = next_char(str, i, delim_chars)
257 | local s = str:sub(i, x - 1)
258 | local n = tonumber(s)
259 | if not n then
260 | decode_error(str, i, "invalid number '" .. s .. "'")
261 | end
262 | return n, x
263 | end
264 |
265 |
266 | local function parse_literal(str, i)
267 | local x = next_char(str, i, delim_chars)
268 | local word = str:sub(i, x - 1)
269 | if not literals[word] then
270 | decode_error(str, i, "invalid literal '" .. word .. "'")
271 | end
272 | return literal_map[word], x
273 | end
274 |
275 |
276 | local function parse_array(str, i)
277 | local res = {}
278 | local n = 1
279 | i = i + 1
280 | while 1 do
281 | local x
282 | i = next_char(str, i, space_chars, true)
283 | -- Empty / end of array?
284 | if str:sub(i, i) == "]" then
285 | i = i + 1
286 | break
287 | end
288 | -- Read token
289 | x, i = parse(str, i)
290 | res[n] = x
291 | n = n + 1
292 | -- Next token
293 | i = next_char(str, i, space_chars, true)
294 | local chr = str:sub(i, i)
295 | i = i + 1
296 | if chr == "]" then break end
297 | if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
298 | end
299 | return res, i
300 | end
301 |
302 |
303 | local function parse_object(str, i)
304 | local res = {}
305 | i = i + 1
306 | while 1 do
307 | local key, val
308 | i = next_char(str, i, space_chars, true)
309 | -- Empty / end of object?
310 | if str:sub(i, i) == "}" then
311 | i = i + 1
312 | break
313 | end
314 | -- Read key
315 | if str:sub(i, i) ~= '"' then
316 | decode_error(str, i, "expected string for key")
317 | end
318 | key, i = parse(str, i)
319 | -- Read ':' delimiter
320 | i = next_char(str, i, space_chars, true)
321 | if str:sub(i, i) ~= ":" then
322 | decode_error(str, i, "expected ':' after key")
323 | end
324 | i = next_char(str, i + 1, space_chars, true)
325 | -- Read value
326 | val, i = parse(str, i)
327 | -- Set
328 | res[key] = val
329 | -- Next token
330 | i = next_char(str, i, space_chars, true)
331 | local chr = str:sub(i, i)
332 | i = i + 1
333 | if chr == "}" then break end
334 | if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
335 | end
336 | return res, i
337 | end
338 |
339 |
340 | local char_func_map = {
341 | ['"'] = parse_string,
342 | ["0"] = parse_number,
343 | ["1"] = parse_number,
344 | ["2"] = parse_number,
345 | ["3"] = parse_number,
346 | ["4"] = parse_number,
347 | ["5"] = parse_number,
348 | ["6"] = parse_number,
349 | ["7"] = parse_number,
350 | ["8"] = parse_number,
351 | ["9"] = parse_number,
352 | ["-"] = parse_number,
353 | ["t"] = parse_literal,
354 | ["f"] = parse_literal,
355 | ["n"] = parse_literal,
356 | ["["] = parse_array,
357 | ["{"] = parse_object,
358 | }
359 |
360 |
361 | parse = function(str, idx)
362 | local chr = str:sub(idx, idx)
363 | local f = char_func_map[chr]
364 | if f then
365 | return f(str, idx)
366 | end
367 | decode_error(str, idx, "unexpected character '" .. chr .. "'")
368 | end
369 |
370 |
371 | function json.decode(str)
372 | if type(str) ~= "string" then
373 | error("expected argument of type string, got " .. type(str))
374 | end
375 | local res, idx = parse(str, next_char(str, 1, space_chars, true))
376 | idx = next_char(str, idx, space_chars, true)
377 | if idx <= #str then
378 | decode_error(str, idx, "trailing garbage")
379 | end
380 | return res
381 | end
382 |
383 | return json
384 |
--------------------------------------------------------------------------------
/rc.lua:
--------------------------------------------------------------------------------
1 | -- awesome_mode: api-level=4:screen=on
2 | -- If LuaRocks is installed, make sure that packages installed through it are
3 | -- found (e.g. lgi). If LuaRocks is not installed, do nothing.
4 | pcall(require, 'luarocks.loader')
5 |
6 | -- Allow Awesome to automatically focus a client upon changing tags or loading.
7 | require('awful.autofocus')
8 | -- Enable hotkeys help widget for VIM and other apps when client with a matching
9 | -- name is opened.
10 | require('awful.hotkeys_popup.keys')
11 |
12 | --- Error handling.
13 | -- Notification library.
14 | local naughty = require('naughty')
15 | -- Check if awesome encountered an error during startup and fell back to another config
16 | -- (This code will only ever execute for the fallback config).
17 | naughty.connect_signal('request::display_error', function(message, startup)
18 | naughty.notification({
19 | urgency = 'critical',
20 | title = 'Oops, an error happened' .. (startup and ' during startup!' or '!'),
21 | message = message
22 | })
23 | end)
24 |
25 | -- Create auxiliary directories for user state.
26 | local awful = require('awful')
27 | awful.spawn('mkdir -p ' .. os.getenv('HOME') .. '/.local/data/awesome')
28 |
29 | -- Load the theme. In other words, defines the variables within the `beautiful` table.
30 | require('theme')
31 |
32 | -- Treat all signals. Bear in mind this implies creating all tags, attaching their
33 | -- layouts, setting client behavior and loading UI.
34 | require('signal')
35 |
36 | -- Set all keybinds.
37 | require('binds')
38 |
39 | -- Load all client rules.
40 | require('config.rules')
41 |
42 | -- Run startup commands.
43 | require('config.auto')
44 |
--------------------------------------------------------------------------------
/script/shooter.lua:
--------------------------------------------------------------------------------
1 | -- Screenshot script using maim and xclip. `awful.screenshot` has yet to be
2 | -- reliable enough to be usable.
3 | local require, io, os = require, io, os
4 |
5 | local awful = require('awful')
6 | local beautiful = require('beautiful')
7 | local naughty = require('naughty')
8 |
9 | local user = require('config.user')
10 | local helpers = require('helpers')
11 |
12 | -- The directory where PERMANENT files would be stored.
13 | local perm_dir = user.screenshot_path or os.getenv('HOME')
14 |
15 | local function send_notif(path)
16 | local ok = naughty.action({ name = 'Ok' })
17 | local save = naughty.action({ name = 'Save' })
18 | local discard = naughty.action({ name = 'Discard' })
19 |
20 | save:connect_signal('invoked', function()
21 | if not helpers.dir_exists(perm_dir) then
22 | awful.spawn('mkdir -p ' .. perm_dir)
23 | end
24 | awful.spawn.easy_async_with_shell('cp ' .. path .. ' ' .. perm_dir, function()
25 | naughty.notification({
26 | icon = beautiful.notification_default,
27 | title = 'Screenshot',
28 | message = 'Saved to ' .. perm_dir,
29 | actions = { ok }
30 | })
31 | end)
32 | end)
33 |
34 | discard:connect_signal('invoked', function()
35 | awful.spawn.easy_async_with_shell('rm ' .. path, function()
36 | naughty.notification({
37 | icon = beautiful.notification_cancel,
38 | title = 'Screenshot',
39 | message = 'Temporary file removed!',
40 | actions = { ok }
41 | })
42 | end)
43 | end)
44 |
45 | -- Check whether the screenshot was taken or not.
46 | local file = io.open(path)
47 | if file ~= nil then
48 | -- If it exists:
49 | io.close(file)
50 | naughty.notification({
51 | icon = path,
52 | title = 'Screenshot',
53 | message = 'Copied to clipboard!',
54 | actions = { save, discard }
55 | })
56 | else
57 | -- If it doesn't:
58 | naughty.notification({
59 | icon = beautiful.notification_cancel,
60 | title = 'Screenshot',
61 | message = 'Cancelled!',
62 | actions = { ok }
63 | })
64 | end
65 | end
66 |
67 | -- Takes a screenshot and puts it in `/tmp`, then copies it to system clipboard
68 | -- and notifies about the result.
69 | local function take_screenshot(cmd)
70 | local tmp = '/tmp/ss-' .. os.date('%Y%m%d-%H%M%S') .. '.png'
71 | awful.spawn.easy_async_with_shell(cmd .. ' ' .. tmp, function()
72 | awful.spawn.with_shell('xclip -selection clip -t image/png -i ' .. tmp)
73 | send_notif(tmp)
74 | end)
75 | end
76 |
77 | return {
78 | screen = function() take_screenshot('maim') end,
79 | selection = function() take_screenshot('maim -s') end,
80 | delayed = function(s) take_screenshot('sleep ' .. s .. '; maim') end
81 | }
82 |
--------------------------------------------------------------------------------
/script/tym-themer.lua:
--------------------------------------------------------------------------------
1 | -- Writes a tym colorcheme using current colorscheme.
2 |
3 | local c = require(require('beautiful').colorscheme)
4 |
5 | local theme = string.format([[
6 | return {
7 | color_window_background = "%s",
8 | color_cursor_foreground = "%s",
9 | color_background = "%s",
10 | color_0 = "%s",
11 | color_foreground = "%s",
12 | color_7 = "%s",
13 | color_bold = "%s",
14 | color_8 = "%s",
15 | color_15 = "%s",
16 | color_cursor = "%s",
17 | color_9 = "%s",
18 | color_1 = "%s",
19 | color_10 = "%s",
20 | color_2 = "%s",
21 | color_11 = "%s",
22 | color_3 = "%s",
23 | color_12 = "%s",
24 | color_4 = "%s",
25 | color_13 = "%s",
26 | color_5 = "%s",
27 | color_14 = "%s",
28 | color_6 = "%s"
29 | }
30 | ]],
31 | c.bg0, c.bg0, c.bg0, c.bg0, c.fg0, c.fg2, c.fg0, c.bg2, c.fg0, c.fg0,
32 | c.red, c.red, c.green, c.green, c.blue, c.blue, c.yellow, c.yellow,
33 | c.magenta, c.magenta, c.cyan, c.cyan
34 | )
35 |
36 | return function()
37 | local file = io.open(os.getenv('HOME') .. '/.config/tym/theme.lua', 'w+')
38 | if file == nil then return end
39 | file:write(theme)
40 | file:close()
41 | end
42 |
--------------------------------------------------------------------------------
/script/xresources.lua:
--------------------------------------------------------------------------------
1 | -- Writes Xresources using current colorscheme and a few other things, does not override
2 | -- your `~/.Xresources` file, instead, it creates a `~/.local/share/gwileful/xresources`
3 | -- file which is merged with your present configuration on AwesomeWM startup.
4 | local require, io = require, io
5 |
6 | local awful = require('awful')
7 | local beautiful = require('beautiful')
8 |
9 | local color = require(beautiful.colorscheme)
10 | local path = beautiful.data_dir .. 'xresources'
11 |
12 | local theme = string.format([[
13 | Nsxiv.window.background: %s
14 | Nsxiv.window.foreground: %s
15 | Nsxiv.bar.background: %s
16 | Nsxiv.bar.foreground: %s
17 | Nsxiv.mark.foreground: %s
18 | Nsxiv.bar.font: %s
19 | ]],
20 | color.bg0, color.accent, color.bg1, color.fg0, color.bccent,
21 | (beautiful.font_bitm .. beautiful.bitm_size):gsub(' ', '-')
22 | )
23 |
24 | return function()
25 | local file = io.open(path, 'w')
26 | -- Create file if it doesn't exist.
27 | if file == nil then
28 | awful.spawn.with_shell('mkdir -p ' .. beautiful.data_dir .. '; touch ' .. path)
29 | file = io.open(path, 'w')
30 | end
31 | -- If it was still somehow impossible to access the file, stop.
32 | if file == nil then return end
33 |
34 | -- Update Xresources.
35 | file:write(theme)
36 | file:close()
37 | awful.spawn('xrdb -override ' .. path)
38 | end
39 |
--------------------------------------------------------------------------------
/signal/client.lua:
--------------------------------------------------------------------------------
1 | local client = client
2 |
3 | -- Add a titlebar if titlebars_enabled is set to true for the client in `config/rules.lua`.
4 | client.connect_signal('request::titlebars', function(c)
5 | -- Some clients don't actually want to have a titlebar.
6 | if c.requests_no_titlebar then return end
7 |
8 | require('ui.titlebar').normal(c)
9 | end)
10 |
11 | -- Enable sloppy focus, so that focus follows mouse.
12 | -- client.connect_signal('mouse::enter', function(c)
13 | -- c:activate({ context = 'mouse_enter', raise = false })
14 | -- end)
15 |
--------------------------------------------------------------------------------
/signal/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | -- Allows all signals to be connected and/or emitted.
4 | return {
5 | client = require(... .. '.client'),
6 | -- NOTE: The `tag` file must be loaded before the `screen` one so that
7 | -- the correct layouts defined in `config.user` are appended to the tags
8 | -- upon creation.
9 | tag = require(... .. '.tag'),
10 | screen = require(... .. '.screen'),
11 | naughty = require(... .. '.naughty')
12 | }
13 |
--------------------------------------------------------------------------------
/signal/naughty.lua:
--------------------------------------------------------------------------------
1 | --- Notifications
2 | local require = require
3 |
4 | -- Something particularly odd about naughty is that you must set position and screen
5 | -- setting through ruled, since trying to do that on the `naughty.notification.layout_box`
6 | -- directly will result in some really buggy behavior. Even more weirdly, notification
7 | -- margins must be set through a `beautiful` variable (in `theme/theme.lua`).
8 | local ruled = require('ruled')
9 | ruled.notification.connect_signal('request::rules', function()
10 | ruled.notification.append_rule({
11 | rule = {},
12 | properties = {
13 | position = 'bottom_right'
14 | }
15 | })
16 | end)
17 |
18 | -- Defines the default notification layout. Given I have OSDs now, special notifications
19 | -- won't be a necessity.
20 | require('naughty').connect_signal('request::display', function(n)
21 | require('ui.notification')(n)
22 | end)
23 |
--------------------------------------------------------------------------------
/signal/screen.lua:
--------------------------------------------------------------------------------
1 | local require, screen, table = require, screen, table
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local user = require('config.user')
10 |
11 | --- Attach tags and require('ui') to all screens.
12 | screen.connect_signal('request::desktop_decoration', function(s)
13 | -- Create all tags and attach the layouts to each of them.
14 | local tags = {}
15 | for i = 1, user.tags - 1, 1 do
16 | table.insert(tags, i)
17 | end
18 | awful.tag(tags, s, awful.layout.layouts[1])
19 | awful.tag.add(user.tags, { screen = s, layout = awful.layout.suit.floating })
20 |
21 | -- Add padding to the screens themselves.
22 | s.padding = dpi(user.tag_padding)
23 |
24 | -- Attach a bar to each screen.
25 | s.bar = require('ui.wibar')(s)
26 | s.launcher = require('ui.launcher')(s)
27 |
28 | -- And some other widgets only to the `main` screen.
29 | if s == awful.screen.focused() then
30 | s.dash = require('ui.dash')(s)
31 | s.time = require('ui.time')(s)
32 | s.osds = require('ui.osd')(s)
33 | end
34 | end)
35 |
36 | --- Wallpaper.
37 | if not beautiful.solid_wallpaper then
38 | -- https://awesomewm.org/apidoc/utility_libraries/gears.wallpaper.html
39 | require('gears').wallpaper.maximized(beautiful.wallpaper)
40 | else
41 | screen.connect_signal('request::wallpaper', function(s)
42 | awful.wallpaper({
43 | screen = s,
44 | widget = {
45 | widget = wibox.container.background,
46 | bg = beautiful.wallpaper
47 | }
48 | })
49 | end)
50 | end
51 |
--------------------------------------------------------------------------------
/signal/system/battery.lua:
--------------------------------------------------------------------------------
1 | -- See: https://lazka.github.io/pgi-docs/UPowerGlib-1.0/classes/Device.html
2 | local require, string, ipairs = require, string, ipairs
3 |
4 | local gears = require('gears')
5 |
6 | local upower = require('lgi').require('UPowerGlib')
7 | local client = upower.Client()
8 | local user = require('config.user')
9 |
10 | local instance = nil
11 |
12 | local battery = {}
13 |
14 | -- Default to BAT0 if the user doesn't set it, in which case the user may not actually
15 | -- have a battery, which won't get past the for loop later on, which checks that
16 | -- `device_path` matches a present battery before attempting to get data from it.
17 | battery.device_path = '/org/freedesktop/UPower/devices/battery_' .. (user.battery_name or 'BAT0')
18 | -- The only relevant ones for this use case are the first five.
19 | battery.device_state = {
20 | 'UNKNOWN', 'CHARGING', 'DISCHARGING', 'EMPTY', 'FULLY_CHARGED',
21 | 'PENDING_CHARGE', 'PENDING_DISCHARGE', 'LAST'
22 | }
23 | -- For some reason, this enum contains some unused values, they were set to nil here.
24 | battery.device_level = {
25 | 'UNKNOWN', 'NONE', nil, 'LOW', 'CRITICAL', nil, 'NORMAL', 'HIGH', 'FULL', nil
26 | }
27 |
28 | local function new()
29 | local self = gears.object({})
30 | gears.table.crush(self, battery, true)
31 |
32 | local devices = client:get_devices()
33 | if devices ~= nil then
34 | -- Check the device the user wants tracked is actually present.
35 | local dev_found = false
36 | for _, dev in ipairs(devices) do
37 | if dev:get_object_path() == self.device_path then
38 | dev_found = true
39 | break
40 | end
41 | end
42 | if dev_found then
43 | -- Given the signal is only emitted if a valid device is found, widgets can be made
44 | -- to simply start off with `visible = false` and have that changed to `true` upon
45 | -- connecting this signal.
46 | require('module.awesome-battery_widget')({
47 | device_path = self.device_path,
48 | instant_update = true
49 | }):connect_signal('upower::update', function(_, device)
50 | self:emit_signal('update',
51 | tonumber(string.format('%.0f', device.percentage)),
52 | self.device_state[device.state + 1],
53 | self.device_level[device.battery_level + 1],
54 | tonumber(string.format('%.0f', device.time_to_empty / 60)),
55 | tonumber(string.format('%.0f', device.time_to_full / 60))
56 | )
57 | end)
58 | end
59 | end
60 |
61 | return self
62 | end
63 |
64 | if not instance then
65 | instance = new()
66 | end
67 |
68 | return instance
69 |
--------------------------------------------------------------------------------
/signal/system/network.lua:
--------------------------------------------------------------------------------
1 | local require, string, table, ipairs, print = require, string, table, ipairs, print
2 |
3 | local gears = require('gears')
4 |
5 | local lgi = require('lgi')
6 | local dbus = require('module.dbus_proxy')
7 |
8 | local instance = nil
9 | local net = {}
10 |
11 | local _NM_status, NM = pcall(function()
12 | return lgi.NM
13 | end)
14 |
15 | if not _NM_status or not NM then
16 | gears.debug.print_warning('Failed to connect to NM interface.')
17 | return
18 | end
19 |
20 | local DEV_TYPE = { WIRED = 1, WIRELESS = 2 }
21 |
22 | local function flags_to_string(flags, wpa_flags, rsn_flags)
23 | local str = ''
24 |
25 | if flags == 1 and wpa_flags == 0 and rsn_flags == 0 then
26 | str = str .. ' WEP'
27 | end
28 | if wpa_flags ~= 0 then
29 | str = str .. ' WPA1'
30 | end
31 | if rsn_flags == 0 then
32 | str = str .. ' WPA2'
33 | end
34 | if wpa_flags == 512 or rsn_flags == 512 then
35 | str = str .. ' 802.1X'
36 | end
37 |
38 | return str
39 | end
40 |
41 | local function get_ap_conns(self, ssid)
42 | local conns = {}
43 |
44 | for _, conn_path in ipairs(self._private.settings:ListConnections()) do
45 | local conn = dbus.Proxy:new({
46 | bus = dbus.Bus.SYSTEM,
47 | name = 'org.freedesktop.NetworkManager',
48 | interface = 'org.freedesktop.NetworkManager.Settings.Connection',
49 | path = conn_path
50 | })
51 |
52 | if string.find(conn.Filename, ssid) then
53 | table.insert(conns, conn)
54 | end
55 | end
56 |
57 | return conns
58 | end
59 |
60 | local function get_dev_proxy(self)
61 | local devs = self._private.client:GetDevices()
62 | for _, dev_path in ipairs(devs) do
63 | local dev = dbus.Proxy:new({
64 | bus = dbus.Bus.SYSTEM,
65 | name = 'org.freedesktop.NetworkManager',
66 | interface = 'org.freedesktop.NetworkManager.Device',
67 | path = dev_path
68 | })
69 |
70 | if dev.DeviceType == DEV_TYPE.WIRELESS then
71 | self._private.dev = dev
72 | self._private.wifi = dbus.Proxy:new({
73 | bus = dbus.Bus.SYSTEM,
74 | name = 'org.freedesktop.NetworkManager',
75 | interface = 'org.freedesktop.NetworkManager.Device.Wireless',
76 | path = dev_path
77 | })
78 | self._private.dev:connect_signal('StateChanged', function(proxy, new, old, reason)
79 | -- idk
80 | end)
81 | elseif dev.DeviceType == DEV_TYPE.WIRED then
82 | self._private.dev = dev
83 | self._private.wired = dbus.Proxy:new({
84 | bus = dbus.Bus.SYSTEM,
85 | name = 'org.freedesktop.NetworkManager',
86 | interface = 'org.freedesktop.NetworkManager.Device.Ethernet',
87 | path = dev_path
88 | })
89 | self._private.dev:connect_signal('StateChanged', function(proxy, new, old, reason)
90 | -- idk
91 | end)
92 | end
93 | end
94 | end
95 |
96 | -- O(n*log(n))
97 | -- **Considering the amount of connections existing to an access point negligible in
98 | -- comparison to the amount of present access points.
99 | -- ***Lua `table.sort` is a C programmed quicksort implementation.
100 | function net:scan_aps()
101 | if self._private.wifi == nil then return end
102 |
103 | -- Reset access point list.
104 | self._private.aps = {}
105 | self._private.wifi:RequestScanAsync(function(_, _, _, failure)
106 | if failure ~= nil then
107 | gears.debug.print_warning('Failed to scan for access points.')
108 | self:emit_signal('wireless::scan::failed', tostring(failure.code))
109 | return
110 | end
111 |
112 | -- Scan all access points.
113 | local aps = self._private.wifi:GetAccessPoints()
114 | for _, ap_path in ipairs(aps) do
115 | -- Create a dbus proxy for the access point.
116 | local ap = dbus.Proxy:new({
117 | bus = dbus.Bus.SYSTEM,
118 | name = 'org.freedesktop.NetworkManager',
119 | interface = 'org.freedesktop.NetworkManager.AccessPoint',
120 | path = ap_path
121 | })
122 |
123 | if ap.Ssid ~= nil then
124 | -- Obtain its info.
125 | local ssid = NM.utils_ssid_to_utf8(ap.Ssid)
126 | local security = flags_to_string(ap.Flags, ap.WpaFlags, ap.RsnFlags)
127 | local password = ''
128 | local conns = get_ap_conns(self, ssid)
129 |
130 | -- If it has a saved connection, get its password.
131 | for _, conn in ipairs(conns) do
132 | if string.find(conn.Filename, ssid) then
133 | local secrets = conn:GetSecrets('802-11-wireless-security')
134 | if secrets ~= nil then
135 | password = secrets['802-11-wireless-security'].psk
136 | break
137 | end
138 | end
139 | end
140 |
141 | -- Add the access point info to the access point table.
142 | local ret = {
143 | raw_ssid = ap.Ssid,
144 | ssid = ssid,
145 | security = security,
146 | password = password,
147 | strength = ap.Strength,
148 | path = ap_path,
149 | dev_if = self._private.dev.Interface,
150 | dev_path = self._private.dev.object_path,
151 | hw_address = ap.HwAddress
152 | }
153 | gears.table.crush(ret, ap, true)
154 |
155 | print('Adding AP: ' .. ssid)
156 | table.insert(self._private.aps, ret)
157 | end
158 | end
159 |
160 | -- Sort the access point based on their signal strength.
161 | table.sort(self._private.aps, function(a, b)
162 | return a.strength > b.strength
163 | end)
164 |
165 | -- Return table of access points.
166 | print('Access point scan successful.')
167 | self:emit_signal('wireless::scan::success', self._private.aps)
168 | end)
169 | end
170 |
171 | function net:set_networking(state)
172 | self._private.client:Enable(state)
173 | end
174 |
175 | function net:get_aps()
176 | return self._private.aps
177 | end
178 |
179 |
180 | local function new()
181 | local self = gears.object({})
182 | gears.table.crush(self, net, true)
183 |
184 | self._private = {}
185 | self._private.aps = {}
186 |
187 | self._private.client = dbus.Proxy:new({
188 | bus = dbus.Bus.SYSTEM,
189 | name = 'org.freedesktop.NetworkManager',
190 | interface = 'org.freedesktop.NetworkManager',
191 | path = '/org/freedesktop/NetworkManager'
192 | })
193 |
194 | self._private.settings = dbus.Proxy:new({
195 | bus = dbus.Bus.SYSTEM,
196 | name = 'org.freedesktop.NetworkManager',
197 | interface = 'org.freedesktop.NetworkManager.Settings',
198 | path = '/org/freedesktop/NetworkManager/Settings'
199 | })
200 |
201 | local client_props = dbus.Proxy:new({
202 | bus = dbus.Bus.SYSTEM,
203 | name = 'org.freedesktop.NetworkManager',
204 | interface = 'org.freedesktop.Dbus.Properties',
205 | path = '/org/freedesktop/NetworkManager'
206 | })
207 |
208 | local ap_scan_timer = gears.timer({
209 | timeout = 5,
210 | callback = function()
211 | print('Attempting AP scan.')
212 | self:scan_aps()
213 | return false
214 | end
215 | })
216 |
217 | client_props:connect_signal('PropertiesChanged', function(_, _, data)
218 | if not data.NetworkingEnabled then return end
219 |
220 | if (data.WirelessEnabled ~= nil and
221 | data.WirelessEnabled ~= self._private.wireless_enabled) then
222 | self._private.wireless_enabled = data.WirelessEnabled
223 | self:emit_signal('wireless::state', data.WirelessEnabled)
224 |
225 | if data.WirelessEnabled then
226 | ap_scan_timer:start()
227 | else
228 | ap_scan_timer:stop()
229 | end
230 | end
231 | end)
232 |
233 | get_dev_proxy()
234 |
235 | return self
236 | end
237 |
238 | if not instance then
239 | instance = new()
240 | end
241 |
242 | return instance
243 |
--------------------------------------------------------------------------------
/signal/system/playerctl.lua:
--------------------------------------------------------------------------------
1 | local instance = nil
2 |
3 | local function new()
4 | return require('module.bling').signal.playerctl.lib({
5 | player = { 'mpd', '%any' },
6 | ignore = { 'firefox' },
7 | update_on_activity = true,
8 | interval = 1,
9 | debounce_delay = 0.35
10 | })
11 | end
12 |
13 | if not instance then
14 | instance = new()
15 | end
16 |
17 | return instance
18 |
--------------------------------------------------------------------------------
/signal/system/weather.lua:
--------------------------------------------------------------------------------
1 | -- The original version of this script was inspired by ChadCat's, see:
2 | -- https://github.com/chadcat7/crystal/blob/the-awesome-config/signals/stat/weather.lua
3 |
4 | -- This version improves upon it by adding timeouts and other measures of control, to
5 | -- ensure that weather information is obtained or fails cleanly.
6 | local require, string, math, table = require, string, math, table
7 |
8 | local awful = require('awful')
9 | local gears = require('gears')
10 |
11 | local json = require('module.json')
12 | local user = require('config.user')
13 |
14 | local instance = nil
15 |
16 | -- Allow custom locations and units. If not set, use defaults!
17 | local args = { area = user.area, imperial = user.imperial }
18 | local conf = gears.table.crush({
19 | area = '',
20 | imperial = false
21 | }, args, true)
22 |
23 | if conf.area == nil then
24 | gears.debug.print_warning('No location provided, using wttr.in default (IP-based)!')
25 | end
26 |
27 | -- The information source.
28 | local command = [[bash -c "curl -s --show-error -X GET '%s'"]]
29 | local url =
30 | 'wttr.in/' .. conf.area .. '?format=j1'
31 | local shell_cmd = string.format(command, url)
32 |
33 | -- Customizable values.
34 | local weather = {
35 | poll_wait = 720,
36 | timeout = 15,
37 | max_retries = 7
38 | }
39 |
40 | -- O our Father who art in heaven...
41 | local icon_map = {
42 | -- Daytime.
43 | ['113d'] = 'day_clear',
44 | ['116d'] = 'day_partly_cloudy',
45 | ['119d'] = 'day_cloudy',
46 | ['122d'] = 'day_cloudy',
47 | ['143d'] = 'day_fog',
48 | ['176d'] = 'day_light_rain',
49 | ['179d'] = 'day_light_rain',
50 | ['182d'] = 'day_light_rain',
51 | ['185d'] = 'day_light_rain',
52 | ['200d'] = 'day_storm',
53 | ['227d'] = 'day_snow',
54 | ['230d'] = 'day_snow',
55 | ['248d'] = 'day_fog',
56 | ['260d'] = 'day_fog',
57 | ['263d'] = 'day_light_rain',
58 | ['266d'] = 'day_light_rain',
59 | ['281d'] = 'day_light_rain',
60 | ['284d'] = 'day_light_rain',
61 | ['293d'] = 'day_light_rain',
62 | ['296d'] = 'day_light_rain',
63 | ['299d'] = 'day_rain',
64 | ['302d'] = 'day_rain',
65 | ['305d'] = 'day_rain',
66 | ['308d'] = 'day_rain',
67 | ['311d'] = 'day_light_rain',
68 | ['314d'] = 'day_light_rain',
69 | ['317d'] = 'day_light_rain',
70 | ['320d'] = 'day_snow',
71 | ['323d'] = 'day_snow',
72 | ['326d'] = 'day_snow',
73 | ['329d'] = 'day_snow',
74 | ['332d'] = 'day_snow',
75 | ['335d'] = 'day_snow',
76 | ['338d'] = 'day_snow',
77 | ['350d'] = 'day_light_rain',
78 | ['353d'] = 'day_light_rain',
79 | ['356d'] = 'day_rain',
80 | ['359d'] = 'day_rain',
81 | ['362d'] = 'day_light_rain',
82 | ['365d'] = 'day_light_rain',
83 | ['368d'] = 'day_snow',
84 | ['371d'] = 'day_snow',
85 | ['374d'] = 'day_light_rain',
86 | ['377d'] = 'day_light_rain',
87 | ['386d'] = 'day_storm',
88 | ['389d'] = 'day_storm',
89 | ['392d'] = 'day_snow',
90 | ['395d'] = 'day_snow',
91 | -- Nighttime.
92 | ['113n'] = 'night_clear',
93 | ['116n'] = 'night_partly_cloudy',
94 | ['119n'] = 'night_cloudy',
95 | ['122n'] = 'night_cloudy',
96 | ['143n'] = 'night_fog',
97 | ['176n'] = 'night_light_rain',
98 | ['179n'] = 'night_light_rain',
99 | ['182n'] = 'night_light_rain',
100 | ['185n'] = 'night_light_rain',
101 | ['200n'] = 'night_storm',
102 | ['227n'] = 'night_snow',
103 | ['230n'] = 'night_snow',
104 | ['248n'] = 'night_fog',
105 | ['260n'] = 'night_fog',
106 | ['263n'] = 'night_light_rain',
107 | ['266n'] = 'night_light_rain',
108 | ['281n'] = 'night_light_rain',
109 | ['284n'] = 'night_light_rain',
110 | ['293n'] = 'night_light_rain',
111 | ['296n'] = 'night_light_rain',
112 | ['299n'] = 'night_rain',
113 | ['302n'] = 'night_rain',
114 | ['305n'] = 'night_rain',
115 | ['308n'] = 'night_rain',
116 | ['311n'] = 'night_light_rain',
117 | ['314n'] = 'night_light_rain',
118 | ['317n'] = 'night_light_rain',
119 | ['320n'] = 'night_snow',
120 | ['323n'] = 'night_snow',
121 | ['326n'] = 'night_snow',
122 | ['329n'] = 'night_snow',
123 | ['332n'] = 'night_snow',
124 | ['335n'] = 'night_snow',
125 | ['338n'] = 'night_snow',
126 | ['350n'] = 'night_light_rain',
127 | ['353n'] = 'night_light_rain',
128 | ['356n'] = 'night_rain',
129 | ['359n'] = 'night_rain',
130 | ['362n'] = 'night_light_rain',
131 | ['365n'] = 'night_light_rain',
132 | ['368n'] = 'night_snow',
133 | ['371n'] = 'night_snow',
134 | ['374n'] = 'night_light_rain',
135 | ['377n'] = 'night_light_rain',
136 | ['386n'] = 'night_storm',
137 | ['389n'] = 'night_storm',
138 | ['392n'] = 'night_snow',
139 | ['395n'] = 'night_snow',
140 | }
141 | -- Amen.
142 |
143 | local function suffix(time)
144 | time = tonumber(time)
145 | return (time > 19 or time < 6) and 'n' or 'd'
146 | end
147 |
148 | local function hhmm_to_hh(time)
149 | return string.format('%02d', math.floor(tonumber(time) / 100))
150 | end
151 |
152 | -- snow > storm > rain > fog > light_rain > cloudy > partly_cloudy > clear
153 | local weight_map = {
154 | ['day_snow'] = 7,
155 | ['night_snow'] = 7,
156 | ['day_storm'] = 6,
157 | ['night_storm'] = 6,
158 | ['day_rain'] = 5,
159 | ['night_rain'] = 5,
160 | ['day_fog'] = 4,
161 | ['night_fog'] = 4,
162 | ['day_light_rain'] = 3,
163 | ['night_light_rain'] = 3,
164 | ['day_cloudy'] = 2,
165 | ['night_cloudy'] = 2,
166 | ['day_partly_cloudy'] = 1,
167 | ['night_partly_cloudy'] = 1,
168 | ['day_clear'] = 0,
169 | ['night_clear'] = 0
170 | }
171 |
172 | local function new()
173 | local self = gears.object({})
174 | gears.table.crush(self, weather, true)
175 | local retries = 0
176 | local data = {}
177 |
178 | -- This timer is a fetch attempt timeout. If it fails to get the weather info, it will
179 | -- wait for `weather.timeout` seconds and try again, up to `weather.max_retries`
180 | -- retries. After that it will give up and wait for the next emision of the `retry`
181 | -- signal. If information is obtained successfully, it's emitted through an object
182 | -- signal.
183 | self.timer = gears.timer({
184 | timeout = self.timeout,
185 | call_now = true,
186 | single_shot = true,
187 | callback = function()
188 | awful.spawn.easy_async_with_shell(shell_cmd, function(out)
189 | if out == nil or out == '' then
190 | if retries < self.max_retries then
191 | print("Weather fetching failed, retrying...")
192 | retries = retries + 1
193 | self.timer:start()
194 | else
195 | retries = 0
196 | end
197 | else
198 | self:emit_signal('get', json.decode(out))
199 | end
200 | end)
201 | end
202 | })
203 |
204 | -- In charge of starting an attempt to fetch weather info.
205 | self:connect_signal('retry', function()
206 | self.timer:start()
207 | end)
208 |
209 | -- Emits weather info outward.
210 | self:connect_signal('get', function(_, res)
211 | if res.current_condition[1] == nil then
212 | gears.debug.print_warning('Failed to fetch weather info!')
213 | return
214 | end
215 |
216 | -- Right now!
217 | local current = res.current_condition[1]
218 | local today = res.weather[1]
219 | data.description = current.weatherDesc[1].value
220 | data.humidity = current.humidity .. '%'
221 | data.temperature = conf.imperial and current.temp_F .. '°F'
222 | or current.temp_C .. '°C'
223 | data.feels_like = conf.imperial and current.FeelsLikeF .. '°F'
224 | or current.FeelsLikeC .. '°C'
225 | data.icon = icon_map[current.weatherCode .. suffix(os.date('%H'))]
226 | data.max_temp = conf.imperial and today.maxtempF .. '°F'
227 | or today.maxtempC .. '°C'
228 | data.min_temp = conf.imperial and today.mintempF .. '°F'
229 | or today.mintempC .. '°C'
230 |
231 | -- The next 8 hours.
232 | data.by_hour = {}
233 | for i = 1, 8, 1 do
234 | local fc = today.hourly[i]
235 | local hour = {
236 | time = hhmm_to_hh(fc.time) .. ':00',
237 | description = fc.weatherDesc[1].value,
238 | humidity = fc.humidity .. '%',
239 | temperature = conf.imperial and fc.tempF .. '°F'
240 | or fc.tempC .. '°C',
241 | rain_chance = fc.chanceofrain .. '%',
242 | icon = icon_map[fc.weatherCode .. suffix(hhmm_to_hh(fc.time))]
243 | }
244 | table.insert(data.by_hour, hour)
245 | end
246 |
247 | -- The next 2 days.
248 | data.by_day = {}
249 | for i = 2, 3, 1 do
250 | local fc = res.weather[i]
251 | local day = {}
252 | day.max_temp = conf.imperial and fc.maxtempF .. '°F'
253 | or fc.maxtempC .. '°C'
254 | day.min_temp = conf.imperial and fc.mintempF .. '°F'
255 | or fc.mintempC .. '°C'
256 |
257 | -- NOTE: none of these are actually given. Must be calculated from hourly
258 | -- forecast.
259 | local worst = -1
260 | local icon = icon_map['113d']
261 | local rain = 0
262 | local hum = 0
263 | for j = 1, #fc.hourly, 1 do
264 | -- Get icon. wttr.in does not provide an icon for a day's general weather, so
265 | -- we get that from the "worst" weather in the day.
266 | local hour = fc.hourly[j]
267 | local status = icon_map[hour.weatherCode .. suffix(hhmm_to_hh(hour.time))]
268 | if weight_map[status] > worst then
269 | worst = weight_map[status]
270 | icon = status
271 | end
272 | -- Get chance of rain as the worst chance of rain in the day.
273 | local chance = tonumber(hour.chanceofrain)
274 | if chance > rain then
275 | rain = chance
276 | end
277 | -- Get humidity as the mean humidity of the day.
278 | hum = hum + tonumber(hour.humidity)
279 | end
280 | day.icon = icon
281 | day.rain_chance = rain .. '%'
282 | day.humidity = math.floor(hum / #fc.hourly) .. '%'
283 |
284 | table.insert(data.by_day, day)
285 | end
286 |
287 | self:emit_signal('weather::data', data)
288 | retries = 0
289 | end)
290 |
291 | -- This timer runs `try` every `weather.poll_wait` seconds. It's running at all times
292 | -- and ensures the weather info gets updated.
293 | gears.timer({
294 | timeout = self.poll_wait,
295 | autostart = true,
296 | callback = function() self:emit_signal('retry') end
297 | })
298 |
299 | -- Same code as ran in the retry timer, meant for widgets to call directly.
300 | function self:request_data()
301 | if data.description ~= nil then
302 | self:emit_signal('weather::data', data)
303 | return
304 | end
305 | if not self.timer.started then
306 | awful.spawn.easy_async_with_shell(shell_cmd, function(out)
307 | -- Test and test and try...
308 | if not self.timer.started then
309 | if out == nil or out == '' then
310 | if retries < self.max_retries then
311 | retries = retries + 1
312 | self.timer:start()
313 | else
314 | retries = 0
315 | end
316 | else
317 | self:emit_signal('get', json.decode(out))
318 | end
319 | end
320 | end)
321 | end
322 | end
323 |
324 | return self
325 | end
326 |
327 | if not instance then
328 | instance = new()
329 | end
330 | return instance
331 |
--------------------------------------------------------------------------------
/signal/tag.lua:
--------------------------------------------------------------------------------
1 | --- Tag layouts.
2 | local tag = tag
3 |
4 | -- Appends all layouts defined in `config/user.lua` to all tags.
5 | tag.connect_signal('request::default_layouts', function()
6 | require('awful').layout.append_default_layouts(require('config.user').layouts)
7 | end)
8 |
--------------------------------------------------------------------------------
/theme/assets/default/pfp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gwynsav/gwileful/a9bde5b98b9055fa432f74283a1ca9a33ae11b71/theme/assets/default/pfp.png
--------------------------------------------------------------------------------
/theme/assets/fonts/gwnce.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gwynsav/gwileful/a9bde5b98b9055fa432f74283a1ca9a33ae11b71/theme/assets/fonts/gwnce.ttf
--------------------------------------------------------------------------------
/theme/assets/notif/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gwynsav/gwileful/a9bde5b98b9055fa432f74283a1ca9a33ae11b71/theme/assets/notif/cancel.png
--------------------------------------------------------------------------------
/theme/assets/notif/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gwynsav/gwileful/a9bde5b98b9055fa432f74283a1ca9a33ae11b71/theme/assets/notif/default.png
--------------------------------------------------------------------------------
/theme/assets/util/awesome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gwynsav/gwileful/a9bde5b98b9055fa432f74283a1ca9a33ae11b71/theme/assets/util/awesome.png
--------------------------------------------------------------------------------
/theme/color/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local user = require('config.user')
4 |
5 | local colorscheme = user.colorscheme or 'rose-pine'
6 | local style = user.style or 'dark'
7 |
8 | local path = 'theme.color.' .. colorscheme .. '.' .. style
9 | local palette = require(path)
10 | palette.transparent = '#00000000'
11 |
12 | return {
13 | palette = palette,
14 | path = path
15 | }
16 |
--------------------------------------------------------------------------------
/theme/color/lite-xl/dark/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#252529'
4 | _C.bg1 = '#2e2e32'
5 | _C.bg2 = '#343434'
6 | _C.bg3 = '#414146'
7 | _C.bg4 = '#525259'
8 | _C.fg2 = '#83838f'
9 | _C.fg1 = '#97979c'
10 | _C.fg0 = '#e1e1e6'
11 |
12 | _C.red = '#f77483'
13 | _C.green = '#72b886'
14 | _C.yellow = '#f7c95c'
15 | _C.blue = '#1c7c9c'
16 | _C.magenta = '#e58ac9'
17 | _C.cyan = '#93ddfa'
18 |
19 | _C.accent = _C.yellow
20 | _C.bccent = _C.cyan
21 |
22 | return _C
23 |
--------------------------------------------------------------------------------
/theme/color/lite-xl/light/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#fbfbfb'
4 | _C.bg1 = '#e8e8e8'
5 | _C.bg2 = '#e0e0e0'
6 | _C.bg3 = '#d0d0d0'
7 | _C.bg4 = '#b0b0b0'
8 | _C.fg2 = '#808080'
9 | _C.fg1 = '#404040'
10 | _C.fg0 = '#181818'
11 |
12 | _C.red = '#fc1785'
13 | _C.green = '#22a21f'
14 | _C.yellow = '#fb6620'
15 | _C.blue = '#1586d2'
16 | _C.magenta = '#aa2782'
17 | _C.cyan = '#1c7c9c'
18 |
19 | _C.accent = _C.blue
20 | _C.bccent = _C.magenta
21 |
22 | return _C
23 |
24 |
--------------------------------------------------------------------------------
/theme/color/mardel/dark/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#1b1b1b'
4 | _C.bg1 = '#222222'
5 | _C.bg2 = '#2e2e2e'
6 | _C.bg3 = '#3c3c3c'
7 | _C.bg4 = '#545454'
8 | _C.fg2 = '#ababab'
9 | _C.fg1 = '#d4d4d4'
10 | _C.fg0 = '#eeeeee'
11 |
12 | _C.red = '#e45c79'
13 | _C.green = '#92bc55'
14 | _C.yellow = '#e0b05c'
15 | _C.blue = '#589ede'
16 | _C.magenta = '#ea93a9'
17 | _C.cyan = '#5ab1de'
18 |
19 | _C.accent = _C.green
20 | _C.bccent = _C.blue
21 |
22 | return _C
23 |
--------------------------------------------------------------------------------
/theme/color/mardel/light/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#f5f5f5'
4 | _C.bg1 = '#edecea'
5 | _C.bg2 = '#dddcd8'
6 | _C.bg3 = '#c2c1bd'
7 | _C.bg4 = '#9b9a97'
8 | _C.fg2 = '#848483'
9 | _C.fg1 = '#666666'
10 | _C.fg0 = '#404040'
11 |
12 | _C.red = '#ac3251'
13 | _C.green = '#465e21'
14 | _C.yellow = '#865329'
15 | _C.blue = '#254f88'
16 | _C.magenta = '#97356f'
17 | _C.cyan = '#226277'
18 |
19 | _C.accent = _C.green
20 | _C.bccent = _C.blue
21 |
22 | return _C
23 |
--------------------------------------------------------------------------------
/theme/color/oxocarbon/dark/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#161616'
4 | _C.bg1 = '#1e1e1e'
5 | _C.bg2 = '#262626'
6 | _C.bg3 = '#393939'
7 | _C.bg4 = '#525252'
8 | _C.fg2 = '#a4a4a4'
9 | _C.fg1 = '#d0d0d0'
10 | _C.fg0 = '#f2f2f2'
11 |
12 | _C.red = '#ee5396'
13 | _C.green = '#42be56'
14 | _C.yellow = '#ff7eb6'
15 | _C.blue = '#33b1ff'
16 | _C.magenta = '#be95ff'
17 | _C.cyan = '#3ddbd9'
18 |
19 | _C.accent = _C.cyan
20 | _C.bccent = _C.magenta
21 |
22 | return _C
23 |
--------------------------------------------------------------------------------
/theme/color/oxocarbon/light/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#ffffff'
4 | _C.bg1 = '#f2f2f2'
5 | _C.bg2 = '#d0d0d0'
6 | _C.bg3 = '#bcbcbc'
7 | _C.bg4 = '#a4a4a4'
8 | _C.fg2 = '#525252'
9 | _C.fg1 = '#393939'
10 | _C.fg0 = '#161616'
11 |
12 | _C.red = '#ee5396'
13 | _C.green = '#42be56'
14 | _C.yellow = '#ff6f00'
15 | _C.blue = '#0f62fe'
16 | _C.magenta = '#673ab7'
17 | _C.cyan = '#08bdba'
18 |
19 | _C.accent = _C.yellow
20 | _C.bccent = _C.blue
21 |
22 | return _C
23 |
--------------------------------------------------------------------------------
/theme/color/rose-pine/dark/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#191724'
4 | _C.bg1 = '#1f1d2e'
5 | _C.bg2 = '#21202e'
6 | _C.bg3 = '#26233a'
7 | _C.bg4 = '#524f67'
8 | _C.fg2 = '#6e6a86'
9 | _C.fg1 = '#908caa'
10 | _C.fg0 = '#e0def4'
11 |
12 | _C.red = '#eb6f92'
13 | _C.green = '#31748f'
14 | _C.yellow = '#f6c177'
15 | _C.blue = '#9ccfd8'
16 | _C.magenta = '#c4a7e7'
17 | _C.cyan = '#ebbcba'
18 |
19 | _C.accent = _C.yellow
20 | _C.bccent = _C.magenta
21 |
22 | return _C
23 |
--------------------------------------------------------------------------------
/theme/color/rose-pine/light/init.lua:
--------------------------------------------------------------------------------
1 | local _C = {}
2 |
3 | _C.bg0 = '#fffaf3'
4 | _C.bg1 = '#faf4ed'
5 | _C.bg2 = '#f2e9e1'
6 | _C.bg3 = '#dad6d9'
7 | _C.bg4 = '#cecacd'
8 | _C.fg2 = '#9893a5'
9 | _C.fg1 = '#797593'
10 | _C.fg0 = '#575279'
11 |
12 | _C.red = '#b4637a'
13 | _C.green = '#286983'
14 | _C.yellow = '#ea9d34'
15 | _C.blue = '#56949f'
16 | _C.magenta = '#907aa9'
17 | _C.cyan = '#d7827e'
18 |
19 | _C.accent = _C.green
20 | _C.bccent = _C.magenta
21 |
22 | return _C
23 |
--------------------------------------------------------------------------------
/theme/icons.lua:
--------------------------------------------------------------------------------
1 | local _I = {
2 | battery = {},
3 | weather = {},
4 | layout = {}
5 | }
6 |
7 | _I.font = 'gwnce '
8 | -- The glyphs are actually 9px tall.
9 | _I.size = require('beautiful').xresources.apply_dpi(9)
10 |
11 | -- Power.
12 | _I['power_shutdown'] = ''
13 | _I['power_reboot'] = ''
14 | _I['power_suspend'] = ''
15 | _I['power_logoff'] = ''
16 | -- Battery.
17 | _I.battery['UNKNOWN'] = ''
18 | _I.battery['NONE'] = ''
19 | _I.battery['CRITICAL'] = ''
20 | _I.battery['LOW'] = ''
21 | _I.battery['NORMAL'] = ''
22 | _I.battery['HIGH'] = ''
23 | _I.battery['FULL'] = ''
24 | _I.battery['CHARGING'] = ''
25 | _I.battery['CHARGED'] = ''
26 |
27 | -- Weather.
28 | _I.weather['day_clear'] = ''
29 | _I.weather['day_partly_cloudy'] = ''
30 | _I.weather['day_cloudy'] = ''
31 | _I.weather['day_light_rain'] = ''
32 | _I.weather['day_rain'] = ''
33 | _I.weather['day_storm'] = ''
34 | _I.weather['day_snow'] = ''
35 | _I.weather['day_fog'] = ''
36 | _I.weather['night_clear'] = ''
37 | _I.weather['night_partly_cloudy'] = ''
38 | _I.weather['night_cloudy'] = ''
39 | _I.weather['night_light_rain'] = ''
40 | _I.weather['night_rain'] = ''
41 | _I.weather['night_storm'] = ''
42 | _I.weather['night_snow'] = ''
43 | _I.weather['night_fog'] = ''
44 |
45 | -- Network.
46 | _I['net_wifi_high'] = ''
47 | _I['net_wifi_normal'] = ''
48 | _I['net_wifi_low'] = ''
49 | _I['net_wifi_none'] = ''
50 | _I['net_wired_normal'] = ''
51 | _I['net_wired_none'] = ''
52 | _I['net_none'] = ''
53 | -- Bluetooth.
54 | _I['bluez_off'] = ''
55 | _I['bluez_scanning'] = ''
56 | _I['bluez_on'] = ''
57 |
58 | -- Media.
59 | _I['music'] = ''
60 | _I['music_previous'] = ''
61 | _I['music_next'] = ''
62 | _I['music_pause'] = ''
63 | _I['music_play'] = ''
64 | _I['music_loop'] = _I['power_reboot']
65 | _I['music_shuffle'] = ''
66 | -- Audio.
67 | _I['audio_muted'] = ''
68 | _I['audio_decrease'] = ''
69 | _I['audio_increase'] = ''
70 | -- Microphone.
71 | _I['mic_muted'] = ''
72 | _I['mic_decrease'] = ''
73 | _I['mic_increase'] = ''
74 |
75 | -- Titlebar.
76 | _I['title_pin'] = ''
77 | _I['title_minimize'] = ''
78 | _I['title_maximize'] = ''
79 | _I['title_close'] = ''
80 | -- Layout.
81 | _I.layout['floating'] = ''
82 | _I.layout['tile'] = ''
83 | _I.layout['tileleft'] = ''
84 | _I.layout['tilebottom'] = ''
85 |
86 | -- Arrows.
87 | _I['arrow_up'] = ''
88 | _I['arrow_right'] = ''
89 | _I['arrow_down'] = ''
90 | _I['arrow_left'] = ''
91 | -- Miscelaneous.
92 | _I['util_magnifier'] = ''
93 | _I['util_hamburger'] = ''
94 |
95 | return _I
96 |
--------------------------------------------------------------------------------
/theme/init.lua:
--------------------------------------------------------------------------------
1 | local require, io = require, io
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 |
7 | -- Themes define colors, icons, font and wallpapers.
8 | beautiful.init(gears.filesystem.get_configuration_dir() .. 'theme/theme.lua')
9 |
10 | -- Check that the colorscheme to be set is actually different than current.
11 | -- If not, do nothing.
12 | local last_path = beautiful.state_dir .. 'last_colorscheme'
13 |
14 | local last = io.open(last_path, 'rb')
15 | local colors = nil
16 | if last == nil then
17 | awful.spawn.with_shell('mkdir -p ' .. beautiful.state_dir .. '; touch ' .. last_path)
18 | else
19 | last:close()
20 | -- It only really has one line.
21 | local lines = {}
22 | for line in io.lines(last_path) do
23 | lines[#lines + 1] = line
24 | end
25 | colors = lines[1]
26 | end
27 | if colors == beautiful.colorscheme then return end
28 |
29 | -- Set the tym colorscheme.
30 | require('script.tym-themer')()
31 | -- Merge the Xresources settings.
32 | require('script.xresources')()
33 |
34 | -- Update history file.
35 | last = io.open(last_path, 'w')
36 | if last == nil then return end
37 | last:write(beautiful.colorscheme)
38 | last:close()
39 |
--------------------------------------------------------------------------------
/theme/theme.lua:
--------------------------------------------------------------------------------
1 | local require, os = require, os
2 |
3 | local gears = require('gears')
4 |
5 | local gc = gears.color
6 | local gfs = gears.filesystem
7 | local dpi = require('beautiful.xresources').apply_dpi
8 |
9 | local home = os.getenv('HOME') .. '/'
10 | local user = require('config.user')
11 | local color = require('theme.color')
12 | local asset = gfs.get_configuration_dir() .. 'theme/assets/'
13 | local colorscheme = color.palette
14 |
15 | local helpers = require('helpers')
16 |
17 | -- NOTE: try to keep all usages of `gears.color.recolor_image` within this file to prevent
18 | -- it from being executed multiple times. It will burn through your RAM at alarming speed.
19 | local _T = {}
20 |
21 | -- Return the path to the file to avoid having to:
22 | -- - 1: clone the entire colors table into beautiful.
23 | -- - 2: having to run the `theme.color` logic every time.
24 | -- This may have been a very smart or ridiculously stupid choice. We'll see.
25 | _T.colorscheme = color.path
26 |
27 | _T.solid_wallpaper = false
28 | if not helpers.file_exists(user.wallpaper) then
29 | if user.wallpaper == nil or not user.wallpaper:match('#%x+') then
30 | gears.debug.print_warning("Wallpaper: inexistent file or incorrect color format")
31 | _T.wallpaper = colorscheme.bg2
32 | else
33 | _T.wallpaper = user.wallpaper
34 | end
35 | _T.solid_wallpaper = true
36 | end
37 |
38 | if not user.lite or user.lite == nil then
39 | _T.pfp = gears.surface.load_uncached(helpers.file_exists(user.pfp) and user.pfp
40 | or asset .. 'default/pfp.png')
41 | if not _T.solid_wallpaper then
42 | _T.wallpaper = gears.surface.load_uncached(user.wallpaper)
43 | end
44 | else
45 | if not _T.solid_wallpaper then
46 | _T.wallpaper = user.wallpaper
47 | end
48 | end
49 |
50 | -- Fonts
51 | _T.font_bitm = 'angwish sans '
52 | _T.font_mono = 'angwish '
53 | _T.bitm_size = dpi(9)
54 |
55 | -- A few defaults.
56 | _T.font = _T.font_bitm .. _T.bitm_size
57 | _T.bg_normal = colorscheme.bg0
58 | _T.fg_normal = colorscheme.fg0
59 |
60 | -- Awesome Icon.
61 | _T.awesome_icon = gc.recolor_image(asset .. 'util/awesome.png', colorscheme.fg0)
62 |
63 | -- WM
64 | -----
65 | _T.useless_gap = user.gaps or dpi(6)
66 | _T.master_width_factor = 0.58
67 |
68 | -- Borders.
69 | _T.border_width = 0
70 |
71 | -- Widgets
72 | ----------
73 | -- Notifications.
74 | _T.notification_spacing = _T.useless_gap * 2
75 | _T.notification_default = gc.recolor_image(asset .. 'notif/default.png', colorscheme.fg0)
76 | _T.notification_cancel = gc.recolor_image(asset .. 'notif/cancel.png', colorscheme.red)
77 |
78 | -- Tooltips.
79 | _T.tooltip_border_color = colorscheme.bg3
80 | _T.tooltip_border_width = dpi(1)
81 | _T.tooltip_bg = colorscheme.bg0
82 | _T.tooltip_fg = colorscheme.fg0
83 |
84 | -- Systray.
85 | _T.bg_systray = colorscheme.bg1
86 | _T.systray_icon_spacing = dpi(2)
87 |
88 | -- Bling
89 | --------
90 | -- Tabbar.
91 | _T.tabbar_disable = true
92 |
93 | -- Tag Preview.
94 | _T.tag_preview_widget_border_width = dpi(1)
95 | _T.tag_preview_widget_margin = _T.useless_gap
96 | _T.tag_preview_widget_bg = colorscheme.bg1
97 | _T.tag_preview_widget_border_color = colorscheme.bg3
98 | --- Clients.
99 | _T.tag_preview_client_opacity = 1
100 | _T.tag_preview_client_border_width = dpi(1)
101 | _T.tag_preview_client_bg = colorscheme.bg0
102 | _T.tag_preview_client_border_color = colorscheme.bg3
103 |
104 | -- Files
105 | --------
106 | _T.data_dir = home .. '.local/share/gwileful/'
107 | _T.state_dir = home .. '.local/state/gwileful/'
108 |
109 | return _T
110 |
--------------------------------------------------------------------------------
/ui/dash/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local beautiful = require('beautiful')
4 | local wibox = require('wibox')
5 |
6 | local dpi = beautiful.xresources.apply_dpi
7 |
8 | local color = require(beautiful.colorscheme)
9 | local mods = require('ui.dash.module')
10 |
11 | local width, height, margin = 352, 510, 6
12 |
13 | return function(s)
14 | local panel = wibox({
15 | ontop = true,
16 | visible = false,
17 | width = dpi(width),
18 | height = dpi(height),
19 | bg = color.bg0,
20 | x = dpi(s.geometry.width - width - margin),
21 | y = dpi(margin + s.bar.height),
22 | border_width = dpi(1),
23 | border_color = color.bg3,
24 | widget = {
25 | layout = wibox.layout.align.vertical,
26 | {
27 | layout = wibox.layout.fixed.vertical,
28 | mods.user(),
29 | {
30 | widget = wibox.container.background,
31 | forced_height = dpi(1),
32 | bg = color.bg3
33 | }
34 | },
35 | {
36 | widget = wibox.container.margin,
37 | margins = dpi(16),
38 | {
39 | layout = wibox.layout.fixed.vertical,
40 | spacing = dpi(16),
41 | mods.grid(),
42 | {
43 | widget = wibox.container.background,
44 | bg = color.bg3,
45 | forced_height = dpi(1)
46 | },
47 | mods.player(),
48 | mods.slider(),
49 | {
50 | widget = wibox.container.background,
51 | bg = color.bg3,
52 | forced_height = dpi(1)
53 | },
54 | {
55 | layout = wibox.layout.align.vertical,
56 | expand = 'none',
57 | nil, mods.random(), nil
58 | }
59 | }
60 | },
61 | mods.title()
62 | }
63 | })
64 |
65 | function panel:hide()
66 | self.visible = false
67 | end
68 |
69 | function panel:show()
70 | s.time:hide()
71 | self.visible = true
72 | end
73 |
74 | function panel:toggle()
75 | if self.visible then
76 | self:hide()
77 | else
78 | self:show()
79 | end
80 | end
81 |
82 | return panel
83 | end
84 |
--------------------------------------------------------------------------------
/ui/dash/module/grid.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local color = require(beautiful.colorscheme)
10 | local widget = require('widget')
11 | local icons = require('theme.icons')
12 |
13 | -- local net = require('signal.system.network')
14 |
15 | return function()
16 | -- Returns a grid entry, with an icon, title, and body.
17 | -- @param args:
18 | -- - title: the entry title.
19 | -- - body: the entry body.
20 | -- - icon: the icon in normal state.
21 | -- - on_click: the action to execute on icon click.
22 | local function entry(args)
23 | local title = widget.textbox.colored({
24 | text = args.title
25 | })
26 | local body = widget.textbox.colored({
27 | text = args.body,
28 | color = color.fg1
29 | })
30 | local icon = widget.textbox.colored({
31 | text = args.icon,
32 | font = icons.font .. icons.size * 2,
33 | align = 'center'
34 | })
35 |
36 | local w = wibox.widget({
37 | widget = wibox.container.background,
38 | bg = color.bg1,
39 | border_width = dpi(1),
40 | border_color = color.bg3,
41 | {
42 | widget = wibox.container.margin,
43 | margins = dpi(8),
44 | {
45 | layout = wibox.layout.fixed.horizontal,
46 | spacing = dpi(6),
47 | {
48 | widget = wibox.container.margin,
49 | margins = dpi(4),
50 | icon
51 | },
52 | {
53 | widget = wibox.container.place,
54 | valign = 'center',
55 | halign = 'left',
56 | {
57 | layout = wibox.layout.fixed.vertical,
58 | title,
59 | body
60 | }
61 | }
62 | }
63 | },
64 | buttons = {
65 | awful.button(nil, 1, args.on_click)
66 | }
67 | })
68 | w:connect_signal('mouse::enter', function(self)
69 | self.border_color = color.accent
70 | icon.color = color.accent
71 | end)
72 | w:connect_signal('mouse::leave', function(self)
73 | self.border_color = color.bg3
74 | icon.color = color.fg0
75 | end)
76 |
77 | return w
78 | end
79 |
80 | local network = entry({
81 | title = 'Network',
82 | body = 'No connection available',
83 | icon = icons['net_none'],
84 | -- on_click = function() net:toggle_networking() end
85 | on_click = function() end
86 | })
87 | local bluetooth = entry({
88 | title = 'Bluetooth',
89 | body = 'Powered on',
90 | icon = icons['bluez_on'],
91 | on_click = function() end
92 | })
93 |
94 | return wibox.widget({
95 | layout = wibox.layout.grid,
96 | orientation = 'horizontal',
97 | expand = true,
98 | spacing = dpi(8),
99 | network,
100 | bluetooth
101 | })
102 | end
103 |
--------------------------------------------------------------------------------
/ui/dash/module/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 | local path = ... .. '.'
3 | return setmetatable({}, {
4 | __index = function(_, key)
5 | local module, _ = require(path .. key)
6 | return module
7 | end
8 | })
9 |
--------------------------------------------------------------------------------
/ui/dash/module/player.lua:
--------------------------------------------------------------------------------
1 | local require, math, string, collectgarbage = require, math, string, collectgarbage
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 | local pctl = require('signal.system.playerctl')
10 |
11 | local widget = require('widget')
12 | local color = require(beautiful.colorscheme)
13 | local icons = require('theme.icons')
14 | local player = require('ui.scratch').music
15 | local user = require('config.user')
16 |
17 | return function()
18 | -- The art is a bit special.
19 | local song_art = wibox.widget.imagebox()
20 | if not user.lite or user.lite == nil then
21 | song_art.image = gears.surface.crop_surface({
22 | ratio = 3,
23 | surface = beautiful.wallpaper
24 | })
25 | end
26 |
27 | local function button(icon, action)
28 | local w = widget.textbox.colored({
29 | text = icon,
30 | font = icons.font .. icons.size
31 | })
32 | w.buttons = { awful.button({}, 1, action) }
33 | w:connect_signal('mouse::enter', function(self)
34 | self.color = color.bccent
35 | end)
36 | w:connect_signal('mouse::leave', function(self)
37 | self.color = color.fg0
38 | end)
39 | return w
40 | end
41 |
42 | local song_icon = button(icons['music'], function() player:turn_on() end)
43 | local song_status = widget.textbox.colored({
44 | text = 'Paused',
45 | color = color.fg1
46 | })
47 | local song_player = widget.textbox.colored({
48 | text = 'Source Unknown',
49 | color = color.fg1,
50 | align = 'right'
51 | })
52 | local song_title = widget.textbox.scrolling({
53 | text = 'Nothing Playing',
54 | font = beautiful.font_mono .. beautiful.bitm_size
55 | })
56 | local song_artist = widget.textbox.scrolling({
57 | text = 'by Unknown',
58 | font = beautiful.font_mono .. beautiful.bitm_size,
59 | color = color.fg1
60 | })
61 | local song_album = widget.textbox.scrolling({
62 | text = 'No album',
63 | font = beautiful.font_mono .. beautiful.bitm_size,
64 | color = color.fg2
65 | })
66 |
67 | local prog_text = widget.textbox.colored({
68 | text = '00:00 / 00:00',
69 | font = beautiful.font,
70 | color = color.fg1
71 | })
72 | local prog_slider = wibox.widget({
73 | widget = wibox.widget.slider,
74 | minimum = 0,
75 | maximum = 100,
76 | value = 0,
77 | bar_color = color.bg1,
78 | bar_active_color = color.bg3
79 | })
80 |
81 | local play_pause = button(icons['music_play'], function() pctl:play_pause() end)
82 | local back = button(icons['music_previous'], function() pctl:previous() end)
83 | local forward = button(icons['music_next'], function() pctl:next() end)
84 | local loop = button(icons['music_loop'], function() pctl:cycle_loop_status() end)
85 | local loop_text = widget.textbox.colored({ text = 'None' })
86 | local shuffle = button(icons['music_shuffle'], function() pctl:cycle_shuffle() end)
87 | local last_shuffle = false
88 | shuffle:connect_signal('mouse::leave', function(self)
89 | self.color = last_shuffle and color.accent or color.fg0
90 | end)
91 |
92 | local w = wibox.widget({
93 | layout = wibox.layout.stack,
94 | song_art,
95 | {
96 | widget = wibox.container.background,
97 | bg = {
98 | type = 'linear',
99 | from = { 0, 0 },
100 | to = { dpi(360), 0 },
101 | stops = {
102 | { 0, color.bg0 .. 'EF' }, { 0.45, color.bg0 .. 'EF' },
103 | { 0.73, color.bg0 .. 'CC' }, { 1, color.bg0 .. 'A0' }
104 | }
105 | },
106 | border_width = dpi(1),
107 | border_color = color.bg3,
108 | {
109 | layout = wibox.layout.align.vertical,
110 | {
111 | widget = wibox.container.margin,
112 | margins = {
113 | top = dpi(16), bottom = dpi(14),
114 | left = dpi(16), right = dpi(16)
115 | },
116 | {
117 | layout = wibox.layout.fixed.vertical,
118 | spacing = dpi(8),
119 | {
120 | layout = wibox.layout.align.horizontal,
121 | {
122 | layout = wibox.layout.fixed.horizontal,
123 | spacing = dpi(6),
124 | song_icon,
125 | song_status
126 | },
127 | nil,
128 | song_player
129 | },
130 | {
131 | layout = wibox.layout.fixed.vertical,
132 | song_title,
133 | song_artist,
134 | song_album
135 | },
136 | {
137 | layout = wibox.layout.align.horizontal,
138 | prog_text,
139 | nil,
140 | {
141 | layout = wibox.layout.fixed.horizontal,
142 | spacing = dpi(4),
143 | back,
144 | play_pause,
145 | forward,
146 | {
147 | widget = wibox.container.margin,
148 | margins = { left = dpi(4), right = dpi(4) },
149 | {
150 | layout = wibox.layout.fixed.horizontal,
151 | spacing = dpi(6),
152 | loop,
153 | loop_text
154 | }
155 | },
156 | shuffle
157 | }
158 | }
159 | }
160 | },
161 | nil,
162 | {
163 | layout = wibox.layout.fixed.vertical,
164 | {
165 | widget = wibox.container.background,
166 | bg = color.bg3,
167 | forced_height = dpi(1)
168 | },
169 | {
170 | widget = wibox.container.constraint,
171 | strategy = 'exact',
172 | height = dpi(4),
173 | prog_slider
174 | }
175 | }
176 | }
177 | }
178 | })
179 |
180 | -- Why not just use the `new` variable that the signal exposes? Because the first song
181 | -- that comes never counts as new, and so using the `new` variable results in the first
182 | -- song upon AwesomeWM reload not updating the widget.
183 | local last_poll = {
184 | title = nil,
185 | artist = nil,
186 | prog = 0
187 | }
188 | pctl:connect_signal('metadata', function(_, title, artist, cover, album, _, source)
189 | -- Whenever a new song comes through:
190 | if title ~= last_poll.title or artist ~= last_poll.artist then
191 | -- Update widget info.
192 | song_title.text = gears.string.xml_unescape(title or 'Unknown')
193 | song_artist.text = 'by ' .. (gears.string.xml_unescape(artist or 'Unknown'))
194 | song_album.text = 'on ' .. (gears.string.xml_unescape(album or 'Unknown'))
195 | song_player.text = 'via ' .. source
196 | -- Update image only if desired.
197 | if not user.lite or user.lite == nil then
198 | song_art.image = gears.surface.crop_surface({
199 | ratio = 3,
200 | surface = gears.surface.load_uncached(cover or beautiful.wallpaper)
201 | })
202 | end
203 |
204 | -- Update last poll info.
205 | last_poll.title = title
206 | last_poll.artist = artist
207 | last_poll.player = source
208 |
209 | -- Manually call Lua's gc to get rid of the old album art and prevent memory
210 | -- usage from stacking up.
211 | collectgarbage('collect')
212 | end
213 | end)
214 |
215 | pctl:connect_signal('position', function(_, prog, len, _)
216 | prog_slider.maximum = len
217 | prog_slider.value = prog
218 | prog_text.text = string.format('%02d:%02d', math.floor(prog / 60), prog % 60)
219 | .. ' / ' .. string.format('%02d:%02d', math.floor(len / 60), len % 60)
220 | end)
221 |
222 | pctl:connect_signal('playback_status', function(_, playing, _)
223 | if playing then
224 | prog_slider.bar_active_color = color.bg4
225 | song_status.text = 'Playing'
226 | play_pause.text = icons['music_pause']
227 | else
228 | prog_slider.bar_active_color = color.bg3
229 | song_status.text = 'Paused'
230 | play_pause.text = icons['music_play']
231 | end
232 | end)
233 |
234 | pctl:connect_signal('loop_status', function(_, loop_status, _)
235 | loop_text.text = loop_status:gsub('^%l', string.upper)
236 | end)
237 |
238 | pctl:connect_signal('shuffle', function(_, shuff, _)
239 | shuffle.color = shuff and color.accent or color.fg0
240 | last_shuffle = shuff
241 | end)
242 |
243 | -- Prevent overwriting of song progress when not using the bar to seek.
244 | -- Expecting a seek to differ by 4 seconds from the previous time seems to do the
245 | -- trick.
246 | local prog_hover = false
247 | prog_slider:connect_signal('mouse::enter', function()
248 | prog_hover = true
249 | end)
250 | prog_slider:connect_signal('mouse::leave', function()
251 | prog_hover = false
252 | end)
253 | prog_slider:connect_signal('property::value', function(_, new)
254 | if prog_hover and (new > last_poll.prog + 4 or new < last_poll.prog - 4) then
255 | pctl:set_position(new)
256 | end
257 | last_poll.prog = new
258 | end)
259 |
260 | return w
261 | end
262 |
--------------------------------------------------------------------------------
/ui/dash/module/random.lua:
--------------------------------------------------------------------------------
1 | local wibox = require('wibox')
2 |
3 | local messages = {
4 | 'This is the world of the recycled vessel, created to avoid the destruction of all.',
5 | 'The Black Scrawl. A lost destiny. A white book. A false truth.',
6 | 'Soldiers of salt calling forth white death. They are Legion, those who plunged the' ..
7 | ' world into darkness.',
8 | 'The dragon\'s corpse brought death to the world, delivering unto it the power of ' ..
9 | 'magic.',
10 | 'The black sickness stains the future. They journey to return to soulless vessels.',
11 | 'The apocalypse divided the world in two: one that knows not day, and one which has' ..
12 | ' never seen night.',
13 | 'Black and White. Thirteen pacts. The vessels\' forms waver as they cross time and ' ..
14 | 'space.',
15 | 'The song of man has been drowned out. In its place, the scream of something inhuman.',
16 | 'The sky falls with the dragon. The world ends this day.',
17 | 'The puppet priest collects the accursed prayers and polishes the vessel.',
18 | 'So long as this memory exists -so long as mankind has hope- a bloody battle will ' ..
19 | 'be waged over the holy domain of the body.',
20 | 'Foolish human. Foolish human. Foolish human.\nFoolish vessel.',
21 | 'All is paid. All is sacrifice.',
22 | 'Do not bring back the light. Do not bring back the vessel. Do not bring back the ' ..
23 | 'future. Do not bring it back.',
24 | 'Every beam of light is an invitation to death.'
25 | }
26 |
27 | return function()
28 | return wibox.widget({
29 | widget = wibox.widget.textbox,
30 | text = messages[math.random(#messages)],
31 | halign = 'center'
32 | })
33 | end
34 |
--------------------------------------------------------------------------------
/ui/dash/module/slider.lua:
--------------------------------------------------------------------------------
1 | local require, math = require, math
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local widget = require('widget')
10 | local pctl = require('module.bling').signal.playerctl.lib()
11 | local audio = require('signal.system.audio')
12 | local color = require(beautiful.colorscheme)
13 | local icons = require('theme.icons')
14 |
15 | -- Creates a slider, with an icon, label and percentage values attached.
16 | -- @param args:
17 | -- - label, the text shown next to the icon.
18 | -- - icon, the icon used in normal state.
19 | -- - icon_click, the action to perform when clicking the icon.
20 | -- - bar_action, what to do with new values of the slider.
21 | local function slider(args)
22 | -- Icon.
23 | local icon = widget.textbox.colored({
24 | text = args.icon,
25 | font = icons.font .. icons.size
26 | })
27 | local iconbox = wibox.widget({
28 | widget = wibox.container.background,
29 | bg = color.bg2,
30 | {
31 | widget = wibox.container.margin,
32 | margins = {
33 | top = dpi(5), bottom = dpi(3),
34 | left = dpi(7), right = dpi(7)
35 | },
36 | icon
37 | }
38 | })
39 | iconbox.buttons = { awful.button(nil, 1, args.icon_click) }
40 | iconbox:connect_signal('mouse::enter', function()
41 | icon.color = color.accent
42 | end)
43 | iconbox:connect_signal('mouse::leave', function()
44 | icon.color = color.fg0
45 | end)
46 |
47 | local label = widget.textbox.colored({ text = args.label })
48 |
49 | -- Slider.
50 | local bar = wibox.widget({
51 | widget = wibox.widget.slider,
52 | bar_height = dpi(21),
53 | bar_color = color.bg1,
54 | handle_width = dpi(4),
55 | handle_border_width = 0,
56 | bar_active_color = color.bg2,
57 | handle_color = color.bg3,
58 | minimum = 0,
59 | maximum = 100,
60 | value = 0,
61 | id = 'slider_role'
62 | })
63 | local is_hovered = false
64 | bar:connect_signal('mouse::enter', function(self)
65 | is_hovered = true
66 | self.handle_color = color.accent
67 | self.bar_border_color = color.accent
68 | end)
69 | bar:connect_signal('mouse::leave', function(self)
70 | is_hovered = false
71 | self.handle_color = color.bg3
72 | self.bar_border_color = color.bg3
73 | end)
74 | bar:connect_signal('property::value', function(_, val)
75 | if is_hovered then
76 | args.bar_action(_, val)
77 | end
78 | end)
79 |
80 | -- Non-interactable level.
81 | local level = widget.textbox.colored({ text = 'N/A' })
82 |
83 | local border = wibox.widget({
84 | widget = wibox.container.background,
85 | border_width = dpi(1),
86 | border_color = color.bg3
87 | })
88 | border:connect_signal('mouse::enter', function(self)
89 | self.border_color = color.accent
90 | end)
91 | border:connect_signal('mouse::leave', function(self)
92 | self.border_color = color.bg3
93 | end)
94 |
95 | return wibox.widget({
96 | widget = wibox.container.constraint,
97 | strategy = 'exact',
98 | height = dpi(21),
99 | {
100 | widget = border,
101 | {
102 | layout = wibox.layout.fixed.horizontal,
103 | iconbox,
104 | {
105 | layout = wibox.layout.stack,
106 | bar,
107 | {
108 | widget = wibox.container.margin,
109 | margins = {
110 | top = dpi(5), bottom = dpi(3),
111 | right = dpi(7)
112 | },
113 | {
114 | layout = wibox.layout.align.horizontal,
115 | {
116 | widget = wibox.container.constraint,
117 | strategy = 'max',
118 | width = dpi(200),
119 | label
120 | },
121 | nil,
122 | level
123 | }
124 | }
125 | }
126 | }
127 | },
128 | set_value = function(_, value)
129 | bar.value = value
130 | end,
131 | set_level = function(_, value)
132 | level.text = value .. '%'
133 | end,
134 | set_label = function(_, value)
135 | label.text = value
136 | end,
137 | set_icon = function(_, value)
138 | icon.text = value
139 | end
140 | })
141 | end
142 |
143 | local volume_slider = slider({
144 | label = 'Audio Device Unknown',
145 | icon = icons['audio_muted'],
146 | icon_click = function() audio:default_sink_toggle_mute() end,
147 | bar_action = function(_, new) audio:default_sink_set_volume(new) end
148 | })
149 | audio:connect_signal('sinks::default', function(_, default_sink)
150 | volume_slider.value = default_sink.volume
151 | volume_slider.level = default_sink.volume
152 | if default_sink.mute or default_sink.volume == 0 then
153 | volume_slider.icon = icons['audio_muted']
154 | elseif default_sink.volume >= 50 then
155 | volume_slider.icon = icons['audio_increase']
156 | else
157 | volume_slider.icon = icons['audio_decrease']
158 | end
159 | volume_slider.label = default_sink.description
160 | end)
161 |
162 | local mic_slider = slider({
163 | label = 'Microphone Unknown',
164 | icon = icons['mic_muted'],
165 | icon_click = function() audio:default_source_toggle_mute() end,
166 | bar_action = function(_, new) audio:default_source_set_volume(new) end
167 | })
168 | audio:connect_signal('sources::default', function(_, default_source)
169 | mic_slider.value = default_source.volume
170 | mic_slider.level = default_source.volume
171 | if default_source.mute or default_source.volume == 0 then
172 | mic_slider.icon = icons['mic_muted']
173 | elseif default_source.volume >= 50 then
174 | mic_slider.icon = icons['mic_increase']
175 | else
176 | mic_slider.icon = icons['mic_decrease']
177 | end
178 | mic_slider.label = default_source.description
179 | end)
180 |
181 | local music_slider = slider({
182 | label = 'Player Unknown',
183 | icon = icons['music'],
184 | icon_click = function() end,
185 | bar_action = function(_, new) pctl:set_volume(new / 100) end
186 | })
187 | pctl:connect_signal('volume', function(_, volume, player)
188 | music_slider.label = player
189 | local value = math.floor((volume or 0) * 100)
190 | music_slider.value = value
191 | music_slider.level = value
192 | end)
193 |
194 | return function()
195 | return wibox.widget({
196 | layout = wibox.layout.fixed.vertical,
197 | spacing = dpi(9),
198 | music_slider,
199 | volume_slider,
200 | mic_slider
201 | })
202 | end
203 |
--------------------------------------------------------------------------------
/ui/dash/module/title.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local beautiful = require('beautiful')
4 | local wibox = require('wibox')
5 |
6 | local dpi = beautiful.xresources.apply_dpi
7 | local color = require(beautiful.colorscheme)
8 | local icons = require('theme.icons')
9 |
10 | return function()
11 | return wibox.widget({
12 | layout = wibox.layout.fixed.vertical,
13 | {
14 | widget = wibox.container.background,
15 | bg = color.bg3,
16 | forced_height = dpi(1)
17 | },
18 | {
19 | widget = wibox.container.background,
20 | bg = color.bg1,
21 | {
22 | layout = wibox.layout.align.horizontal,
23 | {
24 | widget = wibox.container.margin,
25 | margins = {
26 | top = dpi(6), bottom = dpi(6),
27 | left = dpi(10), right = dpi(10)
28 | },
29 | {
30 | widget = wibox.widget.textbox,
31 | text = 'Quick Settings Menu',
32 | }
33 | },
34 | nil,
35 | {
36 | layout = wibox.layout.fixed.horizontal,
37 | {
38 | widget = wibox.container.background,
39 | bg = color.bg3,
40 | forced_width = dpi(1)
41 | },
42 | {
43 | widget = wibox.container.margin,
44 | margins = {
45 | left = dpi(8), right = dpi(8),
46 | top = dpi(6), bottom = dpi(6)
47 | },
48 | {
49 | widget = wibox.widget.textbox,
50 | text = icons['util_hamburger'],
51 | font = icons.font .. icons.size
52 | }
53 | }
54 | }
55 | }
56 | }
57 | })
58 | end
59 |
--------------------------------------------------------------------------------
/ui/dash/module/user.lua:
--------------------------------------------------------------------------------
1 | local require, os, awesome = require, os, awesome
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local color = require(beautiful.colorscheme)
11 | local icons = require('theme.icons')
12 | local widget = require('widget')
13 | local conf = require('config.user')
14 |
15 | local pfp_widget
16 | if not conf.lite or conf.lite == nil then
17 | pfp_widget = wibox.widget({
18 | widget = wibox.widget.imagebox,
19 | image = gears.surface.crop_surface({
20 | surface = beautiful.pfp,
21 | ratio = 7/3,
22 | left = dpi(48)
23 | })
24 | })
25 | end
26 |
27 | local host = widget.textbox.colored({
28 | text = '@host',
29 | font = beautiful.font_bitm .. beautiful.bitm_size * 2,
30 | align = 'right'
31 | })
32 | local user_at_host = wibox.widget({
33 | layout = wibox.layout.fixed.horizontal,
34 | widget.textbox.colored({
35 | text = os.getenv('USER') or 'user',
36 | font = beautiful.font_bitm .. beautiful.bitm_size * 2,
37 | color = color.accent,
38 | align = 'right'
39 | }),
40 | host
41 | })
42 | awful.spawn.easy_async_with_shell('uname -n', function(out)
43 | if out == nil or out == '' then return end
44 | host.text = '@' .. out:gsub('\n', '')
45 | end)
46 |
47 | -- Updated every minute.
48 | local uptime_widget = widget.textbox.colored({
49 | text = 'Uptime Unknown!',
50 | color = color.fg1,
51 | align = 'right'
52 | })
53 | gears.timer({
54 | timeout = 60,
55 | call_now = true,
56 | autostart = true,
57 | callback = function()
58 | awful.spawn.easy_async('uptime -p', function(up)
59 | uptime_widget.text = up:gsub('\n', '')
60 | end)
61 | end
62 | })
63 |
64 | local function button(icon, action)
65 | local w = widget.textbox.colored({
66 | font = icons.font .. icons.size * 2,
67 | text = icon
68 | })
69 | w.buttons = { awful.button(nil, 1, action) }
70 | w.set_action = function(self, new_action)
71 | self.buttons = { awful.button(nil, 1, new_action) }
72 | end
73 | w:connect_signal('mouse::enter', function(self)
74 | self.color = color.red
75 | end)
76 | w:connect_signal('mouse::leave', function(self)
77 | self.color = color.fg0
78 | end)
79 | return w
80 | end
81 |
82 | -- Power options revealer.
83 | local show_power = button(icons['arrow_right'], function() end)
84 | local power = wibox.widget({
85 | layout = wibox.layout.fixed.horizontal,
86 | spacing = dpi(8),
87 | show_power,
88 | {
89 | layout = wibox.layout.fixed.horizontal,
90 | spacing = dpi(8),
91 | visible = false,
92 | id = 'power',
93 | button(icons['power_shutdown'], function() awful.spawn(conf.shutdown_cmd) end),
94 | button(icons['power_reboot'], function() awful.spawn(conf.reboot_cmd) end),
95 | button(icons['power_suspend'], function() awful.spawn(conf.suspend_cmd) end),
96 | button(icons['power_logoff'], function() awesome.quit() end)
97 | },
98 | toggle_show_power = function(self)
99 | local power = self:get_children_by_id('power')[1]
100 | power.visible = not power.visible
101 | show_power.text = power.visible and icons['arrow_left'] or icons['arrow_right']
102 | end
103 | })
104 | show_power.action = function()
105 | power.toggle_show_power(power)
106 | end
107 |
108 | return function()
109 | return wibox.widget({
110 | layout = wibox.layout.stack,
111 | {
112 | layout = wibox.layout.fixed.horizontal,
113 | {
114 | widget = wibox.container.constraint,
115 | strategy = 'exact',
116 | width = dpi(240),
117 | {
118 | layout = wibox.layout.stack,
119 | pfp_widget,
120 | {
121 | widget = wibox.container.background,
122 | bg = {
123 | type = 'linear',
124 | from = { dpi(240), 0 },
125 | to = { 0, 0 },
126 | stops = {
127 | { 0, color.bg1 }, { 0.33, color.bg1 .. 'F0' },
128 | { 0.66, color.bg1 .. 'DC' }, { 1, color.bg1 .. 'C0' }
129 | }
130 | }
131 | }
132 | }
133 | },
134 | {
135 | widget = wibox.container.background,
136 | bg = color.bg1,
137 | forced_width = dpi(115)
138 | }
139 | },
140 | {
141 | widget = wibox.container.margin,
142 | margins = dpi(16),
143 | {
144 | layout = wibox.layout.align.vertical,
145 | {
146 | layout = wibox.layout.fixed.vertical,
147 | spacing = dpi(2),
148 | -- Text alignment sucks.
149 | {
150 | layout = wibox.layout.align.horizontal,
151 | nil, nil,
152 | user_at_host,
153 | },
154 | uptime_widget
155 | },
156 | nil,
157 | power
158 | }
159 | }
160 | })
161 | end
162 |
--------------------------------------------------------------------------------
/ui/launcher/init.lua:
--------------------------------------------------------------------------------
1 | -- Logic based largely (carbon copied, for the most part) on Stardust-kyun's launcher.
2 | -- I removed mouse support :P
3 | -- https://github.com/Stardust-kyun/dotfiles/blob/126b2df5edbec8044cbdbf13fb261f935311f6fe/home/.config/awesome/theme/launcher.lua
4 | local require, table, ipairs = require, table, ipairs
5 |
6 | local awful = require('awful')
7 | local beautiful = require('beautiful')
8 | local gears = require('gears')
9 | local wibox = require('wibox')
10 |
11 | local dpi = beautiful.xresources.apply_dpi
12 | local gio = require('lgi').Gio
13 |
14 | local user = require('config.user')
15 | local color = require(beautiful.colorscheme)
16 | local height, width, margin = 220, 540, 6
17 | local entry_max = 12
18 |
19 | return function(s)
20 | local default
21 | if not user.lite or user.lite == nil then
22 | default = gears.surface.crop_surface({
23 | ratio = width / height,
24 | surface = beautiful.wallpaper
25 | })
26 | end
27 |
28 | local launcher = wibox({
29 | visible = false,
30 | ontop = true,
31 | width = dpi(width),
32 | height = dpi(height),
33 | bg = color.bg0,
34 | border_width = dpi(1),
35 | border_color = color.bg3
36 | })
37 |
38 | --- Widgets
39 | -----------
40 | local _W = {}
41 | _W.prompt = wibox.widget.textbox('Search')
42 | _W.entries = wibox.widget({
43 | layout = wibox.layout.grid,
44 | spacing = dpi(8),
45 | expand = true,
46 | column_count = 3,
47 | minimum_row_height = dpi(29),
48 | homogenous = false
49 | })
50 |
51 | launcher:setup({
52 | layout = wibox.layout.stack,
53 | {
54 | widget = wibox.widget.imagebox,
55 | image = default
56 | },
57 | {
58 | widget = wibox.container.background,
59 | bg = {
60 | type = 'linear',
61 | from = { 0, 0 },
62 | to = { 0, dpi(height) },
63 | stops = {
64 | { 0, color.bg0 .. 'A0' }, { 0.25, color.bg0 .. 'D0' },
65 | { 0.4, color.bg0 .. 'F0' }, { 0.55, color.bg0 }, { 1, color.bg0 }
66 | }
67 | },
68 | {
69 | widget = wibox.container.margin,
70 | margins = dpi(16),
71 | {
72 | layout = wibox.layout.fixed.vertical,
73 | spacing = dpi(16),
74 | {
75 | layout = wibox.layout.align.horizontal,
76 | {
77 | layout = wibox.layout.fixed.vertical,
78 | {
79 | widget = wibox.container.background,
80 | bg = color.bg0,
81 | {
82 | widget = wibox.container.margin,
83 | margins = dpi(8),
84 | forced_height = dpi(28),
85 | _W.prompt
86 | }
87 | },
88 | {
89 | widget = wibox.container.background,
90 | bg = color.bg2,
91 | forced_height = dpi(1)
92 | }
93 | },
94 | nil, nil
95 | },
96 | _W.entries
97 | }
98 | }
99 | }
100 | })
101 |
102 | --- App related
103 | ---------------
104 | local _A = {}
105 |
106 | -- Gets all entries.
107 | function _A.gen()
108 | local entry_list = {}
109 | for _, entry in ipairs(gio.AppInfo.get_all()) do
110 | if entry:should_show() then
111 | local name =
112 | entry:get_name():gsub("&", "&"):gsub("<", "<"):gsub("'", "'")
113 | table.insert(entry_list, { name = name, appinfo = entry })
114 | end
115 | end
116 | return entry_list
117 | end
118 |
119 | -- Reduces shown entries to those matching the user's input, sorted and merged into one
120 | -- table.
121 | function _A.filter(cmd)
122 | _A.filtered = {}
123 | _A.reg_filtered = {}
124 |
125 | -- Filter entries matching `cmd` (user's input).
126 | for _, entry in ipairs(_A.unfiltered) do
127 | if entry.name:lower():sub(1, cmd:len()) == cmd:lower() then
128 | table.insert(_A.filtered, entry)
129 | elseif entry.name:lower():match(cmd:lower()) then
130 | table.insert(_A.reg_filtered, entry)
131 | end
132 | end
133 | -- Sort remaining entries.
134 | table.sort(_A.filtered, function(a, b) return a.name:lower() < b.name:lower() end)
135 | table.sort(_A.reg_filtered, function(a, b) return a.name:lower() < b.name:lower() end)
136 | -- Merge entries.
137 | for i = 1, #_A.reg_filtered do
138 | _A.filtered[#_A.filtered + 1] = _A.reg_filtered[i]
139 | end
140 | -- Clear entries.
141 | _W.entries:reset()
142 | _A.entry_index, _A.start_index = 1, 1
143 |
144 | -- Add filtered entries.
145 | for i, entry in ipairs(_A.filtered) do
146 | local widget = wibox.widget({
147 | widget = wibox.container.background,
148 | bg = color.bg1,
149 | fg = color.fg0,
150 | border_width = dpi(1),
151 | border_color = color.bg1,
152 | {
153 | widget = wibox.container.margin,
154 | margins = {
155 | top = dpi(6), bottom = dpi(6),
156 | left = dpi(12), right = dpi(12)
157 | },
158 | wibox.widget.textbox(entry.name)
159 | }
160 | })
161 |
162 | widget.visible = (_A.start_index <= i and i <= _A.start_index + entry_max - 1)
163 | _W.entries:add(widget)
164 | if i == _A.entry_index then
165 | widget.fg = color.accent
166 | widget.border_color = color.bg3
167 | end
168 | end
169 |
170 | collectgarbage('collect')
171 | end
172 |
173 | function _A.open()
174 | -- Reset everything.
175 | _A.start_index, _A.entry_index = 1, 1
176 | -- Gets all entries.
177 | _A.unfiltered = _A.gen()
178 | _A.filter('')
179 |
180 | -- Populates prompt and makes it responsive.
181 | awful.prompt.run({
182 | prompt = 'Searching: ',
183 | textbox = _W.prompt,
184 | -- Upon modifying the contents of the prompt.
185 | changed_callback = function(cmd)
186 | local input = cmd:gsub("[%[%]%(%)%.%-%+%?%*%%]", "%%%1")
187 | _A.filter(input)
188 | end,
189 | -- Upon pressing enter.
190 | exe_callback = function(cmd)
191 | local entry = _A.filtered[_A.entry_index]
192 | if entry then
193 | entry.appinfo:launch()
194 | else
195 | awful.spawn.with_shell(cmd)
196 | end
197 | end,
198 | -- When all else is done.
199 | done_callback = function()
200 | launcher.visible = false
201 | end
202 | })
203 | end
204 |
205 | function launcher:open()
206 | launcher.visible = not launcher.visible
207 |
208 | if launcher.visible then
209 | _A.open()
210 | else
211 | awful.keygrabber.stop()
212 | end
213 |
214 | awful.placement.next_to(launcher, {
215 | margins = { top = dpi(margin), left = dpi(margin) },
216 | geometry = s.bar,
217 | preferred_positions = 'bottom',
218 | preferred_anchors = 'front'
219 | })
220 | end
221 |
222 | return launcher
223 | end
224 |
--------------------------------------------------------------------------------
/ui/menu/init.lua:
--------------------------------------------------------------------------------
1 | local require, awesome, os = require, awesome, os
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local hotkeys = require('awful.hotkeys_popup')
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local apps = require('config.apps')
11 | local user = require('config.user')
12 | local color = require(beautiful.colorscheme)
13 |
14 | --- Menu
15 | local menu = {}
16 | local section = {}
17 |
18 | section.awesome = {
19 | { 'Hotkeys', function() hotkeys.show_help(nil, awful.screen.focused()) end },
20 | { 'Documentation', apps.browser .. ' https://awesomewm.org/apidoc' },
21 | { 'Configuration', apps.editor_cmd .. ' ' .. awesome.conffile },
22 | { 'Reload', awesome.restart }
23 | }
24 |
25 | section.power = {
26 | { 'Log off', function() awesome.quit() end },
27 | { 'Suspend', function() os.execute(user.suspend_cmd) end },
28 | { 'Reboot', function() os.execute(user.reboot_cmd) end },
29 | { 'Shutdown', function() os.execute(user.shutdown_cmd) end }
30 | }
31 |
32 | -- Create a main menu.
33 | menu.main = awful.menu({
34 | theme = {
35 | font = beautiful.font,
36 | width = dpi(172),
37 | height = dpi(32),
38 | bg_normal = color.bg0,
39 | bg_focus = color.bg1,
40 | border_width = dpi(1),
41 | border_color = color.bg3
42 | },
43 | items = {
44 | { 'Terminal', apps.terminal },
45 | { 'Editor', apps.editor },
46 | { 'Browser', apps.browser },
47 | { 'Awesome', section.awesome },
48 | { 'Power', section.power }
49 | }
50 | })
51 |
52 | -- Add margins.
53 | menu.main.wibox:set_widget(wibox.widget({
54 | widget = wibox.container.margin,
55 | margins = dpi(12),
56 | {
57 | widget = wibox.container.background,
58 | menu.main.wibox.widget
59 | }
60 | }))
61 | -- Repeat for submenus.
62 | awful.menu.old_new = awful.menu.new
63 | function awful.menu.new(...)
64 | local submenu = awful.menu.old_new(...)
65 | submenu.wibox.bg = color.bg0
66 | submenu.wibox.border_width = dpi(1)
67 | submenu.wibox.border_color = color.bg3
68 | submenu.wibox:set_widget(wibox.widget({
69 | widget = wibox.container.background,
70 | bg = color.bg0,
71 | {
72 | widget = wibox.container.margin,
73 | margins = dpi(12),
74 | {
75 | widget = wibox.container.background,
76 | submenu.wibox.widget
77 | }
78 | }
79 | }))
80 | return submenu
81 | end
82 |
83 | return menu
84 |
--------------------------------------------------------------------------------
/ui/notification/init.lua:
--------------------------------------------------------------------------------
1 | -- Credits to Aproxia for the timeout animation logic.
2 | -- https://github.com/Aproxia-dev
3 | local require, collectgarbage = require, collectgarbage
4 |
5 | local awful = require('awful')
6 | local beautiful = require('beautiful')
7 | local gears = require('gears')
8 | local naughty = require('naughty')
9 | local wibox = require('wibox')
10 |
11 | local dpi = beautiful.xresources.apply_dpi
12 |
13 | local widget = require('widget')
14 | local color = require(beautiful.colorscheme)
15 |
16 | local _N = {}
17 |
18 | function _N.title(n)
19 | return widget.textbox.scrolling({
20 | text = ''..((n.title == nil or n.title == '') and 'AwesomeWM' or n.title)..''
21 | })
22 | end
23 |
24 | function _N.body(n)
25 | return widget.textbox.scrolling({
26 | text = n.message,
27 | color = color.fg1 .. 'cc',
28 | dir = 'vertical'
29 | })
30 | end
31 |
32 | function _N.icon(n)
33 | return wibox.widget({
34 | widget = wibox.widget.imagebox,
35 | image = n.icon and gears.surface.crop_surface({
36 | ratio = 1,
37 | surface = gears.surface.load_uncached(n.icon)
38 | }) or beautiful.notification_default,
39 | buttons = { awful.button(nil, 1, function() n:destroy() end) },
40 | scaling_quality = 'nearest'
41 | })
42 | end
43 |
44 | function _N.timeout()
45 | return wibox.widget({
46 | widget = wibox.widget.progressbar,
47 | max_value = 100,
48 | value = 0,
49 | background_color = color.bg1,
50 | color = color.accent,
51 | forced_height = dpi(3)
52 | })
53 | end
54 |
55 | function _N.actions(n)
56 | return wibox.widget({
57 | widget = naughty.list.actions,
58 | notification = n,
59 | base_layout = wibox.widget({
60 | layout = wibox.layout.flex.horizontal,
61 | spacing = dpi(2)
62 | }),
63 | style = {
64 | underline_normal = false,
65 | underline_selected = false,
66 | bg_normal = color.bg1
67 | },
68 | widget_template = {
69 | widget = wibox.container.background,
70 | bg = color.bg4 .. '20',
71 | {
72 | widget = wibox.container.margin,
73 | margins = dpi(3),
74 | {
75 | widget = wibox.container.place,
76 | halign = 'center',
77 | {
78 | widget = wibox.widget.textbox,
79 | font = beautiful.font,
80 | id = 'text_role'
81 | }
82 | }
83 | }
84 | }
85 | })
86 | end
87 |
88 | return function(n)
89 | -- Store original timeout and set it to an unreachable number.
90 | local timeout = n.timeout
91 | -- Using `math.huge` here breaks naughty :P.
92 | n.timeout = 999999
93 | local timeout_bar = _N.timeout()
94 |
95 | -- Sections, divided into blocks to avoid YandereDev levels of indentation.
96 | local titlebox = wibox.widget({
97 | widget = wibox.container.background,
98 | bg = color.bg3,
99 | {
100 | widget = wibox.container.margin,
101 | margins = { bottom = dpi(1) },
102 | {
103 | widget = wibox.container.background,
104 | bg = color.bg1,
105 | {
106 | widget = wibox.container.margin,
107 | margins = {
108 | top = dpi(8), bottom = dpi(8),
109 | left = dpi(12), right = dpi(12)
110 | },
111 | {
112 | widget = wibox.container.place,
113 | halign = 'center',
114 | _N.title(n)
115 | }
116 | }
117 | }
118 | }
119 | })
120 |
121 | local contentbox = wibox.widget({
122 | layout = wibox.layout.align.vertical,
123 | {
124 | widget = wibox.container.margin,
125 | margins = dpi(12),
126 | {
127 | widget = wibox.container.constraint,
128 | strategy = 'max',
129 | width = dpi(280),
130 | height = dpi(250),
131 | {
132 | layout = wibox.layout.fixed.vertical,
133 | _N.body(n),
134 | {
135 | -- Add extra spacing to avoid having it look weird.
136 | widget = wibox.container.margin,
137 | margins = { top = dpi(4) },
138 | -- This, however, makes you have to hide the spacing itself.
139 | visible = #n.actions > 0,
140 | _N.actions(n)
141 | }
142 | }
143 | }
144 | },
145 | nil,
146 | {
147 | layout = wibox.layout.fixed.vertical,
148 | {
149 | widget = wibox.container.background,
150 | bg = color.bg3,
151 | forced_height = dpi(1)
152 | },
153 | {
154 | -- Today I learnt setting a constraint on a progress bar makes it use its minimum
155 | -- required size. The number you input into width doesn't matter.
156 | widget = wibox.container.constraint,
157 | strategy = 'max',
158 | width = 0,
159 | timeout_bar
160 | }
161 | }
162 | })
163 |
164 | local iconbox = wibox.widget({
165 | widget = wibox.container.margin,
166 | margins = dpi(8),
167 | {
168 | widget = wibox.container.constraint,
169 | strategy = 'exact',
170 | width = dpi(45),
171 | height = dpi(45),
172 | _N.icon(n)
173 | }
174 | })
175 |
176 | local layout = naughty.layout.box({
177 | notification = n,
178 | cursor = 'hand2',
179 | border_width = 0,
180 | widget_template = {
181 | widget = wibox.container.constraint,
182 | strategy = 'max',
183 | height = dpi(320),
184 | width = dpi(360),
185 | {
186 | widget = wibox.container.constraint,
187 | strategy = 'min',
188 | width = dpi(120),
189 | {
190 | widget = wibox.container.background,
191 | bg = color.bg0,
192 | border_width = dpi(1),
193 | border_color = color.bg3,
194 | {
195 | layout = wibox.layout.fixed.horizontal,
196 | iconbox,
197 | {
198 | widget = wibox.container.background,
199 | bg = color.bg3,
200 | forced_width = dpi(1)
201 | },
202 | {
203 | layout = wibox.layout.fixed.vertical,
204 | titlebox,
205 | contentbox
206 | }
207 | }
208 | }
209 | }
210 | }
211 | })
212 | -- For some reason, doing this inside the `layout` declaration just doesn't work. You
213 | -- have to do it imperatively or it'll literally just get ignored.
214 | layout.buttons = {}
215 |
216 | -- Create an animation for the timeout.
217 | local anim = require('module.rubato').timed({
218 | intro = 0,
219 | duration = timeout,
220 | subscribed = function(pos, time)
221 | timeout_bar.value = pos
222 | if time == timeout then
223 | n:destroy()
224 | collectgarbage('collect')
225 | end
226 | end
227 | })
228 | -- Stop the timeout on notification hover.
229 | layout:connect_signal('mouse::enter', function() anim.pause = true end)
230 | layout:connect_signal('mouse::leave', function() anim.pause = false end)
231 | anim.target = 100
232 |
233 | return layout
234 | end
235 |
--------------------------------------------------------------------------------
/ui/osd/init.lua:
--------------------------------------------------------------------------------
1 | local path = ... .. '.'
2 |
3 | return function(s)
4 | return {
5 | volume = require(path .. '.volume')(s),
6 | player = require(path .. '.player')(s)
7 | }
8 | end
9 |
--------------------------------------------------------------------------------
/ui/osd/player.lua:
--------------------------------------------------------------------------------
1 | local require, math, string, awesome = require, math, string, awesome
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local color = require(beautiful.colorscheme)
11 | local pctl = require('signal.system.playerctl')
12 | local widget = require('widget')
13 | local icons = require('theme.icons')
14 | local user = require('config.user')
15 |
16 | local width, height, timeout = 270, 120, 3
17 | local ratio = width / (height - 24)
18 |
19 | return function(s)
20 | local _W = {}
21 |
22 | _W.cover = wibox.widget.imagebox()
23 | if not user.lite or user.lite == nil then
24 | _W.cover.image = gears.surface.crop_surface({
25 | ratio = ratio,
26 | surface = beautiful.wallpaper
27 | })
28 | end
29 |
30 | _W.icon = widget.textbox.colored({
31 | text = icons['music'],
32 | font = icons.font .. icons.size,
33 | color = color.fg1
34 | })
35 | _W.player = widget.textbox.colored({
36 | text = 'N/A',
37 | color = color.fg1,
38 | align = 'right'
39 | })
40 | _W.title = widget.textbox.scrolling({
41 | text = 'Nothing Playing',
42 | font = beautiful.font_mono .. beautiful.bitm_size
43 | })
44 | _W.artist = widget.textbox.scrolling({
45 | text = 'by Unknown',
46 | font = beautiful.font_mono .. beautiful.bitm_size,
47 | color = color.fg1
48 | })
49 | _W.album = widget.textbox.scrolling({
50 | text = 'on N/A',
51 | font = beautiful.font_mono .. beautiful.bitm_size,
52 | color = color.fg2
53 | })
54 | _W.progress = widget.textbox.colored({
55 | text = '00:00 / 00:00',
56 | color = color.fg1
57 | })
58 | _W.status = wibox.widget({
59 | widget = wibox.container.constraint,
60 | {
61 | layout = wibox.layout.fixed.horizontal,
62 | spacing = dpi(8),
63 | {
64 | widget = widget.textbox.colored({
65 | text = icons['music_pause'],
66 | font = icons.font .. icons.size,
67 | align = 'right'
68 | }),
69 | id = 'icon'
70 | },
71 | {
72 | widget = widget.textbox.colored({
73 | text = 'Paused',
74 | align = 'right'
75 | }),
76 | id = 'text'
77 | }
78 | },
79 | set_icon = function(self, icon)
80 | self:get_children_by_id('icon')[1].text = icon
81 | end,
82 | set_status = function(self, text)
83 | self:get_children_by_id('text')[1].text = text
84 | end
85 | })
86 | _W.volume = wibox.widget({
87 | widget = wibox.widget.progressbar,
88 | background_color = color.bg1,
89 | color = color.fg0,
90 | margins = {
91 | left = dpi(9), right = dpi(9),
92 | top = dpi(4), bottom = dpi(4)
93 | }
94 | })
95 | _W.volume_level = widget.textbox.colored({
96 | text = icons['audio_muted'],
97 | font = icons.font .. icons.size,
98 | align = 'center'
99 | })
100 | _W.volume_label = widget.textbox.colored({
101 | text = 'N/A'
102 | })
103 |
104 | local osd = wibox({
105 | height = height,
106 | width = width,
107 | screen = s,
108 | bg = color.bg0,
109 | ontop = true,
110 | visible = false,
111 | border_width = dpi(1),
112 | border_color = color.bg3,
113 | widget = {
114 | layout = wibox.layout.fixed.vertical,
115 | {
116 | layout = wibox.layout.stack,
117 | _W.cover,
118 | {
119 | widget = wibox.container.background,
120 | bg = {
121 | type = 'linear',
122 | from = { 0, 0 },
123 | to = { dpi(width), 0 },
124 | stops = {
125 | { 0, color.bg0 .. 'EF' }, { 0.45, color.bg0 .. 'EF' },
126 | { 0.73, color.bg0 .. 'CC' }, { 1, color.bg0 .. 'A0' }
127 | }
128 | },
129 | {
130 | widget = wibox.container.margin,
131 | margins = dpi(12),
132 | {
133 | layout = wibox.layout.fixed.vertical,
134 | spacing = dpi(6),
135 | {
136 | layout = wibox.layout.align.horizontal,
137 | _W.icon, nil, _W.player
138 | },
139 | {
140 | layout = wibox.layout.fixed.vertical,
141 | _W.title,
142 | _W.artist,
143 | _W.album
144 | },
145 | {
146 | layout = wibox.layout.align.horizontal,
147 | _W.progress, nil, _W.status
148 | }
149 | }
150 | }
151 | }
152 | },
153 | {
154 | widget = wibox.container.background,
155 | bg = color.bg3,
156 | forced_height = dpi(1)
157 | },
158 | {
159 | widget = wibox.container.margin,
160 | margins = {
161 | top = dpi(6), bottom = dpi(6),
162 | left = dpi(12), right = dpi(12)
163 | },
164 | {
165 | layout = wibox.layout.align.horizontal,
166 | _W.volume_level,
167 | _W.volume,
168 | _W.volume_label
169 | }
170 | }
171 | }
172 | })
173 |
174 | local timer = gears.timer({
175 | timeout = timeout,
176 | single_shot = true,
177 | callback = function()
178 | osd.visible = false
179 | end
180 | })
181 |
182 | local function show()
183 | -- Hide all other OSDs if visible.
184 | awesome.emit_signal('osd::new', osd)
185 | -- Reset timer.
186 | if timer.started then
187 | timer:again()
188 | else
189 | osd.visible = true
190 | awful.placement.bottom(osd, { margins = { bottom = beautiful.useless_gap } })
191 | timer:start()
192 | end
193 | end
194 |
195 | pctl:connect_signal('metadata', function(_, title, artist, cover, album, new, source)
196 | -- Update OSD.
197 | _W.player.text = (source or 'Unknown player')
198 | _W.title.text = gears.string.xml_unescape(title) or 'Unknown'
199 | _W.artist.text = 'by ' .. (gears.string.xml_unescape(artist) or 'Unknown')
200 | _W.album.text = 'on ' .. (gears.string.xml_unescape(album) or 'Unknown')
201 | -- Update cover only if desired.
202 | if not user.lite or user.lite == nil then
203 | _W.cover.image = gears.surface.crop_surface({
204 | surface = gears.surface.load_uncached(cover or beautiful.wallpaper),
205 | ratio = ratio
206 | })
207 | end
208 | -- GC old album covers.
209 | collectgarbage('collect')
210 |
211 | -- Show the OSD when a new song comes through.
212 | if new and not s.dash.visible then show() end
213 | end)
214 |
215 | pctl:connect_signal('playback_status', function(_, playing, _)
216 | -- Update OSD.
217 | if playing then
218 | _W.status.icon = icons['music_play']
219 | _W.status.status = 'Playing'
220 | else
221 | _W.status.icon = icons['music_pause']
222 | _W.status.status = 'Paused'
223 | end
224 |
225 | if not s.dash.visible then show() end
226 | end)
227 |
228 | pctl:connect_signal('volume', function(_, volume, _)
229 | _W.volume.value = volume
230 | volume = volume * 100
231 | _W.volume_label.text = volume .. '%'
232 | if volume == 0 then
233 | _W.volume_level.text = icons['audio_muted']
234 | elseif volume < 50 then
235 | _W.volume_level.text = icons['audio_decrease']
236 | else
237 | _W.volume_level.text = icons['audio_increase']
238 | end
239 |
240 | if not s.dash.visible then show() end
241 | end)
242 |
243 | pctl:connect_signal('position', function(_, prog, len, _)
244 | _W.progress.text = string.format('%02d:%02d', math.floor(prog / 60), prog % 60)
245 | .. ' / ' .. string.format('%02d:%02d', math.floor(len / 60), len % 60)
246 | end)
247 |
248 | awesome.connect_signal('osd::new', function(new_osd)
249 | -- If the new osd is this one, do nothing.
250 | if new_osd == osd then return end
251 | -- Otherwise stop the timer and hide the osd if the timer is running.
252 | if timer.started then
253 | timer:stop()
254 | osd.visible = false
255 | end
256 | end)
257 |
258 | return osd
259 | end
260 |
--------------------------------------------------------------------------------
/ui/osd/volume.lua:
--------------------------------------------------------------------------------
1 | local require, awesome = require, awesome
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local color = require(beautiful.colorscheme)
11 | local audio = require('signal.system.audio')
12 | local widget = require('widget')
13 | local icons = require('theme.icons')
14 |
15 | local width, height, timeout = 200, 32, 3
16 |
17 | return function(s)
18 | local icon = widget.textbox.colored({
19 | text = icons['audio_muted'],
20 | font = icons.font .. icons.size,
21 | align = 'center'
22 | })
23 |
24 | local progress = wibox.widget({
25 | widget = wibox.widget.progressbar,
26 | background_color = color.bg1,
27 | color = color.fg0,
28 | max_value = 100,
29 | margins = {
30 | left = dpi(9), right = dpi(9),
31 | top = dpi(6), bottom = dpi(6)
32 | }
33 | })
34 |
35 | local label = wibox.widget({
36 | widget = wibox.widget.textbox,
37 | text = 'N/A'
38 | })
39 |
40 | local osd = wibox({
41 | x = (s.geometry.width - width) / 2,
42 | y = s.bar.height + beautiful.useless_gap,
43 | height = height,
44 | width = width,
45 | screen = s,
46 | bg = color.bg0,
47 | ontop = true,
48 | visible = false,
49 | border_width = dpi(1),
50 | border_color = color.bg3,
51 | widget = {
52 | widget = wibox.container.margin,
53 | margins = {
54 | left = dpi(12), right = dpi(12),
55 | top = dpi(9), bottom = dpi(9)
56 | },
57 | {
58 | layout = wibox.layout.align.horizontal,
59 | icon,
60 | progress,
61 | label
62 | }
63 | }
64 | })
65 |
66 | local timer = gears.timer({
67 | timeout = timeout,
68 | single_shot = true,
69 | callback = function()
70 | osd.visible = false
71 | end
72 | })
73 |
74 | local old = { mute = nil, level = nil, fresh = true }
75 | audio:connect_signal('sinks::default', function(_, default_sink)
76 | -- Sometimes, pactl gets pretty confused.
77 | if old.mute == default_sink.mute and old.level == default_sink.volume then
78 | return
79 | end
80 |
81 | -- Update OSD.
82 | if default_sink.mute or default_sink.volume == 0 then
83 | icon.text = icons['audio_muted']
84 | elseif default_sink.volume >= 50 then
85 | icon.text = icons['audio_increase']
86 | else
87 | icon.text = icons['audio_decrease']
88 | end
89 | progress.value = default_sink.volume
90 | label.text = default_sink.volume .. '%'
91 | -- Update reference values.
92 | old.mute = default_sink.mute
93 | old.level = default_sink.volume
94 |
95 | -- Prevents the OSD from being shown when interacting with dashboard sliders.
96 | if s.dash.visible then return end
97 | -- Prevents the OSD from being shown on startup.
98 | if old.fresh then
99 | old.fresh = false
100 | return
101 | end
102 | -- Hide all other OSDs if visible.
103 | awesome.emit_signal('osd::new', osd)
104 | -- Reset timer.
105 | if timer.started then
106 | timer:again()
107 | else
108 | osd.visible = true
109 | awful.placement.bottom(osd, { margins = { bottom = beautiful.useless_gap } })
110 | timer:start()
111 | end
112 | end)
113 |
114 | awesome.connect_signal('osd::new', function(new_osd)
115 | -- If the new osd is this one, do nothing.
116 | if new_osd == osd then return end
117 | -- Otherwise stop the timer and hide the osd if the timer is running.
118 | if timer.started then
119 | timer:stop()
120 | osd.visible = false
121 | end
122 | end)
123 |
124 | return osd
125 | end
126 |
--------------------------------------------------------------------------------
/ui/scratch/init.lua:
--------------------------------------------------------------------------------
1 | -- If you came here looking to fix something, please know that the bling
2 | -- scratchpad module is pretty buggy and I didn't do anything! :P
3 |
4 | return {
5 | music = require(... .. '.music')
6 | }
7 |
--------------------------------------------------------------------------------
/ui/scratch/music.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 | local dpi = require('beautiful').xresources.apply_dpi
5 |
6 | local apps = require('config.apps')
7 | local scratch = require('module.bling').module.scratchpad
8 |
9 | local s = awful.screen.focused()
10 | local h = dpi(400)
11 | local w = dpi(600)
12 |
13 | return scratch({
14 | -- `autoclose` is the buggiest shit on earth.
15 | command = apps.terminal .. ' --role="musicpad" -e ncmpcpp',
16 | rule = { role = 'musicpad' },
17 | sticky = true,
18 | floating = true,
19 | geometry = {
20 | height = h, width = w,
21 | x = (s.geometry.width - w) / 2,
22 | y = (s.geometry.height - h) / 2 + s.bar.height
23 | }
24 | })
25 |
--------------------------------------------------------------------------------
/ui/time/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local beautiful = require('beautiful')
4 | local wibox = require('wibox')
5 |
6 | local dpi = beautiful.xresources.apply_dpi
7 |
8 | local color = require(beautiful.colorscheme)
9 | local mods = require('ui.time.module')
10 |
11 | local width, height, margin = dpi(300), dpi(370), dpi(6)
12 |
13 | return function(s)
14 | local panel = wibox({
15 | ontop = true,
16 | visible = false,
17 | width = width,
18 | height = height,
19 | x = s.geometry.width - (margin + width),
20 | y = margin + s.bar.height,
21 | bg = color.bg0,
22 | border_width = dpi(1),
23 | border_color = color.bg3,
24 | widget = {
25 | widget = wibox.container.margin,
26 | margins = dpi(16),
27 | {
28 | layout = wibox.layout.fixed.vertical,
29 | spacing = dpi(16),
30 | mods.clock(),
31 | {
32 | widget = wibox.container.background,
33 | bg = color.bg3,
34 | forced_height = dpi(1)
35 | },
36 | {
37 | widget = wibox.container.constraint,
38 | strategy = 'exact',
39 | height = dpi(270),
40 | mods.calendar.main_widget
41 | },
42 | mods.weather()
43 | }
44 | }
45 | })
46 |
47 | function panel:hide()
48 | self.visible = false
49 | end
50 |
51 | function panel:show()
52 | s.dash:hide()
53 | self.visible = true
54 | end
55 |
56 | function panel:toggle()
57 | if self.visible then
58 | self:hide()
59 | else
60 | self:show()
61 | end
62 | end
63 |
64 | local grown = false
65 | require('signal.system.weather'):connect_signal('weather::data', function()
66 | if not grown then
67 | panel:geometry({
68 | x = panel.x, y = panel.y, width = panel.width,
69 | height = panel.height + dpi(200)
70 | })
71 | grown = true
72 | end
73 | end)
74 |
75 | return panel
76 | end
77 |
--------------------------------------------------------------------------------
/ui/time/module/calendar.lua:
--------------------------------------------------------------------------------
1 | -- From Myagko, see:
2 | -- https://github.com/myagko/dotfiles/blob/0122545e8245d11852fb6785be8fc72c41928574/home/.config/awesome/ui/calendar.lua
3 | local require, math, os = require, math, os
4 |
5 | local awful = require("awful")
6 | local wibox = require("wibox")
7 | local beautiful = require("beautiful")
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local color = require(beautiful.colorscheme)
11 | local icons = require('theme.icons')
12 |
13 | local calendar = {}
14 | local instance = nil
15 |
16 | local hebr_format = {
17 | [1] = 7,
18 | [2] = 1,
19 | [3] = 2,
20 | [4] = 3,
21 | [5] = 4,
22 | [6] = 5,
23 | [7] = 6
24 | }
25 |
26 | local wday_map = {
27 | ['Mon'] = 'Mo',
28 | ['Tue'] = 'Tu',
29 | ['Wed'] = 'We',
30 | ['Thu'] = 'Th',
31 | ['Fri'] = 'Fr',
32 | ['Sat'] = 'Sa',
33 | ['Sun'] = 'Su'
34 | }
35 |
36 | local function create_wday_widget(wday, col)
37 | local fg_color = col or color.fg0
38 | return wibox.widget {
39 | widget = wibox.container.background,
40 | fg = fg_color,
41 | {
42 | widget = wibox.container.margin,
43 | margins = dpi(10),
44 | {
45 | widget = wibox.widget.textbox,
46 | align = "center",
47 | text = wday
48 | }
49 | }
50 | }
51 | end
52 |
53 | local function create_day_widget(day, is_current, is_another_month)
54 | local fg_color = color.fg0
55 | local bg_color = color.bg1
56 |
57 | if is_current then
58 | fg_color = color.bg0
59 | bg_color = color.accent
60 | elseif is_another_month then
61 | fg_color = color.fg2
62 | bg_color = color.bg1 .. '80' -- TODO
63 | end
64 |
65 | return wibox.widget {
66 | widget = wibox.container.background,
67 | fg = fg_color,
68 | bg = bg_color,
69 | {
70 | widget = wibox.container.margin,
71 | margins = dpi(10),
72 | {
73 | widget = wibox.widget.textbox,
74 | halign = "center",
75 | valign = "center",
76 | text = day
77 | }
78 | }
79 | }
80 | end
81 |
82 | local function button(icon, action)
83 | local widget = wibox.widget({
84 | widget = wibox.container.background,
85 | bg = color.bg1,
86 | buttons = {
87 | awful.button({}, 1, action)
88 | },
89 | {
90 | widget = wibox.container.margin,
91 | margins = {
92 | top = dpi(6), bottom = dpi(4),
93 | left = dpi(8), right = dpi(8)
94 | },
95 | {
96 | widget = wibox.widget.textbox,
97 | font = icons.font .. icons.size,
98 | text = icons[icon],
99 | halign = 'center',
100 | valign = 'center'
101 | }
102 | }
103 | })
104 | widget:connect_signal('mouse::enter', function(self)
105 | self.bg = color.bg2 .. '80'
106 | self.fg = color.accent
107 | end)
108 | widget:connect_signal('mouse::leave', function(self)
109 | self.bg = color.bg1
110 | self.fg = color.fg0
111 | end)
112 |
113 | return widget
114 | end
115 |
116 | function calendar:set(date)
117 | calendar.day_layout:reset()
118 | self.date = date
119 |
120 | local curr_date = os.date("*t")
121 | local firstday = os.date("*t", os.time({ year = date.year, month = date.month, day = 1 }))
122 | local lastday = os.date("*t", os.time({ year = date.year, month = date.month + 1, day = 0 }))
123 |
124 | local month_count = lastday.day
125 | local month_start = not self.sun_start and hebr_format[firstday.wday] or firstday.wday
126 | local rows = math.max(5, math.min(6, 5 - (36 - (month_start + month_count))))
127 |
128 | local month_prev_lastday = os.date("*t", os.time({ year = date.year, month = date.month, day = 0 })).day
129 | local month_prev_count = month_start - 1
130 | local month_next_count = rows*7 - lastday.day - month_prev_count
131 |
132 | self.top_widget.title = os.date("%B %Y", os.time(date))
133 |
134 | for day = month_prev_lastday - (month_prev_count - 1), month_prev_lastday, 1 do
135 | self.day_layout:add(create_day_widget(day, false, true))
136 | end
137 |
138 | for day = 1, month_count, 1 do
139 | local is_current = day == curr_date.day and date.month == curr_date.month and date.year == curr_date.year
140 | self.day_layout:add(create_day_widget(day, is_current, false))
141 | end
142 |
143 | for day = 1, month_next_count, 1 do
144 | self.day_layout:add(create_day_widget(day, false, true))
145 | end
146 | end
147 |
148 | function calendar:inc(dir)
149 | local new_calendar_month = self.date.month + dir
150 | self:set({ year = self.date.year, month = new_calendar_month, day = self.date.day })
151 | end
152 |
153 | local function new()
154 | local ret = calendar
155 |
156 |
157 | ret.sun_start = false
158 |
159 | ret.day_layout = wibox.widget {
160 | layout = wibox.layout.grid,
161 | forced_num_cols = 7,
162 | expand = true,
163 | forced_height = dpi(230)
164 | }
165 |
166 | ret.wday_layout = wibox.widget {
167 | layout = wibox.layout.flex.horizontal
168 | }
169 |
170 | for i = 1, 7 do
171 | if ret.sun_start then
172 | i = i - 1
173 | if i > 0 and i < 6 then
174 | ret.wday_layout:add(create_wday_widget(wday_map[os.date("%a", os.time({year = 1, month = 1, day = i}))]))
175 | else
176 | ret.wday_layout:add(create_wday_widget(wday_map[os.date("%a", os.time({year = 1, month = 1, day = i}))], color.red))
177 | end
178 | else
179 | if i < 6 then
180 | ret.wday_layout:add(create_wday_widget(wday_map[os.date("%a", os.time({year = 1, month = 1, day = i}))]))
181 | else
182 | ret.wday_layout:add(create_wday_widget(wday_map[os.date("%a", os.time({year = 1, month = 1, day = i}))], color.red))
183 | end
184 | end
185 | end
186 |
187 | local title_text = wibox.widget({
188 | widget = wibox.widget.textbox,
189 | halign = "center",
190 | valign = "center"
191 | })
192 | local title = wibox.widget({
193 | widget = wibox.container.background,
194 | buttons = {
195 | awful.button({}, 1, function()
196 | ret:set(os.date("*t"))
197 | end)
198 | },
199 | title_text
200 | })
201 | title:connect_signal('mouse::enter', function(self)
202 | self.fg = color.accent
203 | end)
204 | title:connect_signal('mouse::leave', function(self)
205 | self.fg = color.fg0
206 | end)
207 |
208 | ret.top_widget = wibox.widget {
209 | widget = wibox.container.background,
210 | bg = color.bg1,
211 | {
212 | layout = wibox.layout.align.horizontal,
213 | {
214 | widget = wibox.container.margin,
215 | margins = {
216 | top = dpi(8), bottom = dpi(6),
217 | left = dpi(10), right = dpi(10)
218 | },
219 | title
220 | },
221 | nil,
222 | {
223 | widget = wibox.layout.fixed.horizontal,
224 | {
225 | widget = wibox.container.background,
226 | bg = color.bg3,
227 | forced_width = dpi(1)
228 | },
229 | button('arrow_left', function() ret:inc(-1) end),
230 | {
231 | widget = wibox.container.background,
232 | bg = color.bg3,
233 | forced_width = dpi(1)
234 | },
235 | button('arrow_right', function() ret:inc(1) end)
236 | }
237 | },
238 | set_title = function(_, text)
239 | title_text.text = text
240 | end
241 | }
242 |
243 | ret.main_widget = wibox.widget {
244 | widget = wibox.container.background,
245 | bg = color.bg0,
246 | border_width = dpi(1),
247 | border_color = color.bg3,
248 | {
249 | layout = wibox.layout.align.vertical,
250 | {
251 | layout = wibox.layout.fixed.vertical,
252 | ret.top_widget,
253 | {
254 | widget = wibox.container.background,
255 | bg = color.bg3,
256 | forced_height = dpi(1)
257 | }
258 | },
259 | nil,
260 | {
261 | widget = wibox.container.margin,
262 | margins = {
263 | top = dpi(8), bottom = dpi(16),
264 | left = dpi(16), right = dpi(16)
265 | },
266 | {
267 | layout = wibox.layout.fixed.vertical,
268 | spacing = dpi(5),
269 | ret.wday_layout,
270 | ret.day_layout
271 | }
272 | }
273 | }
274 | }
275 |
276 | ret:set(os.date("*t"))
277 |
278 | return ret
279 | end
280 |
281 | if not instance then
282 | instance = new()
283 | end
284 |
285 | return instance
286 |
--------------------------------------------------------------------------------
/ui/time/module/clock.lua:
--------------------------------------------------------------------------------
1 | local require, os = require, os
2 |
3 | local beautiful = require('beautiful')
4 | local wibox = require('wibox')
5 |
6 | local helpers = require('helpers')
7 | local widget = require('widget')
8 | local color = require(beautiful.colorscheme)
9 |
10 | return function()
11 | local hour = wibox.widget({
12 | widget = wibox.widget.textclock,
13 | format = '%H:%M:%S',
14 | refresh = 1,
15 | font = beautiful.font_bitm .. beautiful.bitm_size * 2,
16 | halign = 'center'
17 | })
18 |
19 | local date = widget.textbox.colored({
20 | color = color.fg2,
21 | align = 'center'
22 | })
23 | require('gears').timer({
24 | timeout = 60,
25 | call_now = true,
26 | autostart = true,
27 | callback = function()
28 | local day = tonumber(os.date('%e'))
29 | date.text =
30 | os.date('%A, the ') .. day .. helpers.get_suffix(day) .. os.date(' of %B')
31 | end
32 | })
33 |
34 | return wibox.widget({
35 | layout = wibox.layout.fixed.vertical,
36 | hour, date
37 | })
38 | end
39 |
--------------------------------------------------------------------------------
/ui/time/module/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 | local path = ... .. '.'
3 | return setmetatable({}, {
4 | __index = function(_, key)
5 | local module, _ = require(path .. key)
6 | return module
7 | end
8 | })
9 |
--------------------------------------------------------------------------------
/ui/time/module/weather.lua:
--------------------------------------------------------------------------------
1 | local require, string, os, table, ipairs = require, string, os, table, ipairs
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local imperial = require('config.user').imperial
10 | local weather = require('signal.system.weather')
11 | local widget = require('widget')
12 | local color = require(beautiful.colorscheme)
13 | local icons = require('theme.icons')
14 |
15 | return function()
16 | -- Current weather widgets!
17 | local _C = {}
18 | _C.icon = widget.textbox.colored({
19 | text = icons.weather['day_clear'],
20 | font = icons.font .. icons.size * 12,
21 | color = color.bg3 .. '80'
22 | })
23 | _C.desc = widget.textbox.colored({
24 | text = 'No weather info',
25 | font = beautiful.font_bitm .. beautiful.bitm_size * 2
26 | })
27 | _C.humy = widget.textbox.colored({
28 | text = 'Humidity: N/A',
29 | color = color.fg2
30 | })
31 | _C.temp = widget.textbox.colored({
32 | text = 'N/A',
33 | align = 'right',
34 | font = beautiful.font_bitm .. beautiful.bitm_size * 2
35 | })
36 | _C.feel = widget.textbox.colored({
37 | text = 'N/A',
38 | align = 'right',
39 | color = color.fg2
40 | })
41 |
42 | -- Hourly weather widgets!
43 | local _H = {}
44 | local hour_widgets = wibox.widget({
45 | layout = wibox.layout.flex.horizontal,
46 | spacing = dpi(8)
47 | })
48 |
49 | local function hourly(index)
50 | local time = widget.textbox.colored({
51 | text = '+' .. string.format('%02d', index) .. ':00',
52 | font = beautiful.font_mono .. beautiful.bitm_size,
53 | align = 'center',
54 | color = color.fg1
55 | })
56 | local icon = widget.textbox.colored({
57 | text = icons.weather['day_clear'],
58 | font = icons.font .. icons.size * 2,
59 | align = 'center'
60 | })
61 | local temp = widget.textbox.colored({
62 | text = 'N/A',
63 | font = beautiful.font_mono .. beautiful.bitm_size,
64 | align = 'center'
65 | })
66 | local rain = widget.textbox.colored({
67 | text = 'N/A',
68 | font = beautiful.font_mono .. beautiful.bitm_size,
69 | align = 'center',
70 | color = color.fg2
71 | })
72 |
73 | return wibox.widget({
74 | layout = wibox.layout.fixed.vertical,
75 | time, icon, temp, rain,
76 | set_time = function(_, t)
77 | time.text = t
78 | end,
79 | set_icon = function(_, i)
80 | icon.text = i
81 | end,
82 | set_temp = function(_, t)
83 | temp.text = t
84 | end,
85 | set_rain = function(_, h)
86 | rain.text = h
87 | end
88 | })
89 | end
90 |
91 | for i = 1, 6, 1 do
92 | local w = hourly(i)
93 | table.insert(_H, w)
94 | hour_widgets:add(w)
95 | end
96 |
97 | -- Daily weather widgets!
98 | local _D = {}
99 | local day_widgets = wibox.widget({
100 | layout = wibox.layout.flex.horizontal,
101 | visible = false,
102 | spacing = dpi(16)
103 | })
104 | local weekdays = {
105 | 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
106 | }
107 |
108 | local function daily(index)
109 | local time = widget.textbox.colored({
110 | text = '+' .. index,
111 | font = beautiful.font_mono .. beautiful.bitm_size,
112 | color = color.fg1
113 | })
114 | local icon = widget.textbox.colored({
115 | text = icons.weather['day_clear'],
116 | font = icons.font .. icons.size * 4
117 | })
118 | local max = widget.textbox.colored({
119 | text = 'N/A',
120 | font = beautiful.font_mono .. beautiful.bitm_size
121 | })
122 | local rain = widget.textbox.colored({
123 | text = 'N/A',
124 | font = beautiful.font_mono .. beautiful.bitm_size
125 | })
126 | local humy = widget.textbox.colored({
127 | text = 'N/A',
128 | font = beautiful.font_mono .. beautiful.bitm_size,
129 | color = color.fg2
130 | })
131 | local min = widget.textbox.colored({
132 | text = 'N/A',
133 | font = beautiful.font_mono .. beautiful.bitm_size,
134 | color = color.fg2
135 | })
136 |
137 | return wibox.widget({
138 | widget = wibox.container.margin,
139 | margins = { top = dpi(1), bottom = dpi(2) },
140 | {
141 | layout = wibox.layout.fixed.horizontal,
142 | spacing = dpi(9),
143 | icon,
144 | {
145 | layout = wibox.layout.fixed.vertical,
146 | spacing = dpi(2),
147 | time,
148 | {
149 | layout = wibox.layout.fixed.vertical,
150 | {
151 | layout = wibox.layout.fixed.horizontal,
152 | widget.textbox.colored({
153 | text = 'T: ',
154 | color = color.red
155 | }), max, wibox.widget.textbox('/'), min,
156 | wibox.widget.textbox(imperial and '°F' or '°C')
157 | },
158 | {
159 | layout = wibox.layout.fixed.horizontal,
160 | widget.textbox.colored({
161 | text = 'H: ',
162 | color = color.yellow
163 | }), humy
164 | },
165 | {
166 | layout = wibox.layout.fixed.horizontal,
167 | widget.textbox.colored({
168 | text = 'R: ',
169 | color = color.blue
170 | }), rain
171 | }
172 | }
173 | }
174 | },
175 | set_time = function(_, d)
176 | d = d + index + 1
177 | if d > 7 then
178 | d = d - 7
179 | end
180 | time.text = weekdays[d]
181 | end,
182 | set_icon = function(_, i)
183 | icon.text = i
184 | end,
185 | set_max = function(_, t)
186 | max.text = t:gsub('°[C,F]', '')
187 | end,
188 | set_min = function(_, t)
189 | min.text = t:gsub('°[C,F]', '')
190 | end,
191 | set_rain = function(_, r)
192 | rain.text = r
193 | end,
194 | set_humy = function(_, r)
195 | humy.text = r
196 | end
197 | })
198 | end
199 |
200 | for i = 1, 2, 1 do
201 | local w = daily(i)
202 | table.insert(_D, w)
203 | day_widgets:add(w)
204 | end
205 |
206 | -- Switch between hourly and daily forecast.
207 | local hour_toggle = wibox.widget({
208 | widget = wibox.container.background,
209 | bg = color.bg2,
210 | {
211 | widget = wibox.container.margin,
212 | margins = {
213 | top = dpi(4), bottom = dpi(4),
214 | left = dpi(8), right = dpi(8)
215 | },
216 | {
217 | widget = widget.textbox.colored({
218 | text = 'By Hour',
219 | align = 'center'
220 | }),
221 | id = 'text'
222 | }
223 | },
224 | set_col = function(self, col)
225 | self:get_children_by_id('text')[1].color = col
226 | end
227 | })
228 | local day_toggle = wibox.widget({
229 | widget = wibox.container.background,
230 | {
231 | widget = wibox.container.margin,
232 | margins = {
233 | top = dpi(4), bottom = dpi(4),
234 | left = dpi(8), right = dpi(8)
235 | },
236 | {
237 | widget = widget.textbox.colored({
238 | text = 'By Day',
239 | align = 'center',
240 | color = color.fg2
241 | }),
242 | id = 'text'
243 | }
244 | },
245 | set_col = function(self, col)
246 | self:get_children_by_id('text')[1].color = col
247 | end
248 | })
249 | local toggle = wibox.widget({
250 | widget = wibox.container.background,
251 | bg = color.bg1,
252 | border_width = dpi(1),
253 | border_color = color.bg3,
254 | {
255 | layout = wibox.layout.fixed.horizontal,
256 | {
257 | widget = hour_toggle,
258 | buttons = {
259 | awful.button(nil, 1, function()
260 | hour_widgets.visible = true
261 | day_widgets.visible = false
262 | hour_toggle.bg = color.bg2
263 | hour_toggle.col = color.fg0
264 | day_toggle.bg = color.bg2 .. '80'
265 | day_toggle.col = color.fg2
266 | end)
267 | }
268 | },
269 | {
270 | widget = wibox.container.background,
271 | bg = color.bg3,
272 | forced_width = dpi(1)
273 | },
274 | {
275 | widget = day_toggle,
276 | buttons = {
277 | awful.button(nil, 1, function()
278 | day_widgets.visible = true
279 | hour_widgets.visible = false
280 | day_toggle.bg = color.bg2
281 | day_toggle.col = color.fg0
282 | hour_toggle.bg = color.bg2 .. '80'
283 | hour_toggle.col = color.fg2
284 | end)
285 | }
286 | }
287 | }
288 | })
289 |
290 | local w = wibox.widget({
291 | widget = wibox.container.background,
292 | bg = color.bg1,
293 | border_width = dpi(1),
294 | border_color = color.bg3,
295 | visible = false,
296 | {
297 | widget = wibox.container.margin,
298 | margins = dpi(16),
299 | {
300 | widget = wibox.layout.stack,
301 | _C.icon,
302 | {
303 | layout = wibox.layout.fixed.vertical,
304 | spacing = dpi(12),
305 | {
306 | layout = wibox.layout.align.horizontal,
307 | {
308 | layout = wibox.layout.fixed.vertical,
309 | _C.desc,
310 | _C.humy
311 | },
312 | nil,
313 | {
314 | layout = wibox.layout.fixed.vertical,
315 | _C.temp,
316 | _C.feel
317 | }
318 | },
319 | {
320 | layout = wibox.layout.align.horizontal,
321 | expand = 'none',
322 | nil, nil, toggle
323 | },
324 | {
325 | layout = wibox.layout.align.horizontal,
326 | expand = 'none',
327 | nil,
328 | {
329 | layout = wibox.layout.stack,
330 | hour_widgets,
331 | day_widgets
332 | },
333 | nil
334 | }
335 | }
336 | }
337 | }
338 | })
339 |
340 | -- Global signals won't cut it, they get emitted before this widget is even drawn.
341 | weather:connect_signal('weather::data', function(_, info)
342 | w.visible = true
343 |
344 | -- Current.
345 | _C.desc.text = info.description
346 | _C.humy.text = 'Humidity: ' .. info.humidity
347 | _C.temp.text = info.temperature
348 | _C.feel.text = info.feels_like
349 | _C.icon.text = icons.weather[info.icon]
350 |
351 | -- Hourly.
352 | -- Widget can only hold 6 at a time, make sure they're relevant!
353 | local off = 0
354 | if #info.by_hour > #_H then
355 | local curr_h = tonumber(os.date('%H'))
356 | for i = 1, #info.by_hour - #_H, 1 do
357 | if curr_h > (i * 24 / #info.by_hour) then
358 | off = off + 1
359 | end
360 | end
361 | end
362 | for i, h in ipairs(_H) do
363 | local hour = info.by_hour[i + off]
364 | h.time = hour.time
365 | h.icon = icons.weather[hour.icon]
366 | h.temp = hour.temperature
367 | h.rain = hour.rain_chance
368 | end
369 |
370 | -- Daily.
371 | for i, d in ipairs(_D) do
372 | d.time = tonumber(os.date('%w'))
373 | d.icon = icons.weather[info.by_day[i].icon]
374 | d.max = info.by_day[i].max_temp
375 | d.min = info.by_day[i].min_temp
376 | d.rain = info.by_day[i].rain_chance
377 | d.humy = info.by_day[i].humidity
378 | end
379 | end)
380 |
381 | -- Since the panel isn't drawn from the get-go, it may fail to catch the first emision
382 | -- of the `weather::data` signal. I opted to make the widget request a new emision
383 | -- when drawn for the first time.
384 | weather:request_data()
385 |
386 | return w
387 | end
388 |
--------------------------------------------------------------------------------
/ui/titlebar/init.lua:
--------------------------------------------------------------------------------
1 | -- Returns titlebars for normal clients, this structure allows one to
2 | -- easily define special titlebars for particular clients.
3 | return {
4 | normal = require(... .. '.normal')
5 | }
6 |
--------------------------------------------------------------------------------
/ui/titlebar/normal.lua:
--------------------------------------------------------------------------------
1 | local require, client = require, client
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local tabbed = require('module.bling').widget.tabbed_misc
11 | local widget = require('widget')
12 | local color = require(beautiful.colorscheme)
13 | local icons = require('theme.icons')
14 |
15 | --- The titlebar to be used on normal clients.
16 | return function(c)
17 | local function button(icon, hover, action)
18 | local w = wibox.widget({
19 | widget = wibox.container.background,
20 | bg = color.bg2 .. '90',
21 | border_width = dpi(1),
22 | border_color = color.bg3,
23 | {
24 | widget = wibox.container.margin,
25 | margins = { left = dpi(5), right = dpi(5) },
26 | {
27 | widget = widget.textbox.colored({
28 | text = icon,
29 | font = icons.font .. icons.size,
30 | align = 'center'
31 | }),
32 | id = 'image_role'
33 | }
34 | },
35 | buttons = { awful.button(nil, 1, action) },
36 | set_fg_col = function(self, fg)
37 | self:get_children_by_id('image_role')[1].color = fg
38 | end,
39 | set_bd_col = function(self, bd)
40 | self.border_color = bd
41 | end,
42 | set_bg_col = function(self, bg)
43 | self.bg = bg
44 | end
45 | })
46 |
47 | -- Changes the icons for a lighter version when the client is focused. Reverts on
48 | -- focus loss.
49 | client.connect_signal('property::active', function()
50 | if c.active then
51 | w.opacity = 1
52 | else
53 | w.opacity = 0.66
54 | end
55 | end)
56 |
57 | -- Adjust colors when hovering.
58 | w:connect_signal('mouse::enter', function(self)
59 | self.fg_col = hover
60 | self.bd_col = hover
61 | self.bg_col = color.bg2
62 | end)
63 | w:connect_signal('mouse::leave', function(self)
64 | self.fg_col = color.fg0
65 | self.bd_col = color.bg3
66 | self.bg_col = color.bg2 .. '90'
67 | end)
68 |
69 | return w
70 | end
71 |
72 | local tabs = tabbed.titlebar_indicator(c, {
73 | bg_color = color.bg1 .. '80',
74 | bg_color_focus = color.transparent,
75 | fg_color = color.fg0 .. 'AB',
76 | fg_color_focus = color.accent,
77 | layout = wibox.layout.flex.horizontal,
78 | layout_spacing = 0,
79 | widget_template = {
80 | widget = wibox.container.background,
81 | id = 'bg_role',
82 | {
83 | widget = wibox.container.margin,
84 | margins = {
85 | left = dpi(11), right = dpi(11),
86 | top = dpi(11), bottom = dpi(11)
87 | },
88 | {
89 | widget = wibox.widget.textbox,
90 | valign = 'center',
91 | id = 'text_role'
92 | }
93 | },
94 | create_callback = function(self, window, _)
95 | self.text = window.name
96 | end,
97 | update_callback = function(self, window, group)
98 | self.create_callback(self, window, group)
99 | end
100 | }
101 | })
102 |
103 | local top = wibox.widget({
104 | widget = wibox.container.background,
105 | bg = color.bg1,
106 | border_width = dpi(1),
107 | border_color = color.bg3,
108 | {
109 | layout = wibox.layout.align.horizontal,
110 | expand = 'outer',
111 | -- Left
112 | {
113 | layout = wibox.layout.fixed.horizontal,
114 | {
115 | widget = wibox.container.margin,
116 | margins = dpi(6),
117 | button(icons['title_pin'], color.accent,
118 | function()
119 | c.sticky = not c.sticky
120 | end
121 | )
122 | },
123 | {
124 | widget = wibox.container.background,
125 | bg = color.bg3,
126 | forced_width = dpi(1)
127 | }
128 | },
129 | -- Middle
130 | {
131 | widget = wibox.container.background,
132 | bg = color.bg0 .. '80',
133 | tabs
134 | },
135 | -- Right
136 | {
137 | layout = wibox.layout.fixed.horizontal,
138 | {
139 | widget = wibox.container.background,
140 | bg = color.bg3,
141 | forced_width = dpi(1)
142 | },
143 | {
144 | widget = wibox.container.margin,
145 | margins = dpi(7),
146 | {
147 | layout = wibox.layout.fixed.horizontal,
148 | spacing = dpi(2),
149 | button(icons['title_minimize'], color.accent,
150 | function()
151 | gears.timer.delayed_call(function()
152 | c.minimized = not c.minimized
153 | end)
154 | end
155 | ),
156 | button(icons['title_maximize'], color.accent,
157 | function()
158 | c.maximized = not c.maximized
159 | c:raise()
160 | end
161 | ),
162 | button(icons['title_close'], color.red, function() c:kill() end)
163 | }
164 | }
165 | }
166 | }
167 | })
168 |
169 | local bottom = wibox.widget({
170 | widget = wibox.container.background,
171 | bg = color.bg1,
172 | border_width = dpi(1),
173 | border_color = color.bg3,
174 | {
175 | layout = wibox.layout.fixed.horizontal,
176 | {
177 | widget = wibox.container.background,
178 | bg = color.transparent,
179 | forced_width = dpi(48),
180 | buttons = {
181 | awful.button(nil, 1, function()
182 | c:activate({ context = 'titlebar', action = 'mouse_move' })
183 | end),
184 | awful.button(nil, 3, function()
185 | c:activate({ context = 'titlebar', action = 'mouse_resize' })
186 | end)
187 | }
188 | },
189 | {
190 | widget = wibox.container.background,
191 | bg = color.bg3,
192 | forced_width = dpi(1)
193 | }
194 | }
195 | })
196 |
197 | local empty = wibox.widget({
198 | widget = wibox.container.background,
199 | bg = color.bg3
200 | })
201 |
202 | awful.titlebar(c, { position = 'top', size = dpi(33) }).widget = top
203 | awful.titlebar(c, { position = 'bottom', size = dpi(7) }).widget = bottom
204 | awful.titlebar(c, { position = 'left', size = dpi(1) }).widget = empty
205 | awful.titlebar(c, { position = 'right', size = dpi(1) }).widget = empty
206 | end
207 |
--------------------------------------------------------------------------------
/ui/wibar/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local color = require(beautiful.colorscheme)
10 | local module = require(... .. '.module')
11 |
12 | return function(s)
13 | -- Left widgets.
14 | local left = wibox.widget({
15 | layout = wibox.layout.fixed.horizontal,
16 | {
17 | widget = wibox.container.margin,
18 | margins = {
19 | top = dpi(6), bottom = dpi(6),
20 | right = dpi(16)
21 | },
22 | {
23 | layout = wibox.layout.fixed.horizontal,
24 | spacing = dpi(12),
25 | {
26 | layout = wibox.layout.fixed.horizontal,
27 | module.layoutbox(s),
28 | module.taglist(s)
29 | },
30 | module.launcher(s)
31 | }
32 | },
33 | {
34 | widget = wibox.container.background,
35 | bg = color.bg3,
36 | forced_width = dpi(1)
37 | }
38 | })
39 |
40 | -- Middle widgets.
41 | local center = wibox.widget({
42 | widget = wibox.container.background,
43 | bg = color.bg1 .. '80',
44 | {
45 | widget = wibox.container.margin,
46 | margins = {
47 | left = dpi(9), right = dpi(9),
48 | top = dpi(6), bottom = dpi(6)
49 | },
50 | module.tasklist(s)
51 | }
52 | })
53 |
54 | -- Right widgets.
55 | local right = wibox.widget({
56 | layout = wibox.layout.fixed.horizontal,
57 | {
58 | widget = wibox.container.background,
59 | bg = color.bg3,
60 | forced_width = dpi(1)
61 | },
62 | {
63 | widget = wibox.container.margin,
64 | margins = { left = dpi(12) },
65 | {
66 | layout = wibox.layout.fixed.horizontal,
67 | spacing = dpi(12),
68 | {
69 | widget = wibox.container.margin,
70 | margins = {
71 | top = dpi(6), bottom = dpi(6)
72 | },
73 | module.systray()
74 | },
75 | awful.widget.keyboardlayout(),
76 | {
77 | widget = wibox.container.margin,
78 | margins = {
79 | top = dpi(6), bottom = dpi(6)
80 | },
81 | {
82 | layout = wibox.layout.fixed.horizontal,
83 | spacing = dpi(12),
84 | module.status(),
85 | module.clock(s),
86 | module.dash(s)
87 | }
88 | }
89 | }
90 | }
91 | })
92 |
93 | -- Create the wibox
94 | return awful.wibar({
95 | position = 'top',
96 | height = dpi(36),
97 | screen = s,
98 | widget = {
99 | widget = wibox.container.background,
100 | bg = color.bg3,
101 | {
102 | widget = wibox.container.margin,
103 | margins = { bottom = dpi(1) },
104 | {
105 | widget = wibox.container.background,
106 | bg = color.bg0,
107 | {
108 | widget = wibox.container.margin,
109 | margins = { left = dpi(16), right = dpi(16) },
110 | {
111 | layout = wibox.layout.align.horizontal,
112 | expand = 'outer',
113 | left,
114 | center,
115 | right
116 | }
117 | }
118 | }
119 | }
120 | }
121 | })
122 | end
123 |
--------------------------------------------------------------------------------
/ui/wibar/module/clock.lua:
--------------------------------------------------------------------------------
1 | local require, os = require, os
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local widget = require('widget')
10 | local helpers = require('helpers')
11 | local color = require(beautiful.colorscheme)
12 | local icons = require('theme.icons')
13 | local weather = require('signal.system.weather')
14 |
15 | return function(s)
16 | -- A simple widget that shows the correct suffix for the current date.
17 | local day_suffix = wibox.widget({ widget = wibox.widget.textbox })
18 | require('gears').timer({
19 | timeout = 60,
20 | call_now = true,
21 | autostart = true,
22 | callback = function()
23 | local day = tonumber(os.date('%d'))
24 | day_suffix.markup = os.date('%B ') .. day .. helpers.get_suffix(day)
25 | end
26 | })
27 |
28 | local clock = wibox.widget({
29 | widget = wibox.container.background,
30 | fg = color.fg0,
31 | {
32 | layout = wibox.layout.fixed.horizontal,
33 | spacing = dpi(12),
34 | day_suffix,
35 | {
36 | widget = wibox.widget.textclock,
37 | format = '%H:%M'
38 | }
39 | },
40 | buttons = {
41 | awful.button(nil, 1, function()
42 | if s.time then s.time:toggle() end
43 | end)
44 | }
45 | })
46 | clock:connect_signal('mouse::enter', function(self)
47 | self.fg = color.accent
48 | end)
49 | clock:connect_signal('mouse::leave', function(self)
50 | self.fg = color.fg0
51 | end)
52 |
53 | -- Some compact weather information.
54 | local current_weather = wibox.widget({
55 | layout = wibox.layout.fixed.horizontal,
56 | spacing = dpi(9),
57 | visible = false,
58 | {
59 | widget = wibox.container.margin,
60 | margins = {
61 | top = dpi(5), bottom = dpi(5)
62 | },
63 | {
64 | widget = wibox.container.background,
65 | bg = color.bg2,
66 | forced_width = dpi(1)
67 | }
68 | },
69 | {
70 | layout = wibox.layout.fixed.horizontal,
71 | spacing = dpi(6),
72 | {
73 | widget = widget.textbox.colored({
74 | text = icons.weather['net_none'],
75 | font = icons.font .. icons.size
76 | }),
77 | id = 'icon'
78 | },
79 | {
80 | widget = widget.textbox.colored({
81 | text = 'N/A'
82 | }),
83 | id = 'temp'
84 | }
85 | },
86 | buttons = {
87 | awful.button(nil, 1, function()
88 | if s.time then s.time:toggle() end
89 | end)
90 | },
91 | set_col = function(self, col)
92 | self:get_children_by_id('icon')[1].color = col
93 | self:get_children_by_id('temp')[1].color = col
94 | end,
95 | set_icon = function(self, icon)
96 | self:get_children_by_id('icon')[1].text = icon
97 | end,
98 | set_temp = function(self, temp)
99 | self:get_children_by_id('temp')[1].text = temp
100 | end
101 | })
102 | current_weather:connect_signal('mouse::enter', function(self)
103 | self.col = color.accent
104 | end)
105 | current_weather:connect_signal('mouse::leave', function(self)
106 | self.col = color.fg0
107 | end)
108 |
109 | weather:connect_signal('weather::data', function(_, data)
110 | current_weather.visible = true
111 | current_weather.icon = icons.weather[data.icon]
112 | current_weather.temp = data.temperature
113 | end)
114 |
115 | weather:request_data()
116 |
117 | return wibox.widget({
118 | layout = wibox.layout.fixed.horizontal,
119 | spacing = dpi(9),
120 | clock,
121 | current_weather
122 | })
123 | end
124 |
--------------------------------------------------------------------------------
/ui/wibar/module/dash.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local color = require(beautiful.colorscheme)
11 | local widget = require('widget')
12 | local icons = require('theme.icons')
13 | local user = require('config.user')
14 |
15 | -- Create a launcher widget. Opens the Awesome menu when clicked.
16 | return function(s)
17 | local arrow = widget.textbox.colored({
18 | text = icons['arrow_down'],
19 | font = icons.font .. icons.size,
20 | align = 'center'
21 | })
22 |
23 | local idle
24 | if not user.lite or user.lite == nil then
25 | idle = wibox.widget({
26 | widget = wibox.widget.imagebox,
27 | image = beautiful.pfp,
28 | vertical_fit_policy = 'fit',
29 | horizontal_fit_policy = 'fit'
30 | })
31 | else
32 | idle = wibox.widget({
33 | widget = wibox.container.margin,
34 | margins = {
35 | left = dpi(7), right = dpi(7)
36 | },
37 | widget.textbox.colored({
38 | text = icons['util_hamburger'],
39 | font = icons.font .. icons.size
40 | })
41 | })
42 | end
43 |
44 | local w = wibox.widget({
45 | widget = wibox.container.background,
46 | bg = color.bg1,
47 | shape = function(cr, w, h)
48 | gears.shape.circle(cr, w, h)
49 | end,
50 | forced_height = dpi(23),
51 | forced_width = dpi(23),
52 | {
53 | layout = wibox.layout.stack,
54 | idle,
55 | {
56 | widget = wibox.container.background,
57 | visible = false,
58 | bg = color.bg2 .. 'C0',
59 | id = 'hover_over',
60 | arrow
61 | }
62 | },
63 | buttons = {
64 | awful.button(nil, 1, function()
65 | s.dash:toggle()
66 | arrow.text = s.dash.visible and icons['arrow_up'] or icons['arrow_down']
67 | end)
68 | },
69 | set_hover = function(self, bool)
70 | self:get_children_by_id('hover_over')[1].visible = bool
71 | end
72 | })
73 | w:connect_signal('mouse::enter', function(self)
74 | self.hover = true
75 | arrow.direction = s.dash.visible and 'north' or 'south'
76 | end)
77 | w:connect_signal('mouse::leave', function(self)
78 | self.hover = false
79 | end)
80 |
81 | return w
82 | end
83 |
--------------------------------------------------------------------------------
/ui/wibar/module/init.lua:
--------------------------------------------------------------------------------
1 | -- Return a table containing all bar modules, with a name attached
2 | -- to each.
3 | local require = require
4 | local path = ... .. '.'
5 | return setmetatable({}, {
6 | __index = function(_, key)
7 | local module, _ = require(path .. key)
8 | return module
9 | end
10 | })
11 |
--------------------------------------------------------------------------------
/ui/wibar/module/launcher.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local widget = require('widget')
10 | local color = require(beautiful.colorscheme)
11 | local icons = require('theme.icons')
12 |
13 | return function(s)
14 | local w = wibox.widget({
15 | layout = wibox.layout.fixed.horizontal,
16 | spacing = dpi(6),
17 | {
18 | widget = widget.textbox.colored({
19 | text = icons['util_magnifier'],
20 | font = icons.font .. icons.size
21 | }),
22 | id = 'icon_role'
23 | },
24 | {
25 | widget = widget.textbox.colored({ text = 'Search' }),
26 | id = 'text_role'
27 | },
28 | buttons = {
29 | awful.button(nil, 1, function() s.launcher:open() end)
30 | },
31 | set_fg = function(self, col)
32 | self:get_children_by_id('text_role')[1].color = col
33 | self:get_children_by_id('icon_role')[1].color = col
34 | end
35 | })
36 | w:connect_signal('mouse::enter', function(self)
37 | self.fg = color.accent
38 | end)
39 | w:connect_signal('mouse::leave', function(self)
40 | self.fg = color.fg0
41 | end)
42 |
43 | return w
44 | end
45 |
--------------------------------------------------------------------------------
/ui/wibar/module/layoutbox.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local widget = require('widget')
10 | local color = require(beautiful.colorscheme)
11 |
12 | return function(s)
13 | -- Create a textbox widget which will contain an icon indicating which layout we're using.
14 | -- We need one layoutbox per screen.
15 | -- NOTE: the layoutbox widget used here is custom and can be found at `widget.layoutbox`.
16 | local layout = wibox.widget({
17 | widget = wibox.container.background,
18 | bg = color.bg1,
19 | {
20 | layout = wibox.layout.fixed.horizontal,
21 | {
22 | widget = wibox.container.background,
23 | bg = color.bg3,
24 | forced_width = dpi(1)
25 | },
26 | {
27 | layout = wibox.layout.align.vertical,
28 | {
29 | widget = wibox.container.background,
30 | bg = color.bg3,
31 | forced_height = dpi(1)
32 | },
33 | {
34 | widget = wibox.container.margin,
35 | margins = dpi(6),
36 | {
37 | widget = wibox.container.constraint,
38 | strategy = 'exact',
39 | height = dpi(9),
40 | widget.layoutbox({ screen = s })
41 | }
42 | },
43 | {
44 | widget = wibox.container.background,
45 | bg = color.bg3,
46 | forced_height = dpi(1)
47 | }
48 | }
49 | },
50 | buttons = {
51 | awful.button(nil, 1, function() awful.layout.inc( 1) end),
52 | awful.button(nil, 3, function() awful.layout.inc(-1) end),
53 | awful.button(nil, 4, function() awful.layout.inc(-1) end),
54 | awful.button(nil, 5, function() awful.layout.inc( 1) end)
55 | }
56 | })
57 | layout:connect_signal('mouse::enter', function(self)
58 | self.bg = color.bg2
59 | end)
60 | layout:connect_signal('mouse::leave', function(self)
61 | self.bg = color.bg1
62 | end)
63 |
64 | return layout
65 | end
66 |
--------------------------------------------------------------------------------
/ui/wibar/module/status.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local beautiful = require('beautiful')
4 | local wibox = require('wibox')
5 |
6 | local dpi = beautiful.xresources.apply_dpi
7 |
8 | local color = require(beautiful.colorscheme)
9 | local icons = require('theme.icons')
10 | local widget = require('widget')
11 | local audio = require('signal.system.audio')
12 | local battery = require('signal.system.battery')
13 |
14 | local audio_widget = widget.textbox.colored({
15 | text = icons['audio_muted'],
16 | font = icons.font .. icons.size,
17 | color = color.red
18 | })
19 | audio:connect_signal('sinks::default', function(_, default_sink)
20 | if default_sink.mute or default_sink.volume == 0 then
21 | audio_widget.text = icons['audio_muted']
22 | audio_widget.color = color.red
23 | elseif default_sink.volume < 50 then
24 | audio_widget.text = icons['audio_decrease']
25 | audio_widget.color = color.fg0
26 | else
27 | audio_widget.text = icons['audio_increase']
28 | audio_widget.color = color.fg0
29 | end
30 | end)
31 |
32 | -- Only assigned if a valid battery is found.
33 | local battery_icon = widget.textbox.colored({
34 | text = icons.battery['UNKNOWN'],
35 | font = icons.font .. icons.size
36 | })
37 | local battery_level = widget.textbox.colored({
38 | text = 'N/A',
39 | })
40 | local battery_widget = wibox.widget({
41 | widget = wibox.container.margin,
42 | margins = { left = dpi(12) },
43 | visible = false,
44 | {
45 | layout = wibox.layout.fixed.horizontal,
46 | spacing = dpi(6),
47 | battery_icon,
48 | battery_level
49 | }
50 | })
51 | battery:connect_signal('update', function(_, percent, state, _, _, _)
52 | battery_widget.visible = true
53 |
54 | battery_level.text = percent .. '%'
55 | if state == 'CHARGING' or state == 'FULLY_CHARGED' then
56 | battery_icon.text = icons.battery[state]
57 | elseif percent >= 95 then
58 | battery_icon.text = icons.battery['FULL']
59 | elseif percent >= 70 then
60 | battery_icon.text = icons.battery['HIGH']
61 | elseif percent >= 40 then
62 | battery_icon.text = icons.battery['NORMAL']
63 | elseif percent >= 20 then
64 | battery_icon.text = icons.battery['LOW']
65 | else
66 | battery_icon.text = icons.battery['CRITICAL']
67 | end
68 | end)
69 |
70 | return function()
71 | return wibox.widget({
72 | widget = wibox.container.background,
73 | bg = color.bg1,
74 | border_width = dpi(1),
75 | border_color = color.bg3,
76 | {
77 | widget = wibox.container.margin,
78 | margins = {
79 | top = dpi(6), bottom = dpi(6),
80 | left = dpi(12), right = dpi(12)
81 | },
82 | {
83 | layout = wibox.layout.fixed.horizontal,
84 | audio_widget,
85 | battery_widget
86 | }
87 | }
88 | })
89 | end
90 |
--------------------------------------------------------------------------------
/ui/wibar/module/systray.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local wibox = require('wibox')
6 |
7 | local dpi = beautiful.xresources.apply_dpi
8 |
9 | local widget = require('widget')
10 | local color = require(beautiful.colorscheme)
11 | local icons = require('theme.icons')
12 |
13 | return function()
14 | -- The systray itself.
15 | local systray = wibox.widget({
16 | layout = wibox.layout.fixed.horizontal,
17 | visible = false,
18 | {
19 | widget = wibox.container.background,
20 | bg = color.bg3,
21 | forced_width = dpi(1)
22 | },
23 | {
24 | layout = wibox.layout.fixed.vertical,
25 | {
26 | widget = wibox.container.background,
27 | bg = color.bg3,
28 | forced_height = dpi(1)
29 | },
30 | {
31 | widget = wibox.container.background,
32 | bg = color.bg1,
33 | {
34 | widget = wibox.container.margin,
35 | margins = dpi(3),
36 | {
37 | widget = wibox.container.constraint,
38 | strategy = 'exact',
39 | height = dpi(15),
40 | wibox.widget.systray()
41 | }
42 | }
43 | },
44 | {
45 | widget = wibox.container.background,
46 | bg = color.bg3,
47 | forced_height = dpi(1)
48 | }
49 | }
50 | })
51 |
52 | -- The arrow image.
53 | local switch = widget.textbox.colored({
54 | text = icons['arrow_left'],
55 | font = icons.font .. icons.size,
56 | align = 'center'
57 | })
58 | local switchbox = wibox.widget({
59 | widget = wibox.container.background,
60 | bg = color.bg1,
61 | border_width = dpi(1),
62 | border_color = color.bg3,
63 | {
64 | widget = wibox.container.margin,
65 | margins = {
66 | top = dpi(7), bottom = dpi(7),
67 | left = dpi(6), right = dpi(6)
68 | },
69 | switch
70 | },
71 | buttons = {
72 | awful.button(nil, 1, function()
73 | if systray.visible then
74 | systray.visible = false
75 | switch.text = icons['arrow_left']
76 | else
77 | systray.visible = true
78 | switch.text = icons['arrow_right']
79 | end
80 | end)
81 | }
82 | })
83 | switchbox:connect_signal('mouse::enter', function(self)
84 | self.bg = color.bg2
85 | end)
86 | switchbox:connect_signal('mouse::leave', function(self)
87 | self.bg = color.bg1
88 | end)
89 |
90 | -- When hovered, lights up the switch and when clicked, switches states and changes
91 | -- the icon's direction.
92 | return wibox.widget({
93 | layout = wibox.layout.fixed.horizontal,
94 | systray,
95 | switchbox
96 | })
97 | end
98 |
--------------------------------------------------------------------------------
/ui/wibar/module/taglist.lua:
--------------------------------------------------------------------------------
1 | local require, client, awesome = require, client, awesome
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local bling = require('module.bling')
11 | local color = require(beautiful.colorscheme)
12 | local user = require('config.user')
13 | local mod = require('binds.mod')
14 | local modkey = mod.modkey
15 |
16 | local preview = user.lite == nil or not user.lite
17 |
18 | return function(s)
19 | if preview then
20 | -- Enable and customize the tag preview widget.
21 | bling.widget.tag_preview.enable({
22 | show_client_content = true,
23 | scale = 0.125,
24 | honor_padding = true,
25 | honor_workarea = true,
26 | placement_fn = function(c)
27 | awful.placement.next_to(c, {
28 | margins = { top = beautiful.useless_gap, left = dpi(40) },
29 | preferred_positions = 'bottom',
30 | preferred_anchors = 'front',
31 | geometry = s.bar
32 | })
33 | end
34 | })
35 | end
36 |
37 | -- Create the taglist.
38 | local tags = awful.widget.taglist({
39 | screen = s,
40 | filter = awful.widget.taglist.filter.all,
41 | buttons = {
42 | -- Left-clicking a tag changes to it.
43 | awful.button(nil, 1, function(t) t:view_only() end),
44 | -- Mod + Left-clicking a tag sends the currently focused client to it.
45 | awful.button({ modkey }, 1, function(t)
46 | if client.focus then
47 | client.focus:move_to_tag(t)
48 | end
49 | end),
50 | -- Right-clicking a tag makes its contents visible in the current one.
51 | awful.button(nil, 3, awful.tag.viewtoggle),
52 | -- Mod + Right-clicking a tag makes the currently focused client visible in it.
53 | awful.button({ modkey }, 3, function(t)
54 | if client.focus then
55 | client.focus:toggle_tag(t)
56 | end
57 | end),
58 | -- Mousewheel scrolling cycles through tags.
59 | awful.button(nil, 4, function(t) awful.tag.viewprev(t.screen) end),
60 | awful.button(nil, 5, function(t) awful.tag.viewnext(t.screen) end)
61 | },
62 | layout = {
63 | layout = wibox.layout.fixed.horizontal,
64 | spacing = dpi(8)
65 | },
66 | style = {
67 | bg_focus = color.accent,
68 | bg_occupied = color.fg2,
69 | bg_empty = color.bg4 .. 'ac',
70 | bg_urgent = color.red
71 | },
72 |
73 | -- The fun stuff.
74 | widget_template = {
75 | widget = wibox.container.margin,
76 | -- The purpose of this margin widget is purely to fatten the hitbox of the tag
77 | -- lines, as to make them more mouse friendly.
78 | margins = {
79 | top = dpi(11), bottom = dpi(11)
80 | },
81 | {
82 | widget = wibox.container.background,
83 | id = 'background_role',
84 | -- Create the tag icon as an empty textbox.
85 | wibox.widget.textbox()
86 | },
87 | -- Create a callback to change its size with an animation depending
88 | -- on focus and occupation.
89 | create_callback = function(self, tag)
90 | local bar = self:get_children_by_id('background_role')[1]
91 | self.update = function()
92 | if tag.selected then
93 | -- If the tag is focused:
94 | bar.forced_width = dpi(48)
95 | elseif #tag:clients() > 0 then
96 | -- If the tag is occupied:
97 | bar.forced_width = dpi(32)
98 | else
99 | -- If the tag is unoccupied and unfocused:
100 | bar.forced_width = dpi(16)
101 | end
102 | end
103 | -- Generate the bar sizes once.
104 | self.update()
105 |
106 | if preview then
107 | -- Show a preview of the tag if it's hovered for a second.
108 | local visible, hovered = false, false
109 | local timer = gears.timer({
110 | timeout = 1,
111 | single_shot = true,
112 | callback = function()
113 | if not client.focus or not client.focus.fullscreen then
114 | if not visible and hovered then
115 | if #tag:clients() > 0 then
116 | visible = true
117 | awesome.emit_signal('bling::tag_preview::update', tag)
118 | awesome.emit_signal("bling::tag_preview::visibility", s, true)
119 | end
120 | end
121 | end
122 | end
123 | })
124 | self:connect_signal('mouse::enter', function()
125 | hovered = true
126 | timer:start()
127 | end)
128 | self:connect_signal('mouse::leave', function()
129 | hovered = false
130 | if visible then
131 | visible = false
132 | timer:stop()
133 | awesome.emit_signal("bling::tag_preview::visibility", s, false)
134 | end
135 | end)
136 | end
137 | end,
138 | -- Then update on callback.
139 | update_callback = function(self)
140 | self.update()
141 | end
142 | }
143 | })
144 |
145 | return wibox.widget({
146 | widget = wibox.container.background,
147 | bg = color.bg1,
148 | border_width = dpi(1),
149 | border_color = color.bg3,
150 | {
151 | widget = wibox.container.margin,
152 | margins = {
153 | left = dpi(11), right = dpi(11)
154 | },
155 | tags
156 | }
157 | })
158 | end
159 |
--------------------------------------------------------------------------------
/ui/wibar/module/tasklist.lua:
--------------------------------------------------------------------------------
1 | local require, awesome, client = require, awesome, client
2 |
3 | local awful = require('awful')
4 | local beautiful = require('beautiful')
5 | local gears = require('gears')
6 | local wibox = require('wibox')
7 |
8 | local dpi = beautiful.xresources.apply_dpi
9 |
10 | local bling = require('module.bling')
11 | local color = require(beautiful.colorscheme)
12 | local user = require('config.user')
13 |
14 | local preview = user.lite == nil or not user.lite
15 |
16 | return function(s)
17 | if preview then
18 | -- Enable and customize the task preview widget.
19 | bling.widget.task_preview.enable({
20 | placement_fn = function(c)
21 | awful.placement.next_to(c, {
22 | margins = { top = beautiful.useless_gap },
23 | preferred_positions = 'bottom',
24 | preferred_anchors = 'middle',
25 | geometry = s.bar
26 | })
27 | end,
28 | structure = {
29 | widget = wibox.container.background,
30 | bg = color.bg1,
31 | border_width = dpi(1),
32 | border_color = color.bg3,
33 | {
34 | widget = wibox.container.margin,
35 | margins = {
36 | top = dpi(6), bottom = dpi(8),
37 | left = dpi(8), right = dpi(8)
38 | },
39 | {
40 | layout = wibox.layout.fixed.vertical,
41 | spacing = dpi(4),
42 | {
43 | widget = wibox.widget.textbox,
44 | id = 'name_role'
45 | },
46 | {
47 | widget = wibox.widget.imagebox,
48 | resize = true,
49 | valign = 'center',
50 | halign = 'center',
51 | id = 'image_role'
52 | }
53 | }
54 | }
55 | }
56 | })
57 | end
58 |
59 | -- Create a tasklist widget.
60 | return awful.widget.tasklist({
61 | screen = s,
62 | filter = awful.widget.tasklist.filter.currenttags,
63 | buttons = {
64 | -- Left-clicking a client indicator minimizes it if it's unminimized, or
65 | -- unminimizes it if it's minimized.
66 | awful.button(nil, 1, function(c)
67 | c:activate({ context = 'tasklist', action = 'toggle_minimization' })
68 | end),
69 | -- Right-clicking a client indicator shows the list of all open clients in all
70 | -- visible tags.
71 | awful.button(nil, 3, function() awful.menu.client_list({
72 | theme = { width = 250 } })
73 | end),
74 | -- Mousewheel scrolling cycles through clients.
75 | awful.button(nil, 4, function() awful.client.focus.byidx(-1) end),
76 | awful.button(nil, 5, function() awful.client.focus.byidx( 1) end)
77 | },
78 | layout = {
79 | layout = wibox.layout.flex.horizontal,
80 | spacing = dpi(6)
81 | },
82 | style = {
83 | -- Colors.
84 | bg_minimize = color.bg1,
85 | fg_minimize = color.bg4,
86 | bg_normal = color.bg1,
87 | fg_normal = color.fg2,
88 | bg_focus = color.bg2 .. '80',
89 | fg_focus = color.accent,
90 | bg_urgent = color.bg1,
91 | fg_urgent = color.red,
92 | shape_border_width = dpi(1),
93 | shape_border_color = color.bg3,
94 | shape_border_color_focus = color.bg4 .. 'BF',
95 | shape_border_color_minimized = color.bg2,
96 | shape_border_color_urgent = color.red,
97 | -- Styling.
98 | font = beautiful.font,
99 | disable_icon = true,
100 | maximized = '[+]',
101 | minimized = '[-]',
102 | sticky = '[*]',
103 | floating = '[~]',
104 | ontop = '[^]',
105 | above = '[!]'
106 | },
107 | widget_template = {
108 | widget = wibox.container.background,
109 | id = 'background_role',
110 | {
111 | widget = wibox.container.margin,
112 | margins = {
113 | left = dpi(13), right = dpi(13)
114 | },
115 | {
116 | widget = wibox.widget.textbox,
117 | valign = 'center',
118 | id = 'text_role'
119 | }
120 | },
121 | create_callback = function(self, task)
122 | if preview then
123 | -- Show a preview of the task if it's hovered for a second.
124 | local visible, hovered = false, false
125 | local timer = gears.timer({
126 | timeout = 1,
127 | single_shot = true,
128 | callback = function()
129 | if not client.focus or not client.focus.fullscreen then
130 | if not visible and hovered then
131 | visible = true
132 | awesome.emit_signal("bling::task_preview::visibility", s, true, task)
133 | end
134 | end
135 | end
136 | })
137 | self:connect_signal('mouse::enter', function()
138 | hovered = true
139 | timer:start()
140 | end)
141 | self:connect_signal('mouse::leave', function()
142 | hovered = false
143 | if visible then
144 | visible = false
145 | timer:stop()
146 | awesome.emit_signal("bling::task_preview::visibility", s, false, task)
147 | end
148 | end)
149 | end
150 | end
151 | }
152 | })
153 | end
154 |
--------------------------------------------------------------------------------
/widget/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | return {
4 | layoutbox = require(... .. '.layoutbox'),
5 | textbox = require(... .. '.textbox')
6 | }
7 |
--------------------------------------------------------------------------------
/widget/layoutbox.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- Display the current client layout (`awful.layout`) icon or name.
3 | --
4 | -- @DOC_awful_widget_layoutbox_default_EXAMPLE@
5 | --
6 | -- @author Julien Danjou <julien@danjou.info>
7 | -- @copyright 2009 Julien Danjou
8 | -- @widgetmod awful.widget.layoutbox
9 | -- @supermodule wibox.layout.fixed
10 | ---------------------------------------------------------------------------
11 | --- Modified to use my icon font instead of the usual images.
12 |
13 | local require, pairs, assert, type, setmetatable
14 | = require, pairs, assert, type, setmetatable
15 | local capi = { screen = screen, tag = tag }
16 | local layout = require("awful.layout")
17 | local tooltip = require("awful.tooltip")
18 | local beautiful = require("beautiful")
19 | local wibox = require("wibox")
20 | local gdebug = require("gears.debug")
21 | local gtable = require("gears.table")
22 |
23 | local icon = require('theme.icons')
24 |
25 | local function get_screen(s)
26 | return s and capi.screen[s]
27 | end
28 |
29 | local layoutbox = { mt = {} }
30 |
31 | local boxes = nil
32 |
33 | local function update(w, screen)
34 | screen = get_screen(screen)
35 | local name = layout.getname(layout.get(screen))
36 | w._layoutbox_tooltip:set_text(name or "[no name]")
37 | if icon.layout[name] then
38 | w.text = icon.layout[name]
39 | w.font = icon.font .. icon.size
40 | else
41 | w.text = name
42 | w.font = beautiful.font
43 | end
44 | end
45 |
46 | local function update_from_tag(t)
47 | local screen = get_screen(t.screen)
48 | local w = boxes[screen]
49 | if w then
50 | update(w, screen)
51 | end
52 | end
53 |
54 | --- Create a layoutbox widget. It draws a picture with the current layout
55 | -- symbol of the current tag.
56 | -- @constructorfct awful.widget.layoutbox
57 | -- @tparam table args The arguments.
58 | -- @tparam screen args.screen The screen number that the layout will be represented for.
59 | -- @tparam table args.buttons The `awful.button`s for this layoutbox.
60 | -- @return The layoutbox.
61 | function layoutbox.new(args)
62 | args = args or {}
63 | local screen = args.screen
64 |
65 | if type(args) == "number" or type(args) == "screen" or args.fake_remove then
66 | screen, args = args, {}
67 |
68 | gdebug.deprecate(
69 | "Use awful.widget.layoutbox{screen=s} instead of awful.widget.layoutbox(screen)",
70 | {deprecated_in=5}
71 | )
72 | end
73 |
74 | assert(type(args) == "table")
75 |
76 | screen = get_screen(screen or 1)
77 |
78 | -- Do we already have the update callbacks registered?
79 | if boxes == nil then
80 | boxes = setmetatable({}, { __mode = "kv" })
81 | capi.tag.connect_signal("property::selected", update_from_tag)
82 | capi.tag.connect_signal("property::layout", update_from_tag)
83 | capi.tag.connect_signal("property::screen", function()
84 | for s, w in pairs(boxes) do
85 | if s.valid then update(w, s) end
86 | end
87 | end)
88 | layoutbox.boxes = boxes
89 | end
90 |
91 | -- Do we already have a layoutbox for this screen?
92 | local w = boxes[screen]
93 | if not w then
94 | w = wibox.widget.textbox()
95 | w._layoutbox_tooltip = tooltip {objects = {w}, delay_show = 1}
96 |
97 | -- Apply the buttons, visible, forced_width and so on
98 | gtable.crush(w, args)
99 |
100 | update(w, screen)
101 | boxes[screen] = w
102 | end
103 |
104 | return w
105 | end
106 |
107 | function layoutbox.mt:__call(...)
108 | return layoutbox.new(...)
109 | end
110 |
111 | return setmetatable(layoutbox, layoutbox.mt)
112 |
--------------------------------------------------------------------------------
/widget/textbox/colored.lua:
--------------------------------------------------------------------------------
1 | --- A colorable textbox widget.
2 |
3 | local require, type, setmetatable = require, type, setmetatable
4 |
5 | local beautiful = require('beautiful')
6 | local gears = require('gears')
7 | local wibox = require('wibox')
8 |
9 | local textbox = { mt = {} }
10 |
11 | --- Create a colorable textbox widget. Like a regular `wibox.widget.textbox`, but also
12 | --- capable of coloring its text.
13 | -- @constructorfct widget.textbox.colored
14 | -- @tparam string The text to be displayed.
15 | -- @tparam table args The arguments.
16 | -- @tparam string args.text The text to be displayed.
17 | -- @tparam string args.font The font to be used.
18 | -- @tparam string args.color The color the text will be by default.
19 | -- @tparam string args.align The horizontal text alignment (left, center, right).
20 | -- @return The colored textbox.
21 | function textbox.new(args)
22 | -- Make sure the args have the correct type.
23 | local args_type = type(args)
24 | if args_type == 'string' or args_type == 'number' then
25 | local text = args
26 | args = {}
27 | args.text = text
28 | end
29 | assert(type(args) == 'table')
30 |
31 | -- Normalize args.
32 | local conf = gears.table.crush({
33 | text = '',
34 | font = beautiful.font,
35 | color = beautiful.fg_normal,
36 | align = 'left'
37 | }, args, true)
38 |
39 | local w = wibox.widget({
40 | widget = wibox.container.background,
41 | fg = conf.color,
42 | {
43 | widget = wibox.widget.textbox,
44 | markup = conf.text,
45 | font = conf.font,
46 | halign = conf.align,
47 | id = 'text_role'
48 | },
49 | set_text = function(self, new_text)
50 | self:get_children_by_id('text_role')[1].markup = new_text
51 | end,
52 | set_color = function(self, new_color)
53 | self.fg = new_color
54 | end
55 | })
56 |
57 | return w
58 | end
59 |
60 | function textbox.mt:__call(...)
61 | return textbox.new(...)
62 | end
63 |
64 | return setmetatable(textbox, textbox.mt)
65 |
--------------------------------------------------------------------------------
/widget/textbox/init.lua:
--------------------------------------------------------------------------------
1 | local require = require
2 |
3 | return {
4 | colored = require(... .. '.colored'),
5 | scrolling = require(... .. '.scrolling')
6 | }
7 |
--------------------------------------------------------------------------------
/widget/textbox/scrolling.lua:
--------------------------------------------------------------------------------
1 | --- A scrolling, colorable textbox widget.
2 |
3 | local require, type, setmetatable = require, type, setmetatable
4 |
5 | local beautiful = require('beautiful')
6 | local gears = require('gears')
7 | local wibox = require('wibox')
8 |
9 | local textbox = { mt = {} }
10 |
11 | --- Create a scrolling, colorable textbox widget. Like a regular `wibox.widget.textbox`,
12 | --- but also capable of coloring its text, and scrolling in either axis.
13 | -- @constructorfct widget.textbox.scrolling
14 | -- @tparam string The text to be displayed.
15 | -- @tparam table args The arguments.
16 | -- @tparam string args.text The text to be displayed.
17 | -- @tparam string args.font The font to be used.
18 | -- @tparam string args.color The color the text will be by default.
19 | -- @tparam string args.align The horizontal text alignment (left, center, right).
20 | -- @tparam string args.dir The axis on which to scroll (horizontal, vertical).
21 | -- @return The scrollable, colored textbox.
22 | function textbox.new(args)
23 | -- Make sure the args have the correct type.
24 | local args_type = type(args)
25 | if args_type == 'string' or args_type == 'number' then
26 | local text = args
27 | args = {}
28 | args.text = text
29 | end
30 | assert(type(args) == 'table')
31 |
32 | -- Normalize args.
33 | local conf = gears.table.crush({
34 | text = '',
35 | font = beautiful.font,
36 | color = beautiful.fg_normal,
37 | align = 'left',
38 | dir = 'horizontal'
39 | }, args, true)
40 |
41 | local scroll
42 | if conf.dir == 'horizontal' then
43 | scroll = wibox.container.scroll.horizontal
44 | else
45 | scroll = wibox.container.scroll.vertical
46 | end
47 |
48 | local w = wibox.widget({
49 | widget = scroll,
50 | step_function =
51 | wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth,
52 | speed = 50,
53 | {
54 | widget = require('widget.textbox.colored')({
55 | text = conf.text,
56 | font = conf.font,
57 | color = conf.color,
58 | align = conf.align
59 | }),
60 | id = 'text_role'
61 | },
62 | set_text = function(self, new_text)
63 | self:get_children_by_id('text_role')[1].text = new_text
64 | end,
65 | set_color = function(self, new_color)
66 | self:get_children_by_id('text_role')[1].fg = new_color
67 | end
68 | })
69 |
70 | return w
71 | end
72 |
73 | function textbox.mt:__call(...)
74 | return textbox.new(...)
75 | end
76 |
77 | return setmetatable(textbox, textbox.mt)
78 |
79 |
--------------------------------------------------------------------------------