├── .gitignore
├── .gitmodules
├── README.md
├── assets
├── default.svg
├── music.svg
├── pfp.jpg
└── terminal.svg
├── backgrounds
├── abstract_1.jpg
├── abstract_2.jpg
├── abstract_3.jpg
├── abstract_4.jpg
├── abstract_5.png
├── abstract_6.png
├── abstract_7.jpg
├── abstract_8.png
├── abstract_9.png
├── bird_1.jpg
├── bird_2.png
├── blahaj.png
├── charizard.png
├── forest_1.jpg
├── gasstation.jpg
├── goose.jpg
├── macos_1.jpg
├── mountains_1.jpg
├── road_1.jpg
├── shade.png
├── window.jpg
└── winterlake.jpg
├── components
├── bar
│ ├── init.lua
│ └── modules
│ │ ├── battery.lua
│ │ ├── clock.lua
│ │ ├── launcher.lua
│ │ ├── layout.lua
│ │ ├── systray.lua
│ │ ├── taglist.lua
│ │ ├── tasklist.lua
│ │ ├── volume.lua
│ │ └── wifi.lua
├── init.lua
├── lock
│ ├── init.lua
│ └── modules
│ │ ├── circle.lua
│ │ └── clock.lua
├── osd
│ ├── brightness.lua
│ ├── init.lua
│ └── volume.lua
├── sidebar
│ ├── init.lua
│ └── modules
│ │ ├── header.lua
│ │ ├── launcher.lua
│ │ ├── media.lua
│ │ ├── notifications.lua
│ │ └── status.lua
└── switcher
│ └── init.lua
├── config.lua
├── daemons
├── battery.lua
├── brightness.lua
├── cpu.lua
├── init.lua
├── memory.lua
├── network.lua
├── playerctl.lua
├── uptime.lua
└── volume.lua
├── helpers.lua
├── keys.lua
├── layouts
├── cornerne.png
├── cornernew.png
├── cornernw.png
├── cornernww.png
├── cornerse.png
├── cornersew.png
├── cornersw.png
├── cornersww.png
├── dwindle.png
├── dwindlew.png
├── fairh.png
├── fairhw.png
├── fairv.png
├── fairvw.png
├── floating.png
├── floatingw.png
├── fullscreen.png
├── fullscreenw.png
├── magnifier.png
├── magnifierw.png
├── max.png
├── maxw.png
├── spiral.png
├── spiralw.png
├── tile.png
├── tilebottom.png
├── tilebottomw.png
├── tileleft.png
├── tileleftw.png
├── tiletop.png
├── tiletopw.png
└── tilew.png
├── liblua_pam.so
├── notifications.lua
├── preview.png
├── rc.lua
├── rules.lua
├── screenshot
├── signals
├── error.lua
├── init.lua
├── tags.lua
└── titlebars.lua
├── themes.lua
└── wibox
├── layout
├── init.lua
└── overflow.lua
└── widget
└── hierarchy.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | macos/
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "rubato"]
2 | path = rubato
3 | url = https://github.com/andOrlando/rubato
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## AwesomeWM Config
2 | 
3 |
4 | #### Features
5 | - Tab Switcher (Alt+Tab)
6 | - Lock Screen (Shift+Alt+x)
7 | - Sidebar/App Launcher (Super+p)
8 |
9 | #### Requirements
10 | - [Material Design Iconic Font](https://zavoloklom.github.io/material-design-iconic-font/)
11 | - [Roboto Condensed Font](https://fonts.google.com/specimen/Roboto+Condensed)
12 | - [Rubato](https://github.com/andOrlando/rubato) (Replace the Rubato folder)
13 | - playerctl (Media)
14 | - xbacklight (Brightness)
15 | - NetworkManager (Wifi)
16 | - pamixer & pactl (Audio)
17 | - UPower (Battery)
18 |
19 | #### Post-Installation
20 | - Add your own background in `backgrounds/` and change in `themes.lua`
21 | - Change `assets/pfp.jpg`
22 | - Change `config.lua` based on your own system
23 | - Change the pinned apps for the launcher in `components/sidebar/modules/launcher.lua`
24 |
25 | #### Misc
26 | - Theme is Github Dark, you can change it in `themes.lua`
27 |
--------------------------------------------------------------------------------
/assets/default.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/music.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/pfp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/assets/pfp.jpg
--------------------------------------------------------------------------------
/assets/terminal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backgrounds/abstract_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_1.jpg
--------------------------------------------------------------------------------
/backgrounds/abstract_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_2.jpg
--------------------------------------------------------------------------------
/backgrounds/abstract_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_3.jpg
--------------------------------------------------------------------------------
/backgrounds/abstract_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_4.jpg
--------------------------------------------------------------------------------
/backgrounds/abstract_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_5.png
--------------------------------------------------------------------------------
/backgrounds/abstract_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_6.png
--------------------------------------------------------------------------------
/backgrounds/abstract_7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_7.jpg
--------------------------------------------------------------------------------
/backgrounds/abstract_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_8.png
--------------------------------------------------------------------------------
/backgrounds/abstract_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/abstract_9.png
--------------------------------------------------------------------------------
/backgrounds/bird_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/bird_1.jpg
--------------------------------------------------------------------------------
/backgrounds/bird_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/bird_2.png
--------------------------------------------------------------------------------
/backgrounds/blahaj.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/blahaj.png
--------------------------------------------------------------------------------
/backgrounds/charizard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/charizard.png
--------------------------------------------------------------------------------
/backgrounds/forest_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/forest_1.jpg
--------------------------------------------------------------------------------
/backgrounds/gasstation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/gasstation.jpg
--------------------------------------------------------------------------------
/backgrounds/goose.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/goose.jpg
--------------------------------------------------------------------------------
/backgrounds/macos_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/macos_1.jpg
--------------------------------------------------------------------------------
/backgrounds/mountains_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/mountains_1.jpg
--------------------------------------------------------------------------------
/backgrounds/road_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/road_1.jpg
--------------------------------------------------------------------------------
/backgrounds/shade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/shade.png
--------------------------------------------------------------------------------
/backgrounds/window.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/window.jpg
--------------------------------------------------------------------------------
/backgrounds/winterlake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/backgrounds/winterlake.jpg
--------------------------------------------------------------------------------
/components/bar/init.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local helpers = require("helpers")
4 | local beautiful = require("beautiful")
5 |
6 |
7 | local function create_bar(s)
8 | s.bar = awful.wibar({
9 | position = "left",
10 | width = beautiful.bar_width,
11 | screen = s,
12 | height = s.geometry.height
13 | })
14 |
15 | s.bar.widget = helpers.add_bg0(wibox.widget({
16 | layout = wibox.layout.align.vertical,
17 | { -- Top widgets
18 | -- Launcher, Taglist
19 | require("components.bar.modules.launcher")(),
20 | require("components.bar.modules.taglist")(s),
21 | spacing = beautiful.margin[1],
22 | layout = wibox.layout.fixed.vertical,
23 | },
24 | { -- Middle widgets
25 | -- Tasklist
26 | require("components.bar.modules.tasklist")(s),
27 | spacing = beautiful.margin[1],
28 | layout = wibox.layout.fixed.vertical
29 | },
30 | { -- Bottom widgets
31 | -- Systray, Wifi+Volume, Battery, Clock, Layout
32 | require("components.bar.modules.systray")(),
33 | require("components.bar.modules.volume")(),
34 | require("components.bar.modules.wifi")(),
35 | require("components.bar.modules.battery")(),
36 | require("components.bar.modules.clock")(),
37 | require("components.bar.modules.layout")(),
38 | spacing = beautiful.margin[1],
39 | layout = wibox.layout.fixed.vertical
40 | }
41 | }))
42 | end
43 |
44 | screen.connect_signal("request::desktop_decoration", function(s)
45 | create_bar(s)
46 | end)
47 |
--------------------------------------------------------------------------------
/components/bar/modules/battery.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local beautiful = require("beautiful")
4 | local helpers = require("helpers")
5 | local naughty = require("naughty")
6 |
7 | local function update(progressbar, icon, tooltip, device)
8 | if device.percentage < 25 then
9 | progressbar.color = beautiful.red
10 | elseif device.percentage < 50 then
11 | progressbar.color = beautiful.orange
12 | elseif device.percentage < 75 then
13 | progressbar.color = beautiful.blue
14 | else
15 | progressbar.color = beautiful.green
16 | end
17 | progressbar.value = device.percentage
18 | if device.state == 1 or device.state == 4 then
19 | icon.visible = true
20 | else
21 | icon.visible = false
22 | end
23 | tooltip.markup = "Battery: " .. math.floor(device.percentage) .. "%"
24 | end
25 |
26 | local function create_widget()
27 | local progressbar = wibox.widget({
28 | max_value = 100,
29 | value = 100,
30 | border_width = 0,
31 | color = beautiful.green,
32 | background_color = beautiful.bg2,
33 | widget = wibox.widget.progressbar,
34 | shape = helpers.rrect()
35 | })
36 |
37 | local icon = wibox.widget({
38 | font = beautiful.font_icon,
39 | markup = "",
40 | valign = "center",
41 | halign = "center",
42 | widget = wibox.widget.textbox
43 | })
44 |
45 | local stack = wibox.widget({
46 | {
47 | progressbar,
48 | forced_height = beautiful.dpi(48),
49 | direction = "east",
50 | layout = wibox.container.rotate
51 | },
52 | {
53 | icon,
54 | fg = beautiful.bg0,
55 | layout = wibox.container.background,
56 | },
57 | layout = wibox.layout.stack
58 | })
59 |
60 | local widget = helpers.add_margin(stack, beautiful.margin[1], beautiful.margin[0])
61 |
62 | local tooltip = helpers.add_tooltip(widget, "Unknown")
63 |
64 | awesome.connect_signal("battery::update", function(device)
65 | update(progressbar, icon, tooltip, device)
66 | end)
67 |
68 | return widget
69 | end
70 | return create_widget
71 |
--------------------------------------------------------------------------------
/components/bar/modules/clock.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 |
7 | local function create_widget()
8 | local clock = wibox.widget({
9 | format = '%I\n%M',
10 | valign = "center",
11 | halign = "center",
12 | widget = wibox.widget.textclock,
13 | })
14 |
15 | local tooltip = helpers.add_tooltip(clock, "Date: "..os.date("%A, %B %e"))
16 | return helpers.add_margin(clock)
17 | end
18 | return create_widget
19 |
--------------------------------------------------------------------------------
/components/bar/modules/launcher.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox")
2 | local awful = require("awful")
3 | local beautiful = require("beautiful")
4 | local helpers = require("helpers")
5 |
6 | local function create_widget()
7 | local image = wibox.widget {
8 | image = beautiful.icon_awesome,
9 | resize = true,
10 | widget = wibox.widget.imagebox,
11 | }
12 |
13 | local widget = helpers.add_margin(
14 | helpers.add_click(helpers.add_margin(image, beautiful.margin[1], beautiful.margin[1])), beautiful.margin[0],
15 | beautiful.margin[0])
16 |
17 | local tooltip = helpers.add_tooltip(widget, "Launch")
18 |
19 | widget:buttons({
20 | awful.button({}, 1, function()
21 | awesome.emit_signal("launcher::toggle")
22 | end)
23 | })
24 |
25 | return widget
26 | end
27 |
28 | return create_widget
29 |
--------------------------------------------------------------------------------
/components/bar/modules/layout.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local helpers = require("helpers")
3 |
4 | local function create_widget(s)
5 | local layout_box = awful.widget.layoutbox({
6 | screen = s,
7 | -- Add buttons, allowing you to change the layout
8 | buttons = {
9 | awful.button({}, 1, function()
10 | awful.layout.inc(1)
11 | end),
12 | awful.button({}, 3, function()
13 | awful.layout.inc(-1)
14 | end),
15 | awful.button({}, 4, function()
16 | awful.layout.inc(1)
17 | end),
18 | awful.button({}, 5, function()
19 | awful.layout.inc(-1)
20 | end),
21 | },
22 | })
23 |
24 | local widget = helpers.add_margin(helpers.add_click(helpers.add_margin(layout_box)))
25 | return widget
26 | end
27 | return create_widget
28 |
--------------------------------------------------------------------------------
/components/bar/modules/systray.lua:
--------------------------------------------------------------------------------
1 | local beautiful = require("beautiful")
2 | local wibox = require("wibox")
3 | local helpers = require("helpers")
4 |
5 | local function create_widget()
6 | local systray = wibox.widget({
7 | widget = wibox.widget.systray(),
8 | horizontal = false,
9 | })
10 |
11 | return helpers.add_margin(systray, beautiful.margin[2], beautiful.margin[2])
12 | end
13 |
14 | return create_widget
15 |
--------------------------------------------------------------------------------
/components/bar/modules/taglist.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 |
7 | local taglist_buttons = gears.table.join(awful.button({}, 1, function(t)
8 | t:view_only()
9 | end),
10 | awful.button({}, 4, function(t)
11 | awful.tag.viewnext(t.screen)
12 | end),
13 | awful.button({}, 5, function(t)
14 | awful.tag.viewprev(t.screen)
15 | end))
16 |
17 | local function update_tag(self, tag, index, tags)
18 | local background = self:get_children_by_id("background_role")[1]
19 | local text = self:get_children_by_id("icon")[1]
20 |
21 | local urgent = false
22 | for _, client in ipairs(tag:clients()) do
23 | if client.urgent then
24 | urgent = true
25 | break
26 | end
27 | end
28 |
29 | if urgent then
30 | background.fg = beautiful.red
31 | text.text = ""
32 | elseif tag.selected and #tag:clients() > 0 then
33 | background.fg = beautiful.blue
34 | text.text = ""
35 | elseif tag.selected then
36 | background.fg = beautiful.blue
37 | text.text = ""
38 | elseif #tag:clients() > 0 then
39 | background.fg = beautiful.green
40 | text.text = ""
41 | else
42 | background.fg = beautiful.bg2
43 | text.text = ""
44 | end
45 | end
46 |
47 | local function create_widget(s)
48 | local taglist = awful.widget.taglist({
49 | screen = s,
50 | filter = awful.widget.taglist.filter.all,
51 | layout = {
52 | layout = wibox.layout.fixed.vertical,
53 | spacing = beautiful.margin[1],
54 | },
55 | style = {
56 | shape = helpers.rrect()
57 | },
58 | widget_template = {
59 | {
60 | widget = wibox.widget.textbox,
61 | font = beautiful.font_icon,
62 | valign = "center",
63 | halign = "center",
64 | id = "icon"
65 | },
66 | layout = wibox.container.background,
67 | id = "background_role",
68 | create_callback = update_tag,
69 | update_callback = update_tag
70 | },
71 | buttons = taglist_buttons
72 | })
73 |
74 | local widget = helpers.add_margin(taglist);
75 | return widget
76 | end
77 |
78 | return create_widget
79 |
--------------------------------------------------------------------------------
/components/bar/modules/tasklist.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 |
7 | local tasklist_buttons = gears.table.join(
8 | awful.button({}, 1, function(c)
9 | c:activate({ context = "tasklist", action = "toggle_minimization" })
10 | end))
11 |
12 | local function update(self, client, index, clients)
13 | local background = self:get_children_by_id("background")[1]
14 | local icon = self:get_children_by_id("icon")[1]
15 | local separator = self:get_children_by_id("separator")[1]
16 |
17 | icon.image = client.icon or beautiful.icon_default
18 | if client.active then
19 | background.bg = beautiful.bg2
20 | separator.visible = true
21 | else
22 | background.bg = beautiful.bg1
23 | separator.visible = false
24 | end
25 | end
26 |
27 | local function init(self, client, index, clients)
28 | local background = self:get_children_by_id("background")[1]
29 | local tooltip = helpers.add_tooltip(background, "" .. (client.class:gsub("^%l", string.upper)) .. "")
30 | update(self, client, index, clients)
31 | end
32 |
33 | local function create_widget(s)
34 | local tasklist = awful.widget.tasklist({
35 | screen = s,
36 | filter = awful.widget.tasklist.filter.currenttags,
37 | spacing = beautiful.margin[0],
38 | style = {
39 | shape = helpers.rrect()
40 | },
41 | layout = {
42 | spacing_widget = {
43 | {
44 | forced_width = beautiful.dpi(20),
45 | forced_height = beautiful.dpi(4),
46 | thickness = beautiful.dpi(2),
47 | color = beautiful.fg2,
48 | widget = wibox.widget.separator
49 | },
50 | valign = "center",
51 | halign = "center",
52 | widget = wibox.container.place,
53 | },
54 | spacing = beautiful.margin[1],
55 | layout = wibox.layout.fixed.vertical,
56 | },
57 | widget_template = {
58 | {
59 | {
60 | {
61 | widget = wibox.widget.imagebox,
62 | id = "icon",
63 | },
64 | layout = wibox.container.margin,
65 | margins = beautiful.margin[0],
66 | },
67 | layout = wibox.container.background,
68 | shape = helpers.rrect(),
69 | id = "background",
70 | },
71 | {
72 | {
73 | {
74 | widget = wibox.widget.separator,
75 | id = "separator",
76 | shape = helpers.rrect(),
77 | thickness = beautiful.dpi(2),
78 | forced_width = beautiful.dpi(2),
79 | forced_height = beautiful.dpi(16),
80 | color = beautiful.blue,
81 | },
82 | margins = beautiful.dpi(1),
83 | layout = wibox.container.margin
84 | },
85 | valign = "center",
86 | halign = "left",
87 | layout = wibox.container.place
88 | },
89 | layout = wibox.layout.stack,
90 | create_callback = init,
91 | update_callback = update
92 | },
93 | buttons = tasklist_buttons
94 | })
95 |
96 | local widget = helpers.add_margin(tasklist)
97 | return widget
98 | end
99 |
100 | return create_widget
101 |
--------------------------------------------------------------------------------
/components/bar/modules/volume.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local helpers = require("helpers")
4 | local beautiful = require("beautiful")
5 | local config = require("config")
6 |
7 | local function update(icon, background, tooltip, mute, vol)
8 | if mute then
9 | tooltip.markup = "Muted"
10 | icon.markup = ""
11 | background.fg = beautiful.red
12 | return
13 | end
14 |
15 | if vol == 0 then
16 | icon.markup = ""
17 | background.fg = beautiful.red
18 | elseif vol < 30 then
19 | icon.markup = ""
20 | background.fg = beautiful.purple
21 | elseif vol < 60 then
22 | icon.markup = ""
23 | background.fg = beautiful.blue
24 | else
25 | icon.markup = ""
26 | background.fg = beautiful.green
27 | end
28 |
29 | tooltip.markup = "Volume: " .. tostring(vol) .. "%"
30 | end
31 |
32 | local function create_widget()
33 | local icon = wibox.widget({
34 | font = beautiful.font_icon,
35 | markup = "",
36 | valign = "center",
37 | halign = "center",
38 | widget = wibox.widget.textbox
39 | })
40 |
41 | local background = helpers.add_bg0(icon)
42 |
43 | background:buttons({
44 | awful.button({}, 1, function()
45 | awful.spawn.with_shell(config.apps.volume_manager)
46 | end),
47 | awful.button({}, 2, function()
48 | awesome.emit_signal("volume::mute")
49 | end),
50 | awful.button({}, 4, function()
51 | awesome.emit_signal("volume::increase", 5)
52 | end),
53 | awful.button({}, 5, function()
54 | awesome.emit_signal("volume::decrease", 5)
55 | end)
56 | })
57 |
58 | local widget = helpers.add_margin(background, beautiful.margin[1], 0)
59 |
60 | local tooltip = helpers.add_tooltip(widget, "Volume")
61 |
62 | awesome.connect_signal("volume::update", function(mute, vol)
63 | update(icon, background, tooltip, mute, vol)
64 | end)
65 |
66 | return widget
67 | end
68 |
69 | return create_widget
70 |
--------------------------------------------------------------------------------
/components/bar/modules/wifi.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local helpers = require("helpers")
4 | local beautiful = require("beautiful")
5 | local config = require("config")
6 |
7 | local function update(icon, background, tooltip, id, eth_state, wifi_state)
8 | if id and (eth_state == "ACTIVATED" or wifi_state == "ACTIVATED") then
9 | icon.markup = ""
10 | background.fg = beautiful.blue
11 | tooltip.markup = "Wifi: " .. id
12 | return
13 | end
14 | icon.markup = ""
15 | background.fg = beautiful.red
16 | tooltip.markup = "Disconnected"
17 | end
18 |
19 | local function create_widget()
20 | local icon = wibox.widget({
21 | font = beautiful.font_icon,
22 | markup = "",
23 | valign = "center",
24 | halign = "center",
25 | widget = wibox.widget.textbox
26 | })
27 |
28 | local background = helpers.add_bg0(icon)
29 |
30 | background:buttons({
31 | awful.button({}, 1, function()
32 | awful.spawn.with_shell(config.apps.network_manager)
33 | end)
34 | })
35 |
36 | local widget = helpers.add_margin(background, beautiful.margin[1], 0)
37 |
38 | local tooltip = helpers.add_tooltip(widget, "Unknown")
39 |
40 | awesome.connect_signal("network::update", function(id, eth_state, wifi_state)
41 | update(icon, background, tooltip, id, eth_state, wifi_state)
42 | end)
43 |
44 | return widget
45 | end
46 | return create_widget
47 |
--------------------------------------------------------------------------------
/components/init.lua:
--------------------------------------------------------------------------------
1 | -- Initialize Components
2 |
3 | require("components.bar")
4 | require("components.switcher")
5 | require("components.sidebar")
6 | require("components.lock")
7 | require("components.osd")
8 |
--------------------------------------------------------------------------------
/components/lock/init.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 | local pam = require("liblua_pam")
7 |
8 | local clock = require("components.lock.modules.clock")
9 | local circle = require("components.lock.modules.circle")
10 |
11 | local M = {}
12 | M.visible = false
13 |
14 | function M.auth()
15 | return pam.auth_current_user(M.input)
16 | end
17 |
18 | function M.create_wiboxes()
19 | for s in screen do
20 | M.wiboxes[s] = wibox({
21 | widget = M.widget,
22 | visible = true,
23 | ontop = true,
24 | screen = s,
25 | width = s.geometry.width,
26 | height = s.geometry.height,
27 | x = s.geometry.x,
28 | y = s.geometry.y
29 | })
30 | end
31 | end
32 |
33 | function M.toggle()
34 | M.visible = not M.visible
35 | if M.visible then
36 | M.keygrabber:start()
37 | M.create_wiboxes()
38 | else
39 | M.input = ""
40 | for _, widget in pairs(M.wiboxes) do
41 | widget.visible = false
42 | end
43 | M.keygrabber:stop()
44 | end
45 | end
46 |
47 | function M.stop()
48 | circle.reset()
49 | M.visible = false
50 | M.input = ""
51 | for _, widget in pairs(M.wiboxes) do
52 | widget.visible = false
53 | end
54 | M.keygrabber:stop()
55 | end
56 |
57 | function M.new()
58 | M.clock = clock.new()
59 | M.circle = circle.new()
60 |
61 | M.background = wibox.widget({
62 | halign = "center",
63 | valign = "center",
64 | vertical_fit_policy = true,
65 | horizontal_fit_policy = true,
66 | resize = true,
67 | opacity = 0.5,
68 | clip_shape = helpers.rrect(),
69 | widget = wibox.widget.imagebox
70 | })
71 |
72 | M.widget = wibox.widget({
73 | M.background,
74 | {
75 | M.clock,
76 | valign = "center",
77 | halign = "center",
78 | layout = wibox.container.place
79 | },
80 | {
81 | M.circle,
82 | valign = "bottom",
83 | halign = "left",
84 | layout = wibox.container.place
85 | },
86 | layout = wibox.layout.stack
87 | })
88 |
89 | M.input = ""
90 | M.wiboxes = {}
91 |
92 | M.keygrabber = awful.keygrabber({
93 | stop_event = 'release',
94 | mask_event_callback = true,
95 | keybindings = {awful.key {
96 | modifiers = {'Mod1', 'Mod4', 'Shift', 'Control'},
97 | key = 'Return',
98 | on_press = function(_)
99 | M.input = M.input
100 | circle.random()
101 | end
102 | }},
103 | keypressed_callback = function(_, _, key, event)
104 | if event == "release" then
105 | return
106 | end
107 | if key == 'Escape' then
108 | M.input = ""
109 | circle.random()
110 | return
111 | end
112 |
113 | if key == "BackSpace" then
114 | if #M.input > 0 then
115 | circle.random()
116 | end
117 | M.input = M.input:sub(1, -2)
118 | end
119 |
120 | if #key == 1 then
121 | circle.random()
122 | if not M.input then
123 | M.input = key
124 | else
125 | M.input = M.input .. key
126 | end
127 | end
128 | end,
129 | keyreleased_callback = function(_, _, key, _)
130 | if key == "Return" then
131 | if M.auth() then
132 | M.stop()
133 | else
134 | M.input = ""
135 | end
136 | end
137 | end
138 | })
139 |
140 | M.background.image = gears.surface.load_uncached(beautiful.wallpaper)
141 |
142 | screen.connect_signal("list", function()
143 | if M.visible then
144 | for s, wibox in pairs(M.wiboxes) do
145 | wibox.visible = false
146 | end
147 | M.wiboxes = {}
148 | M.create_wiboxes()
149 | end
150 | end)
151 |
152 | awesome.connect_signal("lockscreen::toggle", function()
153 | M.toggle()
154 | end)
155 |
156 | return M
157 | end
158 |
159 | return M.new()
160 |
--------------------------------------------------------------------------------
/components/lock/modules/circle.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox")
2 | local beautiful = require("beautiful")
3 | local helpers = require("helpers")
4 | local naughty = require("naughty")
5 |
6 | local M = {}
7 |
8 | function M.random()
9 | local val = math.random(0, 360)
10 | M.arcchart.start_angle = val
11 | M.arcchart.value = 30
12 | end
13 |
14 | function M.reset()
15 | M.arcchart.value = nil
16 | M.background.fg = beautiful.red
17 | end
18 |
19 | function M.validate()
20 | M.background.fg = beautiful.green
21 | end
22 |
23 | function M.new()
24 | math.randomseed(os.time())
25 | M.icon = wibox.widget({
26 | markup = "",
27 | valign = "center",
28 | halign = "center",
29 | font = beautiful.font_icon_large,
30 | widget = wibox.widget.textbox
31 | })
32 |
33 | M.background = wibox.widget({
34 | M.icon,
35 | fg = beautiful.red,
36 | bg = beautiful.bg1,
37 | shape = helpers.circle(),
38 | layout = wibox.container.background
39 | })
40 |
41 | M.arcchart = wibox.widget({
42 | M.background,
43 | colors = { beautiful.blue },
44 | thickness = beautiful.dpi(8),
45 | rounded_edge = true,
46 | min_value = 0,
47 | max_value = 100,
48 | start_angle = 0,
49 | forced_width = beautiful.icon_size[4],
50 | forced_height = beautiful.icon_size[4],
51 | layout = wibox.container.arcchart
52 | })
53 |
54 | M.widget = helpers.add_margin(helpers.add_bg0(helpers.add_margin(M.arcchart, beautiful.margin[1], beautiful.margin[1])), beautiful.margin[4],
55 | beautiful.margin[4])
56 |
57 | return M.widget
58 | end
59 |
60 | return M
61 |
--------------------------------------------------------------------------------
/components/lock/modules/clock.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox")
2 | local beautiful = require("beautiful")
3 |
4 | local M = {}
5 |
6 | function M.new()
7 | M.date = wibox.widget({
8 | valign = "center",
9 | halign = "center",
10 | font = beautiful.font_medium,
11 | format = "%A, %B %e",
12 | widget = wibox.widget.textclock
13 | })
14 |
15 | M.time = wibox.widget({
16 | valign = "center",
17 | halign = "center",
18 | font = beautiful.font_large,
19 | format = "%l:%M %p",
20 | widget = wibox.widget.textclock
21 | })
22 |
23 | M.widget = wibox.widget({
24 | M.time,
25 | M.date,
26 | layout = wibox.layout.fixed.vertical
27 | })
28 |
29 | return M.widget
30 | end
31 |
32 | return M
33 |
--------------------------------------------------------------------------------
/components/osd/brightness.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 |
7 | local M = {}
8 |
9 | M.timeout = 2
10 |
11 | function M.update_widget(value)
12 | M.slider.value = value
13 | M.text.markup = tostring(value) .. "%"
14 | end
15 |
16 | function M.display_widget()
17 | M.wibox.screen = awful.screen.focused()
18 | awful.placement.bottom(M.wibox, {
19 | margins = {
20 | bottom = 2 * beautiful.useless_gap,
21 | }
22 | })
23 | M.wibox.visible = true
24 | M.timer:again()
25 | end
26 |
27 | function M.hide_widget()
28 | M.wibox.visible = false
29 | M.timer:stop()
30 | end
31 |
32 | function M.new()
33 | M.icon = wibox.widget({
34 | widget = wibox.widget.textbox,
35 | font = beautiful.font_icon_large,
36 | markup = "",
37 | halign = "center",
38 | valign = "center",
39 | forced_width = beautiful.icon_size[2],
40 | forced_height = beautiful.icon_size[2]
41 | })
42 |
43 | M.background = helpers.add_bg0(M.icon)
44 |
45 | M.text = wibox.widget({
46 | widget = wibox.widget.textbox,
47 | markup = "0%",
48 | halign = "center",
49 | valign = "center",
50 | forced_width = beautiful.icon_size[2],
51 | forced_height = beautiful.icon_size[2]
52 | })
53 |
54 | M.slider = wibox.widget({
55 | background_color = beautiful.bg1,
56 | color = beautiful.blue,
57 | thickness = beautiful.dpi(8),
58 | shape = helpers.rrect(),
59 | bar_shape = helpers.rrect(),
60 | max_value = 100,
61 | start_angle = 0,
62 | forced_width = beautiful.dpi(200),
63 | forced_height = beautiful.dpi(20),
64 | widget = wibox.widget.progressbar
65 | })
66 |
67 | M.widget = helpers.add_bg0(helpers.add_margin(wibox.widget({
68 | M.background,
69 | helpers.add_margin(M.slider, beautiful.margin[2], beautiful.margin[2]),
70 | M.text,
71 | layout = wibox.layout.fixed.horizontal
72 | }), beautiful.margin[2], beautiful.margin[2]))
73 |
74 | M.wibox = awful.popup({
75 | width = beautiful.dpi(400),
76 | height = beautiful.dpi(400),
77 | widget = M.widget,
78 | ontop = true,
79 | visible = false,
80 | border_color = beautiful.border_color_active,
81 | border_width = beautiful.border_width,
82 | shape = helpers.rrect(),
83 | })
84 |
85 | M.timer = gears.timer({
86 | timeout = M.timeout,
87 | autostart = false,
88 | call_now = false,
89 | callback = function()
90 | M.wibox.visible = false
91 | end,
92 | single_shot = true
93 | })
94 |
95 | awesome.connect_signal("brightness::update", function(value)
96 | M.update_widget(value)
97 | end)
98 |
99 | awesome.connect_signal("brightness::osd", function()
100 | M.display_widget()
101 | end)
102 |
103 | awesome.connect_signal("volume::osd", function()
104 | M.hide_widget()
105 | end)
106 |
107 | return M.wibox
108 | end
109 |
110 | return M
111 |
--------------------------------------------------------------------------------
/components/osd/init.lua:
--------------------------------------------------------------------------------
1 | require("components.osd.brightness").new()
2 | require("components.osd.volume").new()
3 |
--------------------------------------------------------------------------------
/components/osd/volume.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 |
7 | local M = {}
8 |
9 | M.timeout = 2
10 |
11 | function M.update_widget(mute, vol)
12 | if mute then
13 | M.icon.markup = ""
14 | M.background.fg = beautiful.red
15 | M.slider.color = beautiful.red
16 | return
17 | end
18 |
19 | if vol == 0 then
20 | M.icon.markup = ""
21 | M.background.fg = beautiful.red
22 | M.slider.color = beautiful.red
23 | elseif vol < 30 then
24 | M.icon.markup = ""
25 | M.background.fg = beautiful.purple
26 | M.slider.color = beautiful.purple
27 | elseif vol < 60 then
28 | M.icon.markup = ""
29 | M.background.fg = beautiful.blue
30 | M.slider.color = beautiful.blue
31 | else
32 | M.icon.markup = ""
33 | M.background.fg = beautiful.green
34 | M.slider.color = beautiful.green
35 | end
36 | M.text.markup = tostring(vol) .. "%"
37 | M.slider.value = vol
38 | end
39 |
40 | function M.display_widget()
41 | M.wibox.screen = awful.screen.focused()
42 | awful.placement.bottom(M.wibox, {
43 | margins = {
44 | bottom = 2 * beautiful.useless_gap,
45 | }
46 | })
47 | M.wibox.visible = true
48 | M.timer:again()
49 | end
50 |
51 | function M.hide_widget()
52 | M.wibox.visible = false
53 | M.timer:stop()
54 | end
55 |
56 | function M.new()
57 | M.icon = wibox.widget({
58 | widget = wibox.widget.textbox,
59 | font = beautiful.font_icon_large,
60 | markup = "",
61 | halign = "center",
62 | valign = "center",
63 | forced_width = beautiful.icon_size[2],
64 | forced_height = beautiful.icon_size[2]
65 | })
66 |
67 | M.background = helpers.add_bg0(M.icon)
68 |
69 | M.text = wibox.widget({
70 | widget = wibox.widget.textbox,
71 | markup = "0%",
72 | halign = "center",
73 | valign = "center",
74 | forced_width = beautiful.icon_size[2],
75 | forced_height = beautiful.icon_size[2]
76 | })
77 |
78 | M.slider = wibox.widget({
79 | background_color = beautiful.bg1,
80 | color = beautiful.blue,
81 | thickness = beautiful.dpi(8),
82 | shape = helpers.rrect(),
83 | bar_shape = helpers.rrect(),
84 | max_value = 100,
85 | start_angle = 0,
86 | forced_width = beautiful.dpi(200),
87 | forced_height = beautiful.dpi(20),
88 | widget = wibox.widget.progressbar
89 | })
90 |
91 | M.widget = helpers.add_bg0(helpers.add_margin(wibox.widget({
92 | M.background,
93 | helpers.add_margin(M.slider, beautiful.margin[2], beautiful.margin[2]),
94 | M.text,
95 | layout = wibox.layout.fixed.horizontal
96 | }), beautiful.margin[2], beautiful.margin[2]))
97 |
98 | M.wibox = awful.popup({
99 | width = beautiful.dpi(400),
100 | height = beautiful.dpi(400),
101 | widget = M.widget,
102 | ontop = true,
103 | visible = false,
104 | border_color = beautiful.border_color_active,
105 | border_width = beautiful.border_width,
106 | shape = helpers.rrect(),
107 | })
108 |
109 | M.timer = gears.timer({
110 | timeout = M.timeout,
111 | autostart = false,
112 | call_now = false,
113 | callback = function()
114 | M.wibox.visible = false
115 | end,
116 | single_shot = true
117 | })
118 |
119 | awesome.connect_signal("volume::update", function(mute, value)
120 | M.update_widget(mute, value)
121 | end)
122 |
123 | awesome.connect_signal("volume::osd", function()
124 | M.display_widget()
125 | end)
126 |
127 | awesome.connect_signal("brightness::osd", function()
128 | M.hide_widget()
129 | end)
130 |
131 | return M.wibox
132 | end
133 |
134 | return M
135 |
--------------------------------------------------------------------------------
/components/sidebar/init.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local beautiful = require("beautiful")
4 | local helpers = require("helpers")
5 |
6 | local header = require("components.sidebar.modules.header")
7 | local launcher = require("components.sidebar.modules.launcher")
8 | local media = require("components.sidebar.modules.media")
9 | local status = require("components.sidebar.modules.status")
10 | local notifications = require("components.sidebar.modules.notifications")
11 |
12 | local M = {}
13 |
14 | function M.toggle()
15 | M.wibox.visible = not M.wibox.visible
16 | if M.wibox.visible then
17 | M.wibox.height = awful.screen.focused().geometry.height - 4 * beautiful.useless_gap
18 | M.wibox.screen = awful.screen.focused()
19 | awful.placement.top_left(M.wibox, {
20 | margins = {
21 | top = 2 * beautiful.useless_gap,
22 | left = beautiful.bar_width + 2 * beautiful.useless_gap
23 | }
24 | })
25 | launcher.input = ""
26 | launcher.prompt.markup = "|"
27 | launcher.get_apps()
28 | launcher.update_apps()
29 | awful.spawn.easy_async_with_shell("echo $USER", function(stdout)
30 | stdout = stdout:gsub("[\n\r]", ""):gsub("^%l", string.upper)
31 | header.name.markup = "" .. stdout .. ""
32 | end)
33 | launcher.keygrabber:start()
34 | else
35 | launcher.keygrabber:stop()
36 | end
37 | end
38 |
39 | function M.stop()
40 | M.wibox.visible = false
41 | launcher.keygrabber:stop()
42 | end
43 |
44 | function M.new()
45 | M.header = header.new()
46 | M.launcher = launcher.new()
47 | M.media = media.new()
48 | M.status = status.new()
49 | M.notifications = notifications.new()
50 |
51 | M.widget = helpers.add_margin(wibox.widget({
52 | M.header,
53 | M.launcher,
54 | M.media,
55 | M.status,
56 | M.notifications,
57 | fill_space = true,
58 | spacing = beautiful.margin[0],
59 | layout = wibox.layout.fixed.vertical
60 | }), beautiful.margin[2], beautiful.margin[2])
61 |
62 | M.wibox = wibox({
63 | widget = M.widget,
64 | shape = helpers.rrect(),
65 | ontop = true,
66 | visible = false,
67 | width = beautiful.dpi(600),
68 | height = awful.screen.focused().geometry.height - 4 * beautiful.useless_gap,
69 | border_color = beautiful.border_color_active,
70 | border_width = beautiful.border_width,
71 | })
72 |
73 | awesome.connect_signal("launcher::toggle", function()
74 | M.toggle()
75 | end)
76 |
77 | awesome.connect_signal("launcher::stop", function()
78 | M.stop()
79 | end)
80 | end
81 |
82 | M.new()
83 |
--------------------------------------------------------------------------------
/components/sidebar/modules/header.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local gears = require("gears")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 | local M = {}
7 |
8 | M.buttons = {
9 | {
10 | icon = "",
11 | cmd = "systemctl poweroff",
12 | text = "Shutdown",
13 | color = beautiful.red,
14 | },
15 | {
16 | icon = "",
17 | cmd = "systemctl reboot",
18 | text = "Restart",
19 | color = beautiful.orange,
20 | },
21 | {
22 | icon = "",
23 | cmd = "awesome-client 'awesome.emit_signal(\"lockscreen::toggle\")'",
24 | text = "Lock",
25 | color = beautiful.green,
26 | },
27 | {
28 | icon = "",
29 | cmd = "awesome-client 'awesome.quit()'",
30 | text = "Exit",
31 | color = beautiful.blue,
32 | }
33 | }
34 |
35 | function M.create_button(icon, cmd, text, color)
36 | local icon_widget = wibox.widget({
37 | markup = icon,
38 | font = beautiful.font_icon,
39 | forced_width = beautiful.icon_size[2],
40 | forced_height = beautiful.icon_size[2],
41 | valign = "center",
42 | halign = "center",
43 | widget = wibox.widget.textbox
44 | })
45 |
46 | local widget = helpers.add_bg1(helpers.add_margin(icon_widget))
47 | widget.fg = color
48 | widget:buttons({
49 | awful.button({}, 1, function()
50 | awesome.emit_signal("launcher::stop")
51 | awful.spawn.with_shell(cmd)
52 | end)
53 | })
54 |
55 | widget:connect_signal("mouse::enter", function()
56 | widget.bg = beautiful.bg2
57 | end)
58 |
59 | widget:connect_signal("mouse::leave", function()
60 | widget.bg = beautiful.bg1
61 | end)
62 |
63 | return widget
64 | end
65 |
66 | function M.new()
67 | M.pfp = wibox.widget({
68 | image = gears.filesystem.get_configuration_dir() .. "assets/pfp.jpg",
69 | halign = "center",
70 | valign = "center",
71 | forced_height = beautiful.icon_size[2],
72 | forced_width = beautiful.icon_size[2],
73 | resize = true,
74 | clip_shape = gears.shape.circle,
75 | widget = wibox.widget.imagebox
76 | })
77 |
78 | M.uptime_text = wibox.widget({
79 | valign = "center",
80 | halign = "left",
81 | widget = wibox.widget.textbox
82 | })
83 |
84 | M.uptime = helpers.add_bg0(M.uptime_text)
85 | M.uptime.fg = beautiful.bg2
86 |
87 | M.name = wibox.widget({
88 | markup = "",
89 | valign = "center",
90 | halign = "left",
91 | widget = wibox.widget.textbox
92 | })
93 |
94 | M.button_group = wibox.widget({
95 | spacing = beautiful.margin[2],
96 | layout = wibox.layout.flex.horizontal
97 | })
98 |
99 | for _, button in ipairs(M.buttons) do
100 | M.button_group:add(M.create_button(button.icon, button.cmd, button.text, button.color))
101 | end
102 |
103 | M.widget = helpers.add_margin(wibox.widget({
104 | {
105 | M.pfp,
106 | {
107 | M.name,
108 | M.uptime,
109 | layout = wibox.layout.fixed.vertical
110 | },
111 | spacing = beautiful.margin[1],
112 | forced_height = beautiful.icon_size[2],
113 | layout = wibox.layout.fixed.horizontal
114 | },
115 | nil,
116 | M.button_group,
117 | expand = "none",
118 | layout = wibox.layout.align.horizontal
119 | }))
120 |
121 | awesome.connect_signal("uptime::update", function(stdout)
122 | M.uptime_text.markup = stdout
123 | end)
124 |
125 | return M.widget
126 | end
127 |
128 | return M
129 |
--------------------------------------------------------------------------------
/components/sidebar/modules/launcher.lua:
--------------------------------------------------------------------------------
1 | local Gio = require("lgi").Gio
2 | local awful = require("awful")
3 | local wibox = require("wibox")
4 | local beautiful = require("beautiful")
5 | local helpers = require("helpers")
6 | local naughty = require("naughty")
7 | local gears = require("gears")
8 |
9 | local M = {}
10 |
11 | M.pinned = { "Nemo", "Firefox Web Browser", "Vesktop", "Alacritty", "Spotify" }
12 | M.num_visible = 5 -- Number of visible apps in launcher at one time
13 |
14 | function M.get_apps()
15 | M.apps = {}
16 | local app_info = Gio.AppInfo
17 | local apps = app_info.get_all()
18 |
19 | for _, app in pairs(apps) do
20 | local name = app_info.get_display_name(app)
21 | local cmd = app_info.get_commandline(app)
22 | local exec = app_info.get_executable(app)
23 | local icon = helpers.get_gicon_path(app_info.get_icon(app)) or nil
24 | local description = Gio.AppInfo.get_description(app)
25 | local filter = name
26 |
27 | local pinned = false
28 | for _, pin_app in pairs(M.pinned) do
29 | if name == pin_app then
30 | pinned = true
31 | break
32 | end
33 | end
34 |
35 | if pinned then
36 | filter = "aaaaaaaaaa" .. filter
37 | end
38 |
39 | if name and exec and icon then
40 | table.insert(M.apps, {
41 | name = "" .. name .. "",
42 | cmd = cmd,
43 | exec = exec,
44 | icon = icon,
45 | filter = filter,
46 | description = description,
47 | })
48 | end
49 | end
50 | end
51 |
52 | function M.create_default()
53 | local image = wibox.widget({
54 | image = beautiful.icon_terminal,
55 | resize = true,
56 | valign = "center",
57 | halign = "center",
58 | forced_height = beautiful.icon_size[2],
59 | forced_width = beautiful.icon_size[2],
60 | widget = wibox.widget.imagebox,
61 | })
62 |
63 | local name = wibox.widget({
64 | markup = "Run in terminal",
65 | valign = "center",
66 | halign = "left",
67 | widget = wibox.widget.textbox,
68 | })
69 |
70 | local description = wibox.widget({
71 | text = "Run prompt input in terminal",
72 | valign = "center",
73 | halign = "left",
74 | widget = wibox.widget.textbox,
75 | })
76 |
77 | local background = helpers.add_bg0(helpers.add_margin(wibox.widget({
78 | image,
79 | {
80 | name,
81 | description,
82 | layout = wibox.layout.fixed.vertical,
83 | },
84 | spacing = beautiful.margin[1],
85 | layout = wibox.layout.fixed.horizontal,
86 | })))
87 |
88 | background:buttons(gears.table.join(awful.button({}, 1, function()
89 | M.run_default()
90 | end)))
91 |
92 | background:connect_signal("mouse::enter", function()
93 | background.bg = beautiful.bg1
94 | end)
95 | background:connect_signal("mouse::leave", function()
96 | background.bg = beautiful.bg0
97 | end)
98 |
99 | return background
100 | end
101 |
102 | function M.create_app_widget(app, default_bg)
103 | local image = wibox.widget({
104 | image = app.icon or beautiful.icon_default,
105 | resize = true,
106 | valign = "center",
107 | halign = "center",
108 | forced_height = beautiful.icon_size[2],
109 | forced_width = beautiful.icon_size[2],
110 | widget = wibox.widget.imagebox,
111 | })
112 |
113 | local name = wibox.widget({
114 | markup = app.name or "Unknown",
115 | valign = "center",
116 | halign = "left",
117 | widget = wibox.widget.textbox,
118 | })
119 |
120 | local description = wibox.widget({
121 | text = app.description,
122 | valign = "center",
123 | halign = "left",
124 | widget = wibox.widget.textbox,
125 | })
126 |
127 | local background = helpers.add_bg0(helpers.add_margin(wibox.widget({
128 | image,
129 | {
130 | name,
131 | description,
132 | layout = wibox.layout.fixed.vertical,
133 | },
134 | spacing = beautiful.margin[1],
135 | layout = wibox.layout.fixed.horizontal,
136 | })))
137 |
138 | background.bg = default_bg
139 |
140 | background:buttons(gears.table.join(awful.button({}, 1, function()
141 | M.run_app(app)
142 | end)))
143 |
144 | background:connect_signal("mouse::enter", function()
145 | background.bg = beautiful.blue
146 | background.fg = beautiful.bg0
147 | end)
148 | background:connect_signal("mouse::leave", function()
149 | background.bg = default_bg
150 | background.fg = beautiful.fg0
151 | end)
152 |
153 | return background
154 | end
155 |
156 | function M.update_apps()
157 | M.matches = {}
158 | for _, app in pairs(M.apps) do
159 | if app.filter:lower():find(M.input:lower():gsub("%W", "%%%0")) then
160 | table.insert(M.matches, app)
161 | end
162 | end
163 |
164 | table.sort(M.matches, function(a, b)
165 | return a.filter:lower() < b.filter:lower()
166 | end)
167 |
168 | M.list:reset()
169 |
170 | local selected = false
171 | for i = M.selected, math.min(M.selected + M.num_visible, #M.matches) do
172 | local app = M.matches[i]
173 | local default_bg = beautiful.bg0
174 | if i == M.selected then
175 | default_bg = beautiful.bg1
176 | selected = true
177 | end
178 | local app_widget = M.create_app_widget(app, default_bg)
179 | M.list:add(app_widget)
180 | end
181 |
182 | if M.selected + M.num_visible > #M.matches then
183 | local default = M.create_default()
184 | if not selected then
185 | default.bg = beautiful.bg1
186 | end
187 | M.list:add(default)
188 | end
189 | end
190 |
191 | function M.run_app(app)
192 | awful.spawn(app.exec)
193 | awesome.emit_signal("launcher::stop")
194 | end
195 |
196 | function M.run_default()
197 | awful.spawn(M.prompt.text:sub(1, -2))
198 | awesome.emit_signal("launcher::stop")
199 | end
200 |
201 | function M.keypressed_callback(_, mod, key, event)
202 | if event == "release" then
203 | return
204 | end
205 |
206 | if key == "BackSpace" then
207 | M.input = M.input:sub(1, -2)
208 | end
209 |
210 | if key == "Escape" then
211 | awesome.emit_signal("launcher::stop")
212 | end
213 |
214 | if key == "Down" then
215 | M.selected = M.selected + 1
216 | if M.selected == #M.matches + 2 then
217 | M.selected = 1
218 | end
219 | end
220 |
221 | if key == "Up" then
222 | M.selected = M.selected - 1
223 | if M.selected == 0 then
224 | M.selected = #M.matches + 1
225 | end
226 | end
227 |
228 | if key == "Return" then
229 | if M.selected <= #M.matches then
230 | M.run_app(M.matches[M.selected])
231 | else
232 | M.run_default()
233 | end
234 | awesome.emit_signal("launcher::stop")
235 | end
236 |
237 | if #key == 1 then
238 | if not M.input then
239 | M.input = key
240 | else
241 | M.input = M.input .. key
242 | end
243 | end
244 |
245 | if not M.input or M.input == "" then
246 | M.prompt.text = "|"
247 | else
248 | M.prompt.text = M.input .. "|"
249 | end
250 | M.update_apps()
251 | end
252 |
253 | function M.new()
254 | M.apps = {}
255 | M.matches = {}
256 | M.input = ""
257 | M.selected = 1
258 |
259 | M.keygrabber = awful.keygrabber({
260 | mask_event_callback = true,
261 | keypressed_callback = M.keypressed_callback,
262 | })
263 |
264 | M.side_text = wibox.widget({
265 | markup = "",
266 | font = beautiful.font_icon,
267 | valign = "center",
268 | halign = "center",
269 | widget = wibox.widget.textbox,
270 | })
271 |
272 | M.prompt = wibox.widget({
273 | text = "|",
274 | valign = "center",
275 | halign = "left",
276 | widget = wibox.widget.textbox,
277 | })
278 |
279 | M.list = wibox.widget({
280 | spacing = beautiful.margin[1],
281 | layout = wibox.layout.fixed.vertical,
282 | })
283 |
284 | M.search = helpers.add_bg1(helpers.add_margin(
285 | wibox.widget({
286 | M.side_text,
287 | M.prompt,
288 | spacing = beautiful.margin[1],
289 | layout = wibox.layout.fixed.horizontal,
290 | }),
291 | beautiful.margin[3],
292 | beautiful.margin[3]
293 | ))
294 |
295 | M.widget = wibox.widget({
296 | helpers.add_margin(wibox.widget({
297 | M.search,
298 | M.list,
299 | spacing = beautiful.margin[1],
300 | layout = wibox.layout.fixed.vertical,
301 | })),
302 | bg = beautiful.bg0,
303 | forced_height = beautiful.dpi(400),
304 | layout = wibox.container.background,
305 | })
306 |
307 | M.widget:buttons({
308 | awful.button({}, 4, function()
309 | M.selected = M.selected - 1
310 | if M.selected == 0 then
311 | M.selected = #M.matches + 1
312 | end
313 | M.update_apps()
314 | end),
315 | awful.button({}, 5, function()
316 | M.selected = M.selected + 1
317 | if M.selected == #M.matches + 2 then
318 | M.selected = 1
319 | end
320 | M.update_apps()
321 | end),
322 | })
323 |
324 | return M.widget
325 | end
326 |
327 | return M
328 |
--------------------------------------------------------------------------------
/components/sidebar/modules/media.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local beautiful = require("beautiful")
4 | local gears = require("gears")
5 | local helpers = require("helpers")
6 |
7 | local M = {}
8 |
9 | function M.new()
10 | M.art = wibox.widget({
11 | image = beautiful.icon_music,
12 | resize = true,
13 | halign = "center",
14 | valign = "center",
15 | forced_height = beautiful.icon_size[3],
16 | forced_width = beautiful.icon_size[3],
17 | clip_shape = helpers.rrect(),
18 | widget = wibox.widget.imagebox
19 | })
20 |
21 | M.title_text = wibox.widget({
22 | markup = "Playing",
23 | halign = "left",
24 | valign = "center",
25 | widget = wibox.widget.textbox
26 | })
27 |
28 | M.title = wibox.widget({
29 | layout = wibox.container.scroll.horizontal,
30 | step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth,
31 | fps = 60,
32 | speed = 75,
33 | M.title_text
34 | })
35 |
36 | M.title:pause()
37 |
38 | M.artist_text = wibox.widget({
39 | markup = "Nothing",
40 | halign = "left",
41 | valign = "center",
42 | widget = wibox.widget.textbox
43 | })
44 |
45 | M.artist = wibox.widget({
46 | layout = wibox.container.scroll.horizontal,
47 | step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth,
48 | fps = 60,
49 | speed = 75,
50 | M.artist_text
51 | })
52 |
53 | M.artist:pause()
54 |
55 | M.toggle_icon = wibox.widget({
56 | markup = "",
57 | widget = wibox.widget.textbox,
58 | font = beautiful.font_icon_large,
59 | forced_height = beautiful.icon_size[1],
60 | forced_width = beautiful.icon_size[1],
61 | halign = "center",
62 | valign = "center"
63 | })
64 |
65 | M.prev_icon = wibox.widget({
66 | markup = "",
67 | widget = wibox.widget.textbox,
68 | font = beautiful.font_icon_large,
69 | forced_height = beautiful.icon_size[1],
70 | forced_width = beautiful.icon_size[1],
71 | halign = "center",
72 | valign = "center"
73 | })
74 |
75 | M.next_icon = wibox.widget({
76 | markup = "",
77 | widget = wibox.widget.textbox,
78 | font = beautiful.font_icon_large,
79 | forced_height = beautiful.icon_size[1],
80 | forced_width = beautiful.icon_size[1],
81 | halign = "center",
82 | valign = "center"
83 | })
84 |
85 | M.toggle = helpers.add_bg1(helpers.add_margin(M.toggle_icon))
86 | M.prev = helpers.add_bg1(helpers.add_margin(M.prev_icon))
87 | M.next = helpers.add_bg1(helpers.add_margin(M.next_icon))
88 |
89 | M.prev.fg = beautiful.blue
90 | M.next.fg = beautiful.blue
91 |
92 | M.toggle_icon:buttons(gears.table.join(awful.button({}, 1, function()
93 | awesome.emit_signal("playerctl::toggle")
94 | end)))
95 |
96 | M.next_icon:buttons(gears.table.join(awful.button({}, 1, function()
97 | awesome.emit_signal("playerctl::next")
98 | end)))
99 |
100 | M.prev_icon:buttons(gears.table.join(awful.button({}, 1, function()
101 | awesome.emit_signal("playerctl::prev")
102 | end)))
103 |
104 | M.button_group = helpers.add_margin(wibox.widget({
105 | M.prev,
106 | M.toggle,
107 | M.next,
108 | layout = wibox.layout.flex.horizontal
109 | }))
110 |
111 | M.text_group = helpers.add_margin(wibox.widget({
112 | M.artist,
113 | M.title,
114 | layout = wibox.layout.flex.vertical
115 | }))
116 |
117 | M.widget = helpers.add_margin(helpers.add_bg1(helpers.add_margin(wibox.widget({
118 | helpers.add_margin(M.art, beautiful.margin[1], beautiful.margin[1]),
119 | M.text_group,
120 | M.button_group,
121 | layout = wibox.layout.align.horizontal
122 | }))))
123 |
124 | M.widget:connect_signal("mouse::enter", function()
125 | M.title:continue()
126 | M.artist:continue()
127 | end)
128 |
129 | M.widget:connect_signal("mouse::leave", function()
130 | M.title:pause()
131 | M.artist:pause()
132 | end)
133 |
134 | awesome.connect_signal("playerctl::metadata::update", function(title, artist, player_name, album)
135 | M.artist_text.markup = artist or "Nothing"
136 | M.title_text.markup = title or "Playing"
137 | end)
138 |
139 | awesome.connect_signal("playerctl::art::update", function(art_path)
140 | M.art.image = art_path and gears.surface.load_uncached_silently(art_path) or beautiful.icon_music
141 | end)
142 |
143 | awesome.connect_signal("playerctl::toggle::update", function(playing)
144 | if playing then
145 | M.toggle_icon.markup = ""
146 | else
147 | M.toggle_icon.markup = ""
148 | end
149 | end)
150 |
151 | return M.widget
152 | end
153 |
154 | return M
155 |
--------------------------------------------------------------------------------
/components/sidebar/modules/notifications.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local beautiful = require("beautiful")
4 | local naughty = require("naughty")
5 | local helpers = require("helpers")
6 |
7 | local M = {}
8 |
9 | function M.new()
10 | M.erase_icon = wibox.widget({
11 | markup = "",
12 | font = beautiful.font_icon,
13 | valign = "center",
14 | halign = "right",
15 | widget = wibox.widget.textbox
16 | })
17 |
18 | M.erase = helpers.add_bg1(M.erase_icon)
19 | M.erase.fg = beautiful.blue
20 |
21 | M.tooltip = helpers.add_tooltip(M.erase, "Clear All")
22 |
23 | M.notifications = wibox.widget({
24 | spacing = beautiful.margin[0],
25 | layout = wibox.layout.overflow.vertical,
26 | forced_height = beautiful.dpi(1000),
27 | scrollbar_width = 10,
28 | step = 50
29 | })
30 |
31 | M.notifications:set_scrollbar_widget(wibox.widget({
32 | shape = helpers.rrect(),
33 | widget = wibox.widget.separator
34 | }))
35 |
36 | M.erase:buttons(awful.util.table.join(awful.button({}, 1, function()
37 | M.notifications:reset()
38 | end)))
39 |
40 | M.widget = helpers.add_margin(helpers.add_bg1(helpers.add_margin(wibox.widget({
41 | helpers.add_margin(wibox.widget({
42 | {
43 | markup = "Notifications",
44 | widget = wibox.widget.textbox
45 | },
46 | nil,
47 | M.erase,
48 | layout = wibox.layout.align.horizontal,
49 | }), beautiful.margin[1], beautiful.margin[1]),
50 | M.notifications,
51 | spacing = beautiful.margin[1],
52 | layout = wibox.layout.fixed.vertical
53 | }))))
54 |
55 | naughty.connect_signal("request::display", function(n)
56 | local close_icon = wibox.widget({
57 | valign = "center",
58 | halign = "center",
59 | forced_width = beautiful.icon_size[1],
60 | forced_height = beautiful.icon_size[1],
61 | markup = "",
62 | font = beautiful.font_icon,
63 | widget = wibox.widget.textbox
64 | })
65 |
66 | local close_button = helpers.add_bg0(close_icon)
67 | close_button.fg = beautiful.red
68 |
69 | local icon = nil
70 | if n.clients[1] ~= nil then
71 | icon = wibox.widget({
72 | client = n.clients[1],
73 | forced_height = beautiful.icon_size[0],
74 | forced_width = beautiful.icon_size[0],
75 | widget = awful.widget.clienticon
76 | })
77 | elseif n.app_icon then
78 | icon = wibox.widget({
79 | image = n.app_icon,
80 | resize = true,
81 | forced_height = beautiful.icon_size[0],
82 | forced_width = beautiful.icon_size[0],
83 | clip_shape = helpers.rrect(),
84 | widget = wibox.widget.imagebox
85 | })
86 | end
87 |
88 | local image = nil
89 | if n.icon then
90 | image = wibox.widget({
91 | image = n.icon,
92 | resize = true,
93 | forced_height = beautiful.icon_size[3],
94 | forced_width = beautiful.icon_size[3],
95 | halign = "right",
96 | valign = "center",
97 | clip_shape = helpers.rrect(),
98 | widget = wibox.widget.imagebox
99 | })
100 | end
101 |
102 | local title = wibox.widget({
103 | layout = wibox.container.scroll.horizontal,
104 | step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth,
105 | fps = 60,
106 | speed = 75,
107 | {
108 | halign = "left",
109 | valign = "center",
110 | markup = "" .. n.title .. "",
111 | widget = wibox.widget.textbox
112 | }
113 | })
114 |
115 | title:pause()
116 |
117 | local message = wibox.widget({
118 | layout = wibox.container.scroll.horizontal,
119 | step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth,
120 | fps = 60,
121 | speed = 75,
122 | {
123 | halign = "left",
124 | valign = "center",
125 | markup = n.message,
126 | widget = wibox.widget.textbox
127 | }
128 | })
129 |
130 | message:pause()
131 |
132 | local app_name = wibox.widget({
133 | valign = "center",
134 | halign = "left",
135 | markup = "" .. n.app_name .. "",
136 | widget = wibox.widget.textbox
137 | })
138 |
139 | local actions = wibox.widget({
140 | notification = n,
141 | base_layout = wibox.widget({
142 | spacing = beautiful.margin[1],
143 | layout = wibox.layout.flex.horizontal
144 | }),
145 | widget_template = {
146 | {
147 | id = "text_role",
148 | valign = "center",
149 | halign = "center",
150 | widget = wibox.widget.textbox
151 | },
152 | shape = helpers.rrect(),
153 | bg = beautiful.blue,
154 | fg = beautiful.bg0,
155 | forced_height = beautiful.dpi(30),
156 | visible = n.actions and #n.actions > 0,
157 | widget = wibox.container.background
158 | },
159 | style = {
160 | underline_normal = false,
161 | underline_selected = true
162 | },
163 | widget = naughty.list.actions
164 | })
165 |
166 | local time = wibox.widget({
167 | markup = os.date("%I:%M %p"),
168 | halign = "right",
169 | valign = "center",
170 | widget = wibox.widget.textbox
171 | })
172 |
173 | local title_bar = helpers.add_margin(wibox.widget({
174 | {
175 | {
176 | icon,
177 | valign = "center",
178 | halign = "center",
179 | layout = wibox.container.place,
180 | },
181 | app_name,
182 | spacing = beautiful.margin[0],
183 | layout = wibox.layout.fixed.horizontal
184 | },
185 | nil,
186 | {
187 | time,
188 | close_button,
189 | spacing = beautiful.margin[0],
190 | layout = wibox.layout.fixed.horizontal
191 | },
192 | layout = wibox.layout.align.horizontal,
193 | }), beautiful.margin[1], beautiful.margin[0])
194 |
195 | local body = helpers.add_margin(wibox.widget({
196 | {
197 | image,
198 | {
199 | title,
200 | message,
201 | spacing = beautiful.margin[1],
202 | layout = wibox.layout.fixed.vertical
203 | },
204 | spacing = beautiful.margin[1],
205 | fill_space = true,
206 | layout = wibox.layout.fixed.horizontal
207 | },
208 | actions,
209 | layout = wibox.layout.fixed.vertical
210 | }), beautiful.margin[1], beautiful.margin[1])
211 | body.shape = nil
212 |
213 | local widget = helpers.add_bg0(wibox.widget({
214 | title_bar,
215 | body,
216 | forced_height = (n.actions and #n.actions > 0) and beautiful.dpi(150) or beautiful.dpi(120),
217 | layout = wibox.layout.fixed.vertical
218 | }))
219 |
220 | widget:connect_signal("mouse::enter", function()
221 | title:continue()
222 | message:continue()
223 | end)
224 |
225 | widget:connect_signal("mouse::leave", function()
226 | title:pause()
227 | message:pause()
228 | end)
229 |
230 | close_button:buttons({ awful.button({}, 1, function()
231 | M.notifications:remove_widgets(widget, true)
232 | end) })
233 |
234 | M.notifications:insert(1, widget)
235 | end)
236 |
237 | return M.widget
238 | end
239 |
240 | return M
241 |
--------------------------------------------------------------------------------
/components/sidebar/modules/status.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local beautiful = require("beautiful")
4 | local helpers = require("helpers")
5 | local config = require("config")
6 |
7 | local M = {}
8 |
9 | function M.create_status_widget(icon, color, hover_text)
10 | local icon_widget = wibox.widget({
11 | markup = icon,
12 | font = beautiful.font_icon_large,
13 | valign = "center",
14 | halign = "center",
15 | widget = wibox.widget.textbox
16 | })
17 |
18 | local progressbar = wibox.widget({
19 | icon_widget,
20 | colors = { color },
21 | bg = beautiful.bg0,
22 | thickness = beautiful.dpi(8),
23 | forced_height = beautiful.dpi(74),
24 | paddings = beautiful.dpi(2),
25 | rounded_edge = true,
26 | min_value = 0,
27 | max_value = 100,
28 | widget = wibox.container.arcchart
29 | })
30 |
31 | local text = wibox.widget({
32 | markup = "100%",
33 | valign = "center",
34 | halign = "center",
35 | widget = wibox.widget.textbox
36 | })
37 |
38 | local widget = helpers.add_bg1(helpers.add_margin(wibox.widget({
39 | helpers.add_margin(progressbar, beautiful.margin[2], beautiful.margin[2]),
40 | text,
41 | spacing = beautiful.margin[1],
42 | layout = wibox.layout.fixed.vertical
43 | })))
44 | widget.fg = color
45 |
46 | local tooltip = helpers.add_tooltip(widget, hover_text)
47 |
48 | return { widget = widget, icon = icon_widget, text = text, progressbar = progressbar, tooltip = tooltip }
49 | end
50 |
51 | function M.new()
52 | local battery = M.create_status_widget("", beautiful.green, "Battery")
53 | awesome.connect_signal("battery::update", function(d)
54 | local percentage = math.floor(d.percentage)
55 | if percentage < 25 then
56 | battery.progressbar.colors[1] = beautiful.red
57 | battery.widget.fg = beautiful.red
58 | elseif percentage < 50 then
59 | battery.progressbar.colors[1] = beautiful.orange
60 | battery.widget.fg = beautiful.orange
61 | elseif percentage < 75 then
62 | battery.progressbar.colors[1] = beautiful.blue
63 | battery.widget.fg = beautiful.blue
64 | else
65 | battery.progressbar.colors[1] = beautiful.green
66 | battery.widget.fg = beautiful.green
67 | end
68 | if d.state == 1 or d.state == 4 then
69 | battery.icon.markup = ""
70 | end
71 | battery.tooltip.markup = "Battery: " .. tostring(percentage) .. "%"
72 | battery.text.markup = tostring(percentage) .. "%"
73 | battery.progressbar.value = percentage
74 | end)
75 |
76 | local volume = M.create_status_widget("", beautiful.green, "Volume")
77 | awesome.connect_signal("volume::update", function(mute, vol)
78 | if mute then
79 | volume.tooltip.markup = "Muted"
80 | volume.icon.markup = ""
81 | volume.widget.fg = beautiful.red
82 | volume.progressbar.colors[1] = beautiful.red
83 | return
84 | end
85 |
86 | if vol == 0 then
87 | volume.icon.markup = ""
88 | volume.widget.fg = beautiful.red
89 | volume.progressbar.colors[1] = beautiful.red
90 | elseif vol < 30 then
91 | volume.icon.markup = ""
92 | volume.widget.fg = beautiful.purple
93 | volume.progressbar.colors[1] = beautiful.purple
94 | elseif vol < 60 then
95 | volume.icon.markup = ""
96 | volume.widget.fg = beautiful.blue
97 | volume.progressbar.colors[1] = beautiful.blue
98 | else
99 | volume.icon.markup = ""
100 | volume.widget.fg = beautiful.green
101 | volume.progressbar.colors[1] = beautiful.green
102 | end
103 | volume.text.markup = tostring(vol).."%"
104 | volume.progressbar.value = vol
105 | volume.tooltip.markup = "Volume: " .. tostring(vol) .. "%"
106 | end)
107 | volume.widget:buttons({
108 | awful.button({}, 1, function()
109 | awful.spawn.with_shell(config.apps.volume_manager)
110 | end),
111 | awful.button({}, 2, function()
112 | awesome.emit_signal("volume::mute")
113 | end),
114 | awful.button({}, 4, function()
115 | awesome.emit_signal("volume::increase", 5)
116 | end),
117 | awful.button({}, 5, function()
118 | awesome.emit_signal("volume::decrease", 5)
119 | end)
120 | })
121 |
122 | local brightness = M.create_status_widget("", beautiful.green, "Brightness")
123 | awesome.connect_signal("brightness::update", function(value)
124 | if value < 25 then
125 | brightness.widget.fg = beautiful.red
126 | brightness.progressbar.colors[1] = beautiful.red
127 | elseif value < 50 then
128 | brightness.widget.fg = beautiful.purple
129 | brightness.progressbar.colors[1] = beautiful.purple
130 | elseif value < 75 then
131 | brightness.widget.fg = beautiful.blue
132 | brightness.progressbar.colors[1] = beautiful.blue
133 | else
134 | brightness.widget.fg = beautiful.green
135 | brightness.progressbar.colors[1] = beautiful.green
136 | end
137 | brightness.text.markup = tostring(value).."%"
138 | brightness.progressbar.value = value
139 | brightness.tooltip.markup = "Brightness: "..tostring(value).."%"
140 | end)
141 | brightness.widget:buttons({
142 | awful.button({}, 4, function()
143 | awesome.emit_signal("brightness::increase", 5)
144 | end),
145 | awful.button({}, 5, function()
146 | awesome.emit_signal("brightness::decrease", 5)
147 | end)
148 | })
149 |
150 | local cpu = M.create_status_widget("", beautiful.green, "CPU")
151 | awesome.connect_signal("cpu::update", function(value)
152 | if value < 25 then
153 | cpu.widget.fg = beautiful.green
154 | cpu.progressbar.colors[1] = beautiful.green
155 | elseif value < 50 then
156 | cpu.widget.fg = beautiful.blue
157 | cpu.progressbar.colors[1] = beautiful.blue
158 | elseif value < 75 then
159 | cpu.widget.fg = beautiful.purple
160 | cpu.progressbar.colors[1] = beautiful.purple
161 | else
162 | cpu.widget.fg = beautiful.red
163 | cpu.progressbar.colors[1] = beautiful.red
164 | end
165 | cpu.text.markup = tostring(value).."%"
166 | cpu.progressbar.value = value
167 | cpu.tooltip.markup = "CPU: "..tostring(value).."%"
168 | end)
169 |
170 | local memory = M.create_status_widget("", beautiful.green, "Memory")
171 | awesome.connect_signal("memory::update", function(value)
172 | if value < 25 then
173 | memory.widget.fg = beautiful.green
174 | memory.progressbar.colors[1] = beautiful.green
175 | elseif value < 50 then
176 | memory.widget.fg = beautiful.blue
177 | memory.progressbar.colors[1] = beautiful.blue
178 | elseif value < 75 then
179 | memory.widget.fg = beautiful.purple
180 | memory.progressbar.colors[1] = beautiful.purple
181 | else
182 | memory.widget.fg = beautiful.red
183 | memory.progressbar.colors[1] = beautiful.red
184 | end
185 | memory.text.markup = tostring(value).."%"
186 | memory.progressbar.value = value
187 | memory.tooltip.markup = "Memory: "..tostring(value).."%"
188 | end)
189 |
190 | M.widget = helpers.add_margin(wibox.widget({
191 | battery.widget,
192 | volume.widget,
193 | brightness.widget,
194 | cpu.widget,
195 | memory.widget,
196 | spacing = beautiful.margin[2],
197 | layout = wibox.layout.flex.horizontal
198 | }))
199 |
200 | return M.widget
201 | end
202 |
203 | return M
204 |
--------------------------------------------------------------------------------
/components/switcher/init.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local beautiful = require("beautiful")
4 | local helpers = require("helpers")
5 |
6 | local M = {}
7 |
8 | function M.create_default()
9 | local image = wibox.widget({
10 | markup = "",
11 | font = beautiful.font_icon_large,
12 | valign = "center",
13 | halign = "center",
14 | widget = wibox.widget.textbox
15 | })
16 |
17 | local name = wibox.widget({
18 | markup = "Desktop",
19 | valign = "center",
20 | halign = "center",
21 | forced_width = beautiful.dpi(150),
22 | forced_height = beautiful.dpi(50),
23 | widget = wibox.widget.textbox
24 | })
25 |
26 | local background = wibox.widget({
27 | bg = beautiful.bg0,
28 | shape = helpers.rrect(),
29 | widget = wibox.container.background
30 | })
31 |
32 | local widget = wibox.widget({
33 | background,
34 | {
35 | image,
36 | valign = "center",
37 | halign = "center",
38 | layout = wibox.container.place
39 | },
40 | {
41 | name,
42 | valign = "bottom",
43 | halign = "center",
44 | layout = wibox.container.place
45 | },
46 | layout = wibox.layout.stack
47 | })
48 |
49 | M.list:add(widget)
50 | end
51 |
52 | function M.create_widget(c)
53 | local icon = c.icon or helpers.get_icon(c.class) or beautiful.icon_default
54 |
55 | local image = wibox.widget({
56 | image = icon,
57 | resize = true,
58 | valign = "center",
59 | halign = "center",
60 | forced_width = beautiful.icon_size[3],
61 | forced_height = beautiful.icon_size[3],
62 | widget = wibox.widget.imagebox
63 | })
64 |
65 | local name = wibox.widget({
66 | markup = c.class and c.class:gsub("^%l", string.upper) or "Unknown",
67 | valign = "center",
68 | halign = "center",
69 | forced_width = beautiful.dpi(150),
70 | forced_height = beautiful.dpi(50),
71 | widget = wibox.widget.textbox
72 | })
73 |
74 | local background = wibox.widget({
75 | bg = beautiful.bg0,
76 | shape = helpers.rrect(),
77 | widget = wibox.container.background
78 | })
79 |
80 | local widget = wibox.widget({
81 | background,
82 | {
83 | image,
84 | valign = "center",
85 | halign = "center",
86 | layout = wibox.container.place
87 | },
88 | {
89 | name,
90 | valign = "bottom",
91 | halign = "center",
92 | layout = wibox.container.place
93 | },
94 | layout = wibox.layout.stack
95 | })
96 |
97 | M.list:add(widget)
98 |
99 | table.insert(M.clients, {
100 | background = background,
101 | client = c
102 | })
103 | end
104 |
105 | function M.place()
106 | M.wibox.screen = awful.screen.focused()
107 | awful.placement.centered(M.wibox)
108 | end
109 |
110 | function M.update_clients()
111 | M.list:reset()
112 | M.clients = {}
113 | for _, c in ipairs(M.history) do
114 | M.create_widget(c)
115 | end
116 |
117 | for _, i in ipairs(awful.screen.focused().selected_tag:clients()) do
118 | local in_history = false
119 | for _, j in ipairs(M.history) do
120 | if i == j then
121 | in_history = true
122 | break
123 | end
124 | end
125 | if not in_history then
126 | M.create_widget(i)
127 | end
128 | end
129 |
130 | if #M.clients == 0 then
131 | M.create_default()
132 | M.wibox.width = beautiful.switcher_width
133 | else
134 | M.wibox.width = #M.clients * beautiful.switcher_width
135 | end
136 | M.wibox.height = beautiful.switcher_height
137 | M.place()
138 | end
139 |
140 | function M.cycle()
141 | local cur = -1
142 | for i, c in ipairs(M.clients) do
143 | if c.client == client.focus then
144 | cur = i
145 | break
146 | end
147 | end
148 |
149 | if cur ~= -1 then
150 | M.clients[cur].background.bg = beautiful.bg0
151 | M.clients[cur].client.minimized = M.client_minimized
152 | else
153 | cur = 1
154 | end
155 |
156 | if cur == #M.clients then
157 | cur = 1
158 | else
159 | cur = cur + 1
160 | end
161 |
162 | if M.clients[cur] then
163 | M.clients[cur].background.bg = beautiful.bg2
164 |
165 | M.client_minimized = M.clients[cur].client.minimized
166 | M.clients[cur].client:jump_to()
167 | for i, c in ipairs(M.history) do
168 | if c == M.clients[cur].client then
169 | table.remove(M.history, i)
170 | end
171 | end
172 | table.insert(M.history, 1, M.clients[cur].client)
173 | end
174 | end
175 |
176 | function M.keypressed_callback(_, mod, key, event)
177 | if key == "Tab" then
178 | M.cycle()
179 | end
180 | end
181 |
182 | function M.keyreleased_callback(_, mod, key, event)
183 | if key:match("Alt") then
184 | M.stop()
185 | end
186 | end
187 |
188 | function M.new()
189 | M.clients = {}
190 | M.history = {}
191 |
192 | M.keygrabber = awful.keygrabber({
193 | keypressed_callback = M.keypressed_callback,
194 | keyreleased_callback = M.keyreleased_callback
195 | })
196 |
197 | M.list = wibox.widget({
198 | spacing = beautiful.margin[0],
199 | layout = wibox.layout.flex.horizontal
200 | })
201 |
202 | M.widget = helpers.add_bg0(helpers.add_margin(M.list))
203 |
204 | M.wibox = wibox({
205 | widget = M.widget,
206 | screen = awful.screen.focused(),
207 | ontop = true,
208 | visible = false,
209 | shape = helpers.rrect(),
210 | border_color = beautiful.border_color_active,
211 | border_width = beautiful.border_width,
212 | })
213 |
214 | awesome.connect_signal("switcher::toggle", function()
215 | M.toggle()
216 | end)
217 |
218 | tag.connect_signal("property::selected", function(t)
219 | M.history = {}
220 | end)
221 |
222 | return M
223 | end
224 |
225 | function M.toggle()
226 | M.wibox.visible = not M.wibox.visible
227 | if M.wibox.visible then
228 | M.client_minimized = false
229 | M.place()
230 | M.update_clients()
231 | M.cycle()
232 | M.keygrabber:start()
233 | else
234 | M.keygrabber:stop()
235 | end
236 | end
237 |
238 | function M.stop()
239 | M.wibox.visible = false
240 | M.keygrabber:stop()
241 | end
242 |
243 | return M.new()
244 |
--------------------------------------------------------------------------------
/config.lua:
--------------------------------------------------------------------------------
1 | -- User specific config here
2 | local M = {}
3 |
4 | M.modkey = "Mod4"
5 |
6 | M.apps = {
7 | terminal = "alacritty",
8 | editor = "nvim",
9 | browser = "firefox",
10 | file_manager = "nemo",
11 | volume_manager = "pavucontrol",
12 | network_manager = "nm-connection-editor",
13 | power_manager = "xfce4-power-manager-settings",
14 | bluetooth_manager = "blueman-manager",
15 | settings = "xfce4-settings-manager",
16 | }
17 |
18 | M.editor_cmd = M.apps.terminal.." -e "..M.apps.editor
19 |
20 | return M
21 |
--------------------------------------------------------------------------------
/daemons/battery.lua:
--------------------------------------------------------------------------------
1 | local upower = require('lgi').require('UPowerGlib')
2 | local gears = require("gears")
3 |
4 | local M = {}
5 |
6 | M.interval = 5
7 | M.device = upower.Client():get_display_device()
8 |
9 | function M.update()
10 | awesome.emit_signal("battery::update", M.device)
11 | end
12 |
13 | function M.start()
14 | gears.timer {
15 | timeout = M.interval,
16 | autostart = true,
17 | call_now = true,
18 | callback = M.update
19 | }
20 | end
21 |
22 | M.start()
23 |
--------------------------------------------------------------------------------
/daemons/brightness.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 |
4 | local M = {}
5 |
6 | M.interval = 5
7 | M.script = "xbacklight -get"
8 |
9 | function M.update()
10 | awful.spawn.easy_async_with_shell(M.script, function(stdout)
11 | stdout = stdout:gsub("[\n\r]", "")
12 | local bright = tonumber(stdout)
13 | if bright then
14 | awesome.emit_signal("brightness::update", bright)
15 | end
16 | end)
17 | end
18 |
19 | function M.start()
20 | gears.timer {
21 | timeout = M.interval,
22 | autostart = true,
23 | call_now = true,
24 | callback = M.update
25 | }
26 |
27 | awesome.connect_signal("brightness::increase", function(i)
28 | awful.spawn.with_shell("xbacklight -inc "..tostring(i))
29 | M.update()
30 | end)
31 | awesome.connect_signal("brightness::decrease", function(d)
32 | awful.spawn.with_shell("xbacklight -dec "..tostring(d))
33 | M.update()
34 | end)
35 | end
36 |
37 | M.start()
38 |
--------------------------------------------------------------------------------
/daemons/cpu.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local naughty = require("naughty")
4 |
5 | local M = {}
6 |
7 | M.interval = 5
8 | M.script = "top -bn1 | grep 'Cpu(s)' | sed 's/.*, *\\([0-9.]*\\)%* id.*/\\1/' | awk '{print 100 - $1\"%\"}'"
9 |
10 | function M.update()
11 | awful.spawn.easy_async_with_shell(M.script, function(stdout)
12 | stdout = stdout:gsub("[\n\r]", "")
13 | local cpu = tonumber(stdout:sub(1, -2))
14 | if cpu then
15 | awesome.emit_signal("cpu::update", math.floor(cpu))
16 | end
17 | end)
18 | end
19 |
20 | function M.start()
21 | gears.timer {
22 | timeout = M.interval,
23 | autostart = true,
24 | call_now = true,
25 | callback = M.update
26 | }
27 | end
28 |
29 | M.start()
30 |
--------------------------------------------------------------------------------
/daemons/init.lua:
--------------------------------------------------------------------------------
1 | require("daemons.battery")
2 | require("daemons.network")
3 | require("daemons.volume")
4 | require("daemons.playerctl")
5 | require("daemons.cpu")
6 | require("daemons.memory")
7 | require("daemons.brightness")
8 | require("daemons.uptime")
9 |
--------------------------------------------------------------------------------
/daemons/memory.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 |
4 | local M = {}
5 |
6 | M.interval = 5
7 | M.script = "free -m | grep Mem | awk '{print ($3/$2)*100}'"
8 |
9 | function M.update()
10 | awful.spawn.easy_async_with_shell(M.script, function(stdout)
11 | local memory = tonumber(stdout)
12 | awesome.emit_signal("memory::update", math.floor(memory))
13 | end)
14 | end
15 |
16 | function M.start()
17 | gears.timer {
18 | timeout = M.interval,
19 | autostart = true,
20 | call_now = true,
21 | callback = M.update
22 | }
23 | end
24 |
25 | M.start()
26 |
--------------------------------------------------------------------------------
/daemons/network.lua:
--------------------------------------------------------------------------------
1 | local naughty = require("naughty")
2 | local gtimer = require("gears.timer")
3 |
4 | local lgi = require("lgi")
5 | local nm = lgi.NM
6 |
7 | local M = {}
8 |
9 | function M.start()
10 | M.client = nm.Client.new()
11 | M.devices = M.client:get_devices()
12 | for i, d in ipairs(M.devices) do
13 | local info = d:get_device_type()
14 | if (info == "ETHERNET") then
15 | M.eth = d
16 | end
17 | if (info == "WIFI") then
18 | M.wifi = d
19 | end
20 | end
21 |
22 | if M.eth then
23 | M.eth.on_state_changed = function()
24 | local id = nil
25 | local eth_state = nil
26 | local wifi_state = nil
27 | if M.client.primary_connection and M.client.primary_connection.id then
28 | id = M.client.primary_connection.id
29 | end
30 | if M.eth and M.eth.state then
31 | eth_state = M.eth.state
32 | end
33 | if M.wifi and M.wifi.state then
34 | wifi_state = M.wifi.state
35 | end
36 | awesome.emit_signal("network::update", id, eth_state, wifi_state)
37 | end
38 | end
39 | if M.wifi then
40 | M.wifi.on_state_changed = function()
41 | local id = nil
42 | local eth_state = nil
43 | local wifi_state = nil
44 | if M.client.primary_connection and M.client.primary_connection.id then
45 | id = M.client.primary_connection.id
46 | end
47 | if M.eth and M.eth.state then
48 | eth_state = M.eth.state
49 | end
50 | if M.wifi and M.wifi.state then
51 | wifi_state = M.wifi.state
52 | end
53 | awesome.emit_signal("network::update", id, eth_state, wifi_state)
54 | end
55 | end
56 | local id = nil
57 | local eth_state = nil
58 | local wifi_state = nil
59 | if M.client.primary_connection and M.client.primary_connection.id then
60 | id = M.client.primary_connection.id
61 | end
62 | if M.eth and M.eth.state then
63 | eth_state = M.eth.state
64 | end
65 | if M.wifi and M.wifi.state then
66 | wifi_state = M.wifi.state
67 | end
68 | gtimer.delayed_call(awesome.emit_signal, 'network::update', id, eth_state, wifi_state)
69 | end
70 |
71 | M.start()
72 |
--------------------------------------------------------------------------------
/daemons/playerctl.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local naughty = require("naughty")
4 |
5 | local M = {}
6 |
7 | function M.update_toggle()
8 | awful.spawn.with_line_callback("playerctl -F status", {
9 | stdout = function(stdout)
10 | stdout = stdout:gsub("[\n\r]", "")
11 | if stdout == "Playing" then
12 | awesome.emit_signal("playerctl::toggle::update", true)
13 | else
14 | awesome.emit_signal("playerctl::toggle::update", false)
15 | end
16 | end
17 | })
18 | end
19 |
20 | function M.update_art()
21 | awful.spawn.with_line_callback(
22 | "playerctl -F metadata --format '{{mpris:artUrl}}'",
23 | {
24 | stdout = function(stdout)
25 | local art_url = stdout or ""
26 |
27 | art_url = art_url:gsub('%\n', '')
28 | art_url = art_url:gsub("open.spotify.com", "i.scdn.co")
29 | local art_path = ""
30 | if art_url ~= nil then
31 | art_path = os.tmpname()
32 | awful.spawn.easy_async_with_shell("curl -L -s " .. art_url .. " -o " .. art_path, function()
33 | awesome.emit_signal("playerctl::art::update", art_path)
34 | end)
35 | end
36 | end
37 | })
38 | end
39 |
40 | function M.update_metadata()
41 | awful.spawn.with_line_callback(
42 | "playerctl -F metadata --format 'title_{{title}}artist_{{artist}}player_name_{{playerName}}album_{{album}}'",
43 | {
44 | stdout = function(stdout)
45 | local title = gears.string.xml_escape(stdout:match('title_(.*)artist_')) or nil
46 | local artist = gears.string.xml_escape(stdout:match('artist_(.*)player_name_')) or nil
47 | local player_name = stdout:match('player_name_(.*)album_') or nil
48 | local album = gears.string.xml_escape(stdout:match('album_(.*)')) or nil
49 | awesome.emit_signal("playerctl::metadata::update", title, artist, player_name, album)
50 | end
51 | })
52 | end
53 |
54 | function M.start()
55 | awful.spawn.easy_async({ 'pkill', '--full', '--uid', os.getenv('USER'), '^playerctl -F' },
56 | function()
57 | M.update_toggle()
58 | M.update_art()
59 | M.update_metadata()
60 | end)
61 |
62 | awesome.connect_signal("playerctl::next", function()
63 | awful.spawn.with_shell("playerctl next")
64 | end)
65 |
66 | awesome.connect_signal("playerctl::prev", function()
67 | awful.spawn.with_shell("playerctl previous")
68 | end)
69 |
70 | awesome.connect_signal("playerctl::toggle", function()
71 | awful.spawn.with_shell("playerctl play-pause")
72 | end)
73 | end
74 |
75 | M.start()
76 |
--------------------------------------------------------------------------------
/daemons/uptime.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 |
4 | local M = {}
5 |
6 | M.interval = 5
7 | M.script = "uptime -p"
8 |
9 | function M.update()
10 | awful.spawn.easy_async_with_shell(M.script, function(stdout)
11 | awesome.emit_signal("uptime::update", stdout)
12 | end)
13 | end
14 |
15 | function M.start()
16 | gears.timer {
17 | timeout = M.interval,
18 | autostart = true,
19 | call_now = true,
20 | callback = M.update
21 | }
22 | end
23 |
24 | M.start()
25 |
--------------------------------------------------------------------------------
/daemons/volume.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local naughty = require("naughty")
3 |
4 | local M = {}
5 | M.mute = false
6 | M.vol = 0
7 |
8 | function M.update()
9 | awful.spawn.easy_async_with_shell("pamixer --get-mute && pamixer --get-volume", function(stdout)
10 | local info = {}
11 | for line in stdout:gmatch("[^\r\n]+") do
12 | table.insert(info, line)
13 | end
14 | if info[1] == "false" then
15 | M.mute = false
16 | else
17 | M.mute = true
18 | end
19 | M.vol = tonumber(info[2])
20 | awesome.emit_signal("volume::update", M.mute, M.vol)
21 | end)
22 | end
23 |
24 | function M.start()
25 | -- Initial values
26 | M.update()
27 |
28 | awful.spawn.easy_async({
29 | 'pkill', '--full', '--uid', os.getenv('USER'), '^pactl subscribe'
30 | }, function()
31 | awful.spawn.with_line_callback([[
32 | bash -c "
33 | LANG=C pactl subscribe 2> /dev/null | grep --line-buffered \"Event 'change' on sink\"
34 | "]], {
35 | stdout = function(line) M.update() end
36 | })
37 | end)
38 |
39 | awesome.connect_signal("volume::increase", function(i)
40 | awful.spawn.with_shell("pamixer -i " .. tostring(i))
41 | end)
42 |
43 | awesome.connect_signal("volume::decrease", function(d)
44 | awful.spawn.with_shell("pamixer -d " .. tostring(d))
45 | end)
46 |
47 | awesome.connect_signal("volume::mute", function()
48 | awful.spawn.with_shell("pamixer -t")
49 | end)
50 | end
51 |
52 | M.start()
53 |
--------------------------------------------------------------------------------
/helpers.lua:
--------------------------------------------------------------------------------
1 | -- Contains a bunch of useful helper functions
2 |
3 | local awful = require("awful")
4 | local gears = require("gears")
5 | local wibox = require("wibox")
6 | local beautiful = require("beautiful")
7 | local lgi = require("lgi")
8 | local Gio = lgi.Gio
9 | local Gtk = lgi.require("Gtk", "3.0")
10 |
11 | local M = {}
12 |
13 | function M.rrect()
14 | return function(cr, width, height)
15 | gears.shape.rounded_rect(cr, width, height, beautiful.radius)
16 | end
17 | end
18 |
19 | function M.circle()
20 | return function(cr, width, height)
21 | gears.shape.circle(cr, width, height)
22 | end
23 | end
24 |
25 | -- Makes widget change color on hover
26 | function M.add_click(widget, color)
27 | color = color or beautiful.blue
28 | local background = wibox.widget({
29 | widget,
30 | shape = M.rrect(),
31 | layout = wibox.container.background,
32 | })
33 |
34 | background:connect_signal("mouse::enter", function()
35 | background.fg = beautiful.bg0
36 | background.bg = color
37 | end)
38 |
39 | background:connect_signal("mouse::leave", function()
40 | background.fg = beautiful.fg0
41 | background.bg = "#00000000"
42 | end)
43 |
44 | return background
45 | end
46 |
47 | -- Adds background to widget
48 | function M.add_bg0(widget)
49 | local background = wibox.widget({
50 | widget,
51 | layout = wibox.container.background,
52 | bg = beautiful.bg0,
53 | fg = beautiful.fg0,
54 | shape = M.rrect()
55 | })
56 | return background
57 | end
58 |
59 | function M.add_bg1(widget)
60 | local background = wibox.widget({
61 | widget,
62 | layout = wibox.container.background,
63 | bg = beautiful.bg1,
64 | fg = beautiful.fg0,
65 | shape = M.rrect()
66 | })
67 | return background
68 | end
69 |
70 | function M.add_bg(widget)
71 | return wibox.widget({
72 | widget,
73 | shape = M.rrect(),
74 | layout = wibox.container.background
75 | })
76 | end
77 |
78 | -- Creates tooltip
79 | function M.add_tooltip(widget, markup)
80 | return awful.tooltip({
81 | objects = { widget },
82 | markup = markup,
83 | shape = M.rrect(),
84 | margins_leftright = beautiful.margin[1],
85 | margins_topbottom = beautiful.margin[1],
86 | border_width = beautiful.border_width,
87 | border_color = beautiful.border_color_active,
88 | })
89 | end
90 |
91 | -- Adds margin to widget
92 | function M.add_margin(widget, h, v)
93 | h = h or beautiful.margin[0]
94 | v = v or beautiful.margin[0]
95 | return wibox.container.margin(widget, h, h, v, v)
96 | end
97 |
98 | -- Icons
99 | M.gtk_theme = Gtk.IconTheme.get_default()
100 | M.apps = Gio.AppInfo.get_all()
101 |
102 | function M.get_icon(client_name)
103 | if not client_name then
104 | return nil
105 | end
106 |
107 | local icon_info = M.gtk_theme:lookup_icon(client_name, beautiful.icon_size[3], 0)
108 | if icon_info then
109 | local icon_path = icon_info:get_filename()
110 | if icon_path then
111 | return icon_path
112 | end
113 | end
114 |
115 | return nil
116 | end
117 |
118 | function M.get_gicon_path(gicon)
119 | if not gicon then
120 | return nil
121 | end
122 |
123 | local info = M.gtk_theme:lookup_by_gicon(gicon, beautiful.icon_size[3], 0)
124 | if info then
125 | return info:get_filename()
126 | end
127 | end
128 |
129 | return M
130 |
--------------------------------------------------------------------------------
/keys.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local hotkeys_popup = require("awful.hotkeys_popup")
3 | local gears = require("gears")
4 | local config = require("config")
5 |
6 | local modkey = config.modkey
7 | local apps = config.apps
8 |
9 | -- General Awesome keys
10 | awful.keyboard.append_global_keybindings({
11 | awful.key({ modkey }, "i", hotkeys_popup.show_help, { description = "show help", group = "awesome" }),
12 | awful.key({ modkey, "Control" }, "r", awesome.restart, { description = "reload awesome", group = "awesome" }),
13 | awful.key({ modkey, "Shift" }, "q", awesome.quit, { description = "quit awesome", group = "awesome" }),
14 | awful.key({ modkey }, "Return", function()
15 | awful.spawn(apps.terminal)
16 | end, { description = "open a terminal", group = "launcher" }),
17 | awful.key({ modkey }, "p", function()
18 | awesome.emit_signal("launcher::toggle")
19 | end, { description = "show the menubar", group = "launcher" }),
20 | awful.key({ modkey, "Shift" }, "s", function()
21 | awful.spawn.with_shell("sh " .. gears.filesystem.get_configuration_dir() .. "screenshot area")
22 | end, { description = "screenshot selection", group = "launcher" }),
23 | awful.key({}, "Print", function()
24 | awful.spawn.with_shell("sh " .. gears.filesystem.get_configuration_dir() .. "screenshot full")
25 | end, { description = "screenshot screen", group = "launcher" }),
26 | awful.key({ "Mod1", "Shift" }, "x", function()
27 | awesome.emit_signal("lockscreen::toggle")
28 | end, { description = "lock screen", group = "launcher" }),
29 | awful.key({
30 | modifiers = {},
31 | key = "XF86MonBrightnessUp",
32 | description = "brightness up",
33 | group = "launcher",
34 | on_press = function()
35 | awesome.emit_signal("brightness::osd")
36 | awesome.emit_signal("brightness::increase", 5)
37 | end,
38 | }),
39 | awful.key({
40 | modifiers = {},
41 | key = "XF86MonBrightnessDown",
42 | description = "brightness down",
43 | group = "launcher",
44 | on_press = function()
45 | awesome.emit_signal("brightness::osd")
46 | awesome.emit_signal("brightness::decrease", 5)
47 | end,
48 | }),
49 | awful.key({
50 | modifiers = {},
51 | key = "XF86AudioRaiseVolume",
52 | description = "volume up",
53 | group = "launcher",
54 | on_press = function()
55 | awesome.emit_signal("volume::osd")
56 | awesome.emit_signal("volume::increase", 5)
57 | end,
58 | }),
59 |
60 | awful.key({
61 | modifiers = {},
62 | key = "XF86AudioLowerVolume",
63 | description = "volume down",
64 | group = "launcher",
65 | on_press = function()
66 | awesome.emit_signal("volume::osd")
67 | awesome.emit_signal("volume::decrease", 5)
68 | end,
69 | }),
70 |
71 | awful.key({
72 | modifiers = {},
73 | key = "XF86AudioMute",
74 | description = "toggle mute",
75 | group = "launcher",
76 | on_press = function()
77 | awesome.emit_signal("volume::osd")
78 | awesome.emit_signal("volume::mute")
79 | end,
80 | }),
81 | })
82 |
83 | -- Focus related keybindings
84 | awful.keyboard.append_global_keybindings({
85 | awful.key({ modkey }, "j", function()
86 | awful.client.focus.byidx(1)
87 | end, { description = "focus next by index", group = "client" }),
88 | awful.key({ modkey }, "k", function()
89 | awful.client.focus.byidx(-1)
90 | end, { description = "focus previous by index", group = "client" }),
91 | awful.key({ "Mod1" }, "Tab", function()
92 | awesome.emit_signal("switcher::toggle")
93 | end, { description = "window switcher", group = "client" }),
94 | awful.key({ modkey }, "Left", function()
95 | awful.screen.focus_relative(1)
96 | end, { description = "focus the next screen", group = "screen" }),
97 | awful.key({ modkey }, "Right", function()
98 | awful.screen.focus_relative(-1)
99 | end, { description = "focus the previous screen", group = "screen" }),
100 | })
101 |
102 | -- Layout related keybindings
103 | awful.keyboard.append_global_keybindings({
104 | awful.key({ modkey, "Shift" }, "j", function()
105 | awful.client.swap.byidx(1)
106 | end, { description = "swap with next client by index", group = "client" }),
107 | awful.key({ modkey, "Shift" }, "k", function()
108 | awful.client.swap.byidx(-1)
109 | end, {
110 | description = "swap with previous client by index",
111 | group = "client",
112 | }),
113 | awful.key({ modkey }, "u", awful.client.urgent.jumpto, { description = "jump to urgent client", group = "client" }),
114 | awful.key({ modkey }, "l", function()
115 | awful.tag.incmwfact(0.05)
116 | end, { description = "increase master width factor", group = "layout" }),
117 | awful.key({ modkey }, "h", function()
118 | awful.tag.incmwfact(-0.05)
119 | end, { description = "decrease master width factor", group = "layout" }),
120 | awful.key({ modkey, "Shift" }, "h", function()
121 | awful.tag.incnmaster(1, nil, true)
122 | end, {
123 | description = "increase the number of master clients",
124 | group = "layout",
125 | }),
126 | awful.key({ modkey, "Shift" }, "l", function()
127 | awful.tag.incnmaster(-1, nil, true)
128 | end, {
129 | description = "decrease the number of master clients",
130 | group = "layout",
131 | }),
132 | awful.key({ modkey, "Control" }, "h", function()
133 | awful.tag.incncol(1, nil, true)
134 | end, {
135 | description = "increase the number of columns",
136 | group = "layout",
137 | }),
138 | awful.key({ modkey, "Control" }, "l", function()
139 | awful.tag.incncol(-1, nil, true)
140 | end, {
141 | description = "decrease the number of columns",
142 | group = "layout",
143 | }),
144 | awful.key({ modkey }, "space", function()
145 | awful.layout.inc(1, awful.screen.focused())
146 | end, {
147 | description = "cycle layouts",
148 | group = "layout",
149 | }),
150 | })
151 |
152 | awful.keyboard.append_global_keybindings({
153 | awful.key({
154 | modifiers = { modkey },
155 | keygroup = "numrow",
156 | description = "only view tag",
157 | group = "tag",
158 | on_press = function(index)
159 | local screen = awful.screen.focused()
160 | local tag = screen.tags[index]
161 | if tag then
162 | tag:view_only()
163 | end
164 | end,
165 | }),
166 | awful.key({
167 | modifiers = { modkey, "Control" },
168 | keygroup = "numrow",
169 | description = "toggle tag",
170 | group = "tag",
171 | on_press = function(index)
172 | local screen = awful.screen.focused()
173 | local tag = screen.tags[index]
174 | if tag then
175 | awful.tag.viewtoggle(tag)
176 | end
177 | end,
178 | }),
179 | awful.key({
180 | modifiers = { modkey, "Shift" },
181 | keygroup = "numrow",
182 | description = "move focused client to tag",
183 | group = "tag",
184 | on_press = function(index)
185 | if client.focus then
186 | local tag = client.focus.screen.tags[index]
187 | if tag then
188 | client.focus:move_to_tag(tag)
189 | end
190 | end
191 | end,
192 | }),
193 | awful.key({
194 | modifiers = { modkey, "Control", "Shift" },
195 | keygroup = "numrow",
196 | description = "toggle focused client on tag",
197 | group = "tag",
198 | on_press = function(index)
199 | if client.focus then
200 | local tag = client.focus.screen.tags[index]
201 | if tag then
202 | client.focus:toggle_tag(tag)
203 | end
204 | end
205 | end,
206 | }),
207 | awful.key({
208 | modifiers = { modkey },
209 | keygroup = "numpad",
210 | description = "select layout directly",
211 | group = "layout",
212 | on_press = function(index)
213 | local t = awful.screen.focused().selected_tag
214 | if t then
215 | t.layout = t.layouts[index] or t.layout
216 | end
217 | end,
218 | }),
219 | })
220 |
221 | awful.mouse.append_global_mousebindings({
222 | awful.button({}, 4, awful.tag.viewprev),
223 | awful.button({}, 5, awful.tag.viewnext),
224 | })
225 | client.connect_signal("request::default_mousebindings", function()
226 | awful.mouse.append_client_mousebindings({
227 | awful.button({}, 1, function(c)
228 | c:activate({ context = "mouse_click" })
229 | end),
230 | awful.button({ modkey }, 1, function(c)
231 | c:activate({ context = "mouse_click", action = "mouse_move" })
232 | end),
233 | awful.button({ modkey }, 3, function(c)
234 | c:activate({ context = "mouse_click", action = "mouse_resize" })
235 | end),
236 | })
237 | end)
238 |
239 | client.connect_signal("request::default_keybindings", function()
240 | awful.keyboard.append_client_keybindings({
241 | awful.key({ modkey }, "f", function(c)
242 | c.fullscreen = not c.fullscreen
243 | c:raise()
244 | end, { description = "toggle fullscreen", group = "client" }),
245 | awful.key({ modkey, "Shift" }, "c", function(c)
246 | c:kill()
247 | end, { description = "close", group = "client" }),
248 | awful.key(
249 | { modkey, "Shift" },
250 | "space",
251 | awful.client.floating.toggle,
252 | { description = "toggle floating", group = "client" }
253 | ),
254 | awful.key({ modkey }, "z", function(c)
255 | local master = awful.client.getmaster()
256 | c:swap(master)
257 | master:activate()
258 | end, { description = "move to master", group = "client" }),
259 | awful.key({ modkey }, "o", function(c)
260 | c:move_to_screen()
261 | end, { description = "move to screen", group = "client" }),
262 | awful.key({ modkey }, "t", function(c)
263 | c.ontop = not c.ontop
264 | end, { description = "toggle keep on top", group = "client" }),
265 | awful.key({ modkey }, "m", function(c)
266 | c.maximized = not c.maximized
267 | c:raise()
268 | end, { description = "(un)maximize", group = "client" }),
269 | awful.key({ modkey, "Control" }, "m", function(c)
270 | c.maximized_vertical = not c.maximized_vertical
271 | c:raise()
272 | end, { description = "(un)maximize vertically", group = "client" }),
273 | awful.key({ modkey, "Shift" }, "m", function(c)
274 | c.maximized_horizontal = not c.maximized_horizontal
275 | c:raise()
276 | end, { description = "(un)maximize horizontally", group = "client" }),
277 | })
278 | end)
279 |
--------------------------------------------------------------------------------
/layouts/cornerne.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornerne.png
--------------------------------------------------------------------------------
/layouts/cornernew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornernew.png
--------------------------------------------------------------------------------
/layouts/cornernw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornernw.png
--------------------------------------------------------------------------------
/layouts/cornernww.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornernww.png
--------------------------------------------------------------------------------
/layouts/cornerse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornerse.png
--------------------------------------------------------------------------------
/layouts/cornersew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornersew.png
--------------------------------------------------------------------------------
/layouts/cornersw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornersw.png
--------------------------------------------------------------------------------
/layouts/cornersww.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/cornersww.png
--------------------------------------------------------------------------------
/layouts/dwindle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/dwindle.png
--------------------------------------------------------------------------------
/layouts/dwindlew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/dwindlew.png
--------------------------------------------------------------------------------
/layouts/fairh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/fairh.png
--------------------------------------------------------------------------------
/layouts/fairhw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/fairhw.png
--------------------------------------------------------------------------------
/layouts/fairv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/fairv.png
--------------------------------------------------------------------------------
/layouts/fairvw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/fairvw.png
--------------------------------------------------------------------------------
/layouts/floating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/floating.png
--------------------------------------------------------------------------------
/layouts/floatingw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/floatingw.png
--------------------------------------------------------------------------------
/layouts/fullscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/fullscreen.png
--------------------------------------------------------------------------------
/layouts/fullscreenw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/fullscreenw.png
--------------------------------------------------------------------------------
/layouts/magnifier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/magnifier.png
--------------------------------------------------------------------------------
/layouts/magnifierw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/magnifierw.png
--------------------------------------------------------------------------------
/layouts/max.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/max.png
--------------------------------------------------------------------------------
/layouts/maxw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/maxw.png
--------------------------------------------------------------------------------
/layouts/spiral.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/spiral.png
--------------------------------------------------------------------------------
/layouts/spiralw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/spiralw.png
--------------------------------------------------------------------------------
/layouts/tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tile.png
--------------------------------------------------------------------------------
/layouts/tilebottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tilebottom.png
--------------------------------------------------------------------------------
/layouts/tilebottomw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tilebottomw.png
--------------------------------------------------------------------------------
/layouts/tileleft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tileleft.png
--------------------------------------------------------------------------------
/layouts/tileleftw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tileleftw.png
--------------------------------------------------------------------------------
/layouts/tiletop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tiletop.png
--------------------------------------------------------------------------------
/layouts/tiletopw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tiletopw.png
--------------------------------------------------------------------------------
/layouts/tilew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/layouts/tilew.png
--------------------------------------------------------------------------------
/liblua_pam.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/liblua_pam.so
--------------------------------------------------------------------------------
/notifications.lua:
--------------------------------------------------------------------------------
1 | local naughty = require("naughty")
2 | local awful = require("awful")
3 | local wibox = require("wibox")
4 | local gears = require("gears")
5 | local beautiful = require("beautiful")
6 | local rubato = require("rubato")
7 | local helpers = require("helpers")
8 |
9 | naughty.connect_signal("request::display", function(n)
10 | n.resident = true
11 | n.position = "top_right"
12 |
13 | local timeout = n.timeout or 20
14 | n.timeout = nil
15 |
16 | local close_icon = wibox.widget({
17 | valign = "center",
18 | halign = "center",
19 | forced_width = beautiful.icon_size[1],
20 | forced_height = beautiful.icon_size[1],
21 | markup = "",
22 | font = beautiful.font_icon,
23 | widget = wibox.widget.textbox
24 | })
25 |
26 | local close_button = helpers.add_bg1(close_icon)
27 | close_button.fg = beautiful.red
28 |
29 | local icon = nil
30 | if n.clients[1] ~= nil then
31 | icon = wibox.widget({
32 | client = n.clients[1],
33 | forced_height = beautiful.icon_size[0],
34 | forced_width = beautiful.icon_size[0],
35 | widget = awful.widget.clienticon
36 | })
37 | elseif n.app_icon then
38 | icon = wibox.widget({
39 | image = n.app_icon,
40 | resize = true,
41 | forced_height = beautiful.icon_size[0],
42 | forced_width = beautiful.icon_size[0],
43 | clip_shape = helpers.rrect(),
44 | widget = wibox.widget.imagebox
45 | })
46 | end
47 |
48 | local image = nil
49 | if n.icon then
50 | image = wibox.widget({
51 | image = n.icon,
52 | resize = true,
53 | forced_height = beautiful.icon_size[3],
54 | forced_width = beautiful.icon_size[3],
55 | halign = "right",
56 | valign = "center",
57 | clip_shape = helpers.rrect(),
58 | widget = wibox.widget.imagebox
59 | })
60 | end
61 |
62 | local title = wibox.widget({
63 | layout = wibox.container.scroll.horizontal,
64 | step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth,
65 | fps = 60,
66 | speed = 75,
67 | {
68 | halign = "left",
69 | valign = "center",
70 | markup = "" .. n.title .. "",
71 | widget = wibox.widget.textbox
72 | }
73 | })
74 |
75 | title:pause()
76 |
77 | local message = wibox.widget({
78 | layout = wibox.container.scroll.horizontal,
79 | step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth,
80 | fps = 60,
81 | speed = 75,
82 | {
83 | halign = "left",
84 | valign = "center",
85 | markup = n.message,
86 | widget = wibox.widget.textbox
87 | }
88 | })
89 |
90 | message:pause()
91 |
92 | local app_name = wibox.widget({
93 | valign = "center",
94 | halign = "left",
95 | markup = "" .. n.app_name .. "",
96 | widget = wibox.widget.textbox
97 | })
98 |
99 | local progressbar = wibox.widget({
100 | value = 0.5,
101 | max_value = 1,
102 | color = beautiful.blue,
103 | background_color = beautiful.bg1,
104 | forced_height = beautiful.dpi(3),
105 | shape = helpers.rrect(),
106 | widget = wibox.widget.progressbar
107 | })
108 |
109 | local timed = rubato.timed({
110 | duration = timeout,
111 | pos = 0,
112 | rate = 20,
113 | clamp_position = true,
114 | subscribed = function(pos)
115 | progressbar.value = pos
116 | if pos == 1 then
117 | n:destroy(naughty.notification_closed_reason.dismissed_by_user)
118 | end
119 | end
120 | })
121 |
122 | timed.target = 1
123 |
124 | close_button:buttons(gears.table.join(awful.button({}, 1, function()
125 | n:destroy(naughty.notification_closed_reason.dismissed_by_user)
126 | end)))
127 |
128 | local actions = wibox.widget({
129 | notification = n,
130 | base_layout = wibox.widget({
131 | spacing = beautiful.margin[1],
132 | layout = wibox.layout.flex.horizontal
133 | }),
134 | widget_template = {
135 | {
136 | id = "text_role",
137 | valign = "center",
138 | halign = "center",
139 | widget = wibox.widget.textbox
140 | },
141 | shape = helpers.rrect(),
142 | bg = beautiful.blue,
143 | fg = beautiful.bg0,
144 | forced_height = beautiful.dpi(30),
145 | visible = n.actions and #n.actions > 0,
146 | widget = wibox.container.background
147 | },
148 | style = {
149 | underline_normal = false,
150 | underline_selected = true
151 | },
152 | widget = naughty.list.actions
153 | })
154 | local time = wibox.widget({
155 | markup = os.date("%I:%M %p"),
156 | halign = "right",
157 | valign = "center",
158 | widget = wibox.widget.textbox
159 | })
160 |
161 | local title_bar = helpers.add_bg1(helpers.add_margin(wibox.widget({
162 | {
163 | {
164 | icon,
165 | valign = "center",
166 | halign = "center",
167 | layout = wibox.container.place,
168 | },
169 | app_name,
170 | spacing = beautiful.margin[0],
171 | layout = wibox.layout.fixed.horizontal
172 | },
173 | nil,
174 | {
175 | time,
176 | close_button,
177 | spacing = beautiful.margin[0],
178 | layout = wibox.layout.fixed.horizontal
179 | },
180 | layout = wibox.layout.align.horizontal
181 | }), beautiful.margin[1], beautiful.margin[0]))
182 | title_bar.shape = nil
183 |
184 | local body = helpers.add_bg0(helpers.add_margin(wibox.widget({
185 | {
186 | image,
187 | {
188 | title,
189 | message,
190 | spacing = beautiful.margin[1],
191 | layout = wibox.layout.fixed.vertical
192 | },
193 | spacing = beautiful.margin[1],
194 | fill_space = true,
195 | layout = wibox.layout.fixed.horizontal
196 | },
197 | actions,
198 | layout = wibox.layout.fixed.vertical
199 | }), beautiful.margin[1], beautiful.margin[1]))
200 | body.shape = nil
201 |
202 | local widget = naughty.layout.box({
203 | notification = n,
204 | type = "notification",
205 | shape = helpers.rrect(),
206 | minimum_width = beautiful.dpi(400),
207 | maximum_width = beautiful.dpi(400),
208 | maximum_height = beautiful.dpi(150),
209 | widget_template = {
210 | title_bar,
211 | progressbar,
212 | body,
213 | layout = wibox.layout.fixed.vertical
214 | },
215 | bg = beautiful.bg0,
216 | border_color = beautiful.border_color_active,
217 | border_width = beautiful.border_width,
218 | layout = naughty.container.background
219 | })
220 |
221 | widget.buttons = {}
222 |
223 | widget:connect_signal("mouse::enter", function()
224 | title:continue()
225 | message:continue()
226 | timed.pause = true
227 | end)
228 |
229 | widget:connect_signal("mouse::leave", function()
230 | title:pause()
231 | message:pause()
232 | timed.pause = false
233 | end)
234 | end)
235 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danxliu/awm/d2d01e39107a9f5ae585108502dae4f490bee79e/preview.png
--------------------------------------------------------------------------------
/rc.lua:
--------------------------------------------------------------------------------
1 | pcall(require, "luarocks.loader")
2 |
3 | local awful = require("awful")
4 | local gears = require("gears")
5 | require("awful.autofocus")
6 |
7 | -- Beautiful
8 | require("themes")
9 |
10 | -- Signals
11 | require("signals")
12 |
13 | --Daemons
14 | require("daemons")
15 |
16 | -- Components
17 | require("components")
18 |
19 | -- Keys
20 | require("keys")
21 |
22 | -- Client rules
23 | require("rules")
24 |
25 | -- Notifications
26 | require("notifications")
27 |
28 | -- Autostart Applications
29 | awful.spawn.single_instance("xfsettingsd", false)
30 | awful.spawn.single_instance("picom", false)
31 | awful.spawn.single_instance("/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1", false)
32 |
33 | -- Run garbage collector regularly to prevent memory leaks
34 | gears.timer {
35 | timeout = 30,
36 | autostart = true,
37 | callback = function() collectgarbage("collect") end
38 | }
39 |
--------------------------------------------------------------------------------
/rules.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local ruled = require("ruled")
3 | ruled.client.connect_signal("request::rules", function()
4 | -- All clients will match this rule.
5 | ruled.client.append_rule({
6 | id = "global",
7 | rule = {},
8 | properties = {
9 | focus = awful.client.focus.filter,
10 | raise = true,
11 | screen = awful.screen.preferred,
12 | placement = awful.placement.no_overlap + awful.placement.no_offscreen,
13 | },
14 | })
15 |
16 | -- Floating clients.
17 | ruled.client.append_rule({
18 | id = "floating",
19 | rule_any = {
20 | instance = { "copyq", "pinentry" },
21 | class = {
22 | "Nemo",
23 | "Blueman-manager",
24 | "Gpick",
25 | },
26 | name = {
27 | "Event Tester",
28 | },
29 | role = {
30 | "AlarmWindow",
31 | "ConfigManager",
32 | "pop-up",
33 | },
34 | },
35 | properties = { floating = true },
36 | })
37 |
38 | -- Add titlebars to normal clients and dialogs
39 | ruled.client.append_rule({
40 | id = "titlebars",
41 | rule_any = {
42 | type = { "normal", "dialog" },
43 | },
44 | properties = { titlebars_enabled = true },
45 | })
46 | end)
47 |
--------------------------------------------------------------------------------
/screenshot:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o pipefail
3 |
4 | <> $_LOG_FILE_ 2>&1
50 | exit 1
51 | fi
52 | }
53 |
54 | # Check save directory
55 | # Create it if it doesn't exist
56 | function check_dir() {
57 | if [[ ! -d "$_SCREENSHOT_DIR_" || ! -d "$_ORIGINAL_DIR_" ]]; then
58 | mkdir -p "$_SCREENSHOT_DIR_"
59 | mkdir -p "$_ORIGINAL_DIR_"
60 | fi
61 | }
62 |
63 | function get_latest_img() {
64 | _LATEST_IMAGE_=$(/bin/ls -th $_SCREENSHOT_DIR_ | grep -vE '.screensht.png$' | grep -E '.png$' | head -n 1)
65 |
66 | if [[ $( echo "$_LATEST_IMAGE_" | wc -w ) -eq 0 ]]; then
67 | exit 1
68 | else
69 | _LATEST_IMAGE_="$_SCREENSHOT_DIR_/$_LATEST_IMAGE_"
70 | fi
71 | }
72 |
73 | function convert() {
74 | _target_file_=$( echo "$_LATEST_IMAGE_" | sed 's/.png/.screensht.png/g' )
75 |
76 | if [[ $_BORDER_SIZE_ -ge 3 ]]; then
77 | magick convert "$_LATEST_IMAGE_" \
78 | -format 'roundrectangle 1,1 %[fx:w+4],%[fx:h+4] '"$_ROUNDED_CORNER_"','"$_ROUNDED_CORNER_"''\
79 | info: > $_SCREENSHOT_DIR_/_rounded_.mvg
80 | check
81 |
82 | magick convert "$_LATEST_IMAGE_" -border $_BORDER_SIZE_ -alpha transparent \
83 | -background none -fill white -stroke none -strokewidth 0 \
84 | -draw "@"$_SCREENSHOT_DIR_"/_rounded_.mvg" $_SCREENSHOT_DIR_/_rounded_mask_.png >> $_LOG_FILE_ 2>&1
85 | check
86 |
87 | magick convert "$_LATEST_IMAGE_" -border $_BORDER_SIZE_ -alpha transparent \
88 | -background none -fill none -stroke $_FG_COLOR_ -strokewidth $_BORDER_SIZE_ \
89 | -draw "@"$_SCREENSHOT_DIR_"/_rounded_.mvg" $_SCREENSHOT_DIR_/_rounded_overlay_.png >> $_LOG_FILE_ 2>&1
90 | check
91 |
92 | magick convert "$_LATEST_IMAGE_" -alpha set -bordercolor none -border $_BORDER_SIZE_ \
93 | $_SCREENSHOT_DIR_/_rounded_mask_.png -compose DstIn -composite \
94 | $_SCREENSHOT_DIR_/_rounded_overlay_.png -compose Over -composite \
95 | "$_target_file_" >> $_LOG_FILE_ 2>&1 && \
96 | rm -f $_SCREENSHOT_DIR_/_rounded_*
97 | check
98 | else
99 | magick convert "$_LATEST_IMAGE_" \( +clone -alpha extract -draw 'fill black polygon 0,0 0,'"$_ROUNDED_CORNER_"' '"$_ROUNDED_CORNER_"',0 fill white circle '"$_ROUNDED_CORNER_"','"$_ROUNDED_CORNER_"' '"$_ROUNDED_CORNER_"',0' \
100 | \( +clone -flip \) -compose Multiply -composite \
101 | \( +clone -flop \) -compose Multiply -composite \
102 | \) -alpha off -compose CopyOpacity -composite -compose over "$_target_file_" >> $_LOG_FILE_ 2>&1
103 | check
104 | fi
105 |
106 | magick convert "$_target_file_" \( +clone -background black -shadow $_SHADOW_SIZE_ \) +swap -background none -layers merge +repage "$_target_file_" >> $_LOG_FILE_ 2>&1 \
107 | && magick convert "$_target_file_" -bordercolor $_BG_COLOR_ -border $_BG_SIZE_ "$_target_file_" >> $_LOG_FILE_ 2>&1
108 | check
109 |
110 | echo -en " $_AUTHOR_NAME_ " | magick convert "$_target_file_" -gravity ${_AUTHOR_POST_[0]} -pointsize $_FONT_SIZE_ -fill $_AUTHOR_COLOR_ -undercolor none -font $_FONT_ -annotate ${_AUTHOR_POST_[1]} @- "$_target_file_" \
111 | >> $_LOG_FILE_ 2>&1 && magick convert "$_target_file_" -gravity South -chop 0x$(( $_BG_SIZE_ / 2 )) "$_target_file_" >> $_LOG_FILE_ 2>&1
112 | check
113 |
114 | magick convert "$_target_file_" -gravity North -background $_BG_COLOR_ -splice 0x$(( $_BG_SIZE_ / 2 )) "$_target_file_" >> $_LOG_FILE_ 2>&1
115 | check
116 |
117 | magick convert "$_target_file_" -profile /usr/share/color/icc/colord/sRGB.icc "$_target_file_" >> $_LOG_FILE_ 2>&1
118 | check
119 | }
120 |
121 | function summary() {
122 | _runtime_job_=$(($2-$1))
123 | hours=$((_runtime_job_ / 3600)); minutes=$(( (_runtime_job_ % 3600) / 60 )); seconds=$(( (_runtime_job_ % 3600) % 60 ))
124 |
125 | if [[ $3 != "failed" ]]; then
126 | xclip -selection clipboard -t image/png -i $_target_file_ >> $_LOG_FILE_ 2>&1
127 |
128 | awesome-client "
129 | -- IMPORTANT NOTE: THIS PART OF THE SCRIPT IS LUA!
130 | naughty = require('naughty')
131 | awful = require('awful')
132 | beautiful = require('beautiful')
133 | dpi = beautiful.xresources.apply_dpi
134 |
135 | local open_image = naughty.action {
136 | name = 'Open',
137 | icon_only = false,
138 | }
139 |
140 | local delete_image = naughty.action {
141 | name = 'Delete',
142 | icon_only = false,
143 | }
144 |
145 | -- Execute the callback when 'Open' is pressed
146 | open_image:connect_signal('invoked', function()
147 | awful.spawn('xdg-open ' .. '${_target_file_}', false)
148 | end)
149 |
150 | -- Execute the callback when 'Delete' is pressed
151 | delete_image:connect_signal('invoked', function()
152 | awful.spawn('gio trash ' .. '${_target_file_}', false)
153 | end)
154 |
155 | -- Show notification
156 | naughty.notification ({
157 | app_name = 'Screenshot Tool',
158 | icon = '${_target_file_}',
159 | timeout = 10,
160 | title = 'Screenshot Captured',
161 | message = '${_notif_message_}',
162 | actions = { open_image, delete_image }
163 | })
164 | "
165 | fi
166 | }
167 |
168 | function main() {
169 | check_dir
170 |
171 | rm -f $_LOG_FILE_
172 | _start_job_=$(date +%s)
173 |
174 | _screenshot_command_="$1"
175 | _notif_message_="$2"
176 |
177 | $_screenshot_command_ $_SCREENSHOT_DIR_\/$_start_job_.png> /dev/null 2>&1
178 | check
179 |
180 | get_latest_img
181 | convert
182 |
183 | mv $_LATEST_IMAGE_ $_ORIGINAL_DIR_
184 |
185 | _end_job_=$(date +%s)
186 | summary $_start_job_ $_end_job_
187 | }
188 |
189 | # Check the args passed
190 | if [ -z "$1" ] || ([ "$1" != 'full' ] && [ "$1" != 'area' ]);
191 | then
192 | echo "
193 | Requires an argument:
194 | area - Area screenshot
195 | full - Fullscreen screenshot
196 |
197 | Example:
198 | ./screensht area
199 | ./screensht full
200 | "
201 | elif [ "$1" = 'full' ];
202 | then
203 | msg="Full screenshot saved and copied to clipboard!"
204 | main 'maim -u -m 10' "${msg}"
205 | elif [ "$1" = 'area' ];
206 | then
207 | msg='Area screenshot saved and copied to clipboard!'
208 | main 'maim -u -m 10 -s -b 2' "${msg}"
209 | fi
210 |
--------------------------------------------------------------------------------
/signals/error.lua:
--------------------------------------------------------------------------------
1 | local naughty = require("naughty")
2 |
3 | naughty.connect_signal("request::display_error", function(message, startup)
4 | naughty.notification({
5 | urgency = "critical",
6 | title = "An error occured" .. (startup and " during startup." or "."),
7 | message = message
8 | })
9 | end)
10 |
--------------------------------------------------------------------------------
/signals/init.lua:
--------------------------------------------------------------------------------
1 | require("signals.tags")
2 | require("signals.titlebars")
3 | require("signals.error")
4 |
--------------------------------------------------------------------------------
/signals/tags.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 |
3 | -- Tags
4 | tag.connect_signal("request::default_layouts", function()
5 | awful.layout.append_default_layouts({ awful.layout.suit.tile, awful.layout.suit.floating,
6 | awful.layout.suit.spiral, awful.layout.suit.spiral.dwindle -- awful.layout.suit.max,
7 | })
8 | end)
9 |
10 | screen.connect_signal("request::desktop_decoration", function(s)
11 | awful.tag({ "1", "2", "3", "4", "5" }, s, awful.layout.layouts[1])
12 | end)
13 |
--------------------------------------------------------------------------------
/signals/titlebars.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local beautiful = require("beautiful")
4 | local helpers = require("helpers")
5 | local naughty = require("naughty")
6 |
7 | -- Titlebars
8 | client.connect_signal("request::titlebars", function(c)
9 | local buttons = { awful.button({}, 1, function()
10 | c:activate({
11 | context = "titlebar",
12 | action = "mouse_move"
13 | })
14 | end), awful.button({}, 3, function()
15 | c:activate({
16 | context = "titlebar",
17 | action = "mouse_resize"
18 | })
19 | end) }
20 |
21 | local close_icon = wibox.widget({
22 | valign = "center",
23 | halign = "center",
24 | markup = "",
25 | forced_width = beautiful.icon_size[1],
26 | forced_height = beautiful.icon_size[1],
27 | font = beautiful.font_icon,
28 | widget = wibox.widget.textbox
29 | })
30 | local close_button = helpers.add_bg(close_icon)
31 | close_button:buttons({ awful.button({}, 1, function()
32 | c:kill()
33 | end) })
34 |
35 | local minimize_icon = wibox.widget({
36 | valign = "center",
37 | halign = "center",
38 | markup = "",
39 | forced_width = beautiful.icon_size[1],
40 | forced_height = beautiful.icon_size[1],
41 | font = beautiful.font_icon,
42 | widget = wibox.widget.textbox
43 | })
44 |
45 | -- Ok this doesn't work properly, see https://github.com/awesomeWM/awesome/issues/3716 :(
46 |
47 | local minimize_button = helpers.add_bg(minimize_icon)
48 | minimize_button:buttons({ awful.button({}, 1, function()
49 | c.minimized = true
50 | end) })
51 |
52 | local maximize_icon = wibox.widget({
53 | valign = "center",
54 | halign = "center",
55 | markup = "",
56 | forced_width = beautiful.icon_size[1],
57 | forced_height = beautiful.icon_size[1],
58 | font = beautiful.font_icon,
59 | widget = wibox.widget.textbox
60 | })
61 |
62 | local maximize_button = helpers.add_bg(maximize_icon)
63 | maximize_button:buttons({ awful.button({}, 1, function()
64 | c.maximized = not c.maximized
65 | end) })
66 |
67 | local left_widget = helpers.add_margin(wibox.widget({
68 | {
69 | image = c.icon or helpers.get_icon(c.class) or beautiful.icon_default,
70 | resize = true,
71 | valign = "center",
72 | halign = "center",
73 | widget = wibox.widget.imagebox,
74 | },
75 | {
76 | halign = "left",
77 | valign = "center",
78 | markup = "" .. (c.class or c.name or "unknown"):gsub("^%l", string.upper) .. "",
79 | widget = wibox.widget.textbox,
80 | font = beautiful.font_small
81 | },
82 | buttons = buttons,
83 | spacing = beautiful.margin[1],
84 | layout = wibox.layout.fixed.horizontal
85 | }))
86 |
87 | local right_widget = helpers.add_margin(wibox.widget({
88 | -- minimize_button,
89 | maximize_button,
90 | close_button,
91 | spacing = beautiful.margin[0],
92 | layout = wibox.layout.fixed.horizontal
93 | }))
94 |
95 | local titlebar_widget = helpers.add_margin(wibox.widget({
96 | left_widget,
97 | nil,
98 | right_widget,
99 | layout = wibox.layout.align.horizontal
100 | }))
101 |
102 | local titlebar = awful.titlebar(c, {
103 | size = beautiful.titlebar_height
104 | })
105 |
106 | titlebar.widget = titlebar_widget
107 |
108 | c.shape = helpers.rrect()
109 |
110 | client.connect_signal("focus", function()
111 | if c.active then
112 | minimize_button.fg = beautiful.orange
113 | maximize_button.fg = beautiful.green
114 | close_button.fg = beautiful.red
115 | else
116 | minimize_button.fg = beautiful.bg1
117 | maximize_button.fg = beautiful.bg1
118 | close_button.fg = beautiful.bg1
119 | end
120 | end)
121 | end)
122 |
123 | client.connect_signal("manage", function(c)
124 | if not awesome.startup then
125 | awful.client.setslave(c)
126 | end
127 |
128 | if awesome.startup and not c.size_hints.user_position and not c.size_hints.program_position then
129 | awful.placement.no_offscreen(c)
130 | end
131 | end)
132 |
133 | client.connect_signal("property::maximized", function(c)
134 | if c.maximized then
135 | c.shape = nil
136 | else
137 | c.shape = helpers.rrect()
138 | end
139 | end)
140 |
141 | client.connect_signal("property::fullscreen", function(c)
142 | if c.fullscreen then
143 | c.shape = nil
144 | else
145 | c.shape = helpers.rrect()
146 | end
147 | end)
148 |
--------------------------------------------------------------------------------
/themes.lua:
--------------------------------------------------------------------------------
1 | local beautiful = require("beautiful")
2 | local gears = require("gears")
3 | local dpi = beautiful.xresources.apply_dpi
4 | local helpers = require("helpers")
5 |
6 | awesome.set_preferred_icon_size(64)
7 |
8 | local M = {}
9 |
10 | M.themes_path = gears.filesystem.get_configuration_dir()
11 |
12 | M.wallpaper = M.themes_path .. "backgrounds/mountains_1.jpg"
13 | screen.connect_signal("request::desktop_decoration", function(s)
14 | gears.wallpaper.maximized(M.wallpaper, s)
15 | end)
16 |
17 | -- Converts the input to dpi
18 | function M.dpi(i)
19 | return dpi(i)
20 | end
21 |
22 | M.font = "RobotoCondensed 14"
23 | M.font_medium = "RobotoCondensed 48"
24 | M.font_large = "RobotoCondensed 64"
25 | M.font_icon = "Material-Design-Iconic-Font 14"
26 | M.font_icon_large = "Material-Design-Iconic-Font 24"
27 | M.radius = M.dpi(8)
28 |
29 | M.margin = {}
30 | M.margin[0] = M.dpi(4)
31 | M.margin[1] = M.dpi(8)
32 | M.margin[2] = M.dpi(12)
33 | M.margin[3] = M.dpi(16)
34 | M.margin[4] = M.dpi(32)
35 |
36 | M.icon_size = {}
37 | M.icon_size[0] = M.dpi(16)
38 | M.icon_size[1] = M.dpi(32)
39 | M.icon_size[2] = M.dpi(48)
40 | M.icon_size[3] = M.dpi(64)
41 | M.icon_size[4] = M.dpi(128)
42 |
43 | -- Github Dark Theme
44 | M.bg0 = "#0d1117"
45 | M.bg1 = "#161b22"
46 | M.bg2 = "#21262d"
47 | M.fg0 = "#ecf2f8"
48 | M.fg1 = "#c6cdd5"
49 | M.fg2 = "#89929b"
50 |
51 | M.red = "#fa7970"
52 | M.orange = "#faa356"
53 | M.green = "#7ce38b"
54 | M.cyan = "#a2d2fb"
55 | M.blue = "#77bdfb"
56 | M.purple = "#cea5fb"
57 |
58 | -- Default colors
59 | M.bg_normal = M.bg0
60 | M.bg_focus = M.bg1
61 | M.bg_urgent = M.bg0
62 | M.bg_minimize = M.bg0
63 |
64 | M.fg_normal = M.fg2
65 | M.fg_focus = M.fg0
66 | M.fg_urgent = M.red
67 | M.fg_minimize = M.fg2
68 |
69 | M.border_color_normal = M.bg0
70 | M.border_color_active = M.bg1
71 | M.border_width = dpi(2)
72 |
73 | -- Configs
74 | M.bar_width = M.dpi(42)
75 |
76 | M.titlebar_height = M.dpi(42)
77 | M.useless_gap = M.dpi(4)
78 |
79 | M.switcher_height = M.dpi(200)
80 | M.switcher_width = M.dpi(200)
81 |
82 | M.notification_bg_normal = M.bg2
83 | M.notification_bg_selected = M.blue
84 |
85 | M.hotkeys_bg = M.bg0
86 | M.hotkeys_fg = M.fg0
87 | M.hotkeys_shape = helpers.rrect()
88 | M.hotkeys_modifiers_fg = M.blue
89 | M.hotkeys_border_width = M.margin[1]
90 | M.hotkeys_border_color = M.bg0
91 | M.hotkeys_label_bg = M.red
92 | M.hotkeys_label_fg = M.bg0
93 | M.hotkeys_group_margin = M.margin[3]
94 | M.hotkeys_font = M.font
95 | M.hotkeys_description_font = M.font
96 |
97 | -- AwesomeWM icon
98 | M.icon_awesome = beautiful.theme_assets.awesome_icon(M.dpi(64), M.fg0, M.bg0)
99 |
100 | M.icon_default = M.themes_path .. "assets/default.svg"
101 | M.icon_music = M.themes_path .. "assets/music.svg"
102 | M.icon_terminal = M.themes_path .. "assets/terminal.svg"
103 |
104 | -- Layout icons
105 | M.layout_fairh = M.themes_path .. "layouts/fairhw.png"
106 | M.layout_fairv = M.themes_path .. "layouts/fairvw.png"
107 | M.layout_floating = M.themes_path .. "layouts/floatingw.png"
108 | M.layout_magnifier = M.themes_path .. "layouts/magnifierw.png"
109 | M.layout_max = M.themes_path .. "layouts/maxw.png"
110 | M.layout_fullscreen = M.themes_path .. "layouts/fullscreenw.png"
111 | M.layout_tilebottom = M.themes_path .. "layouts/tilebottomw.png"
112 | M.layout_tileleft = M.themes_path .. "layouts/tileleftw.png"
113 | M.layout_tile = M.themes_path .. "layouts/tilew.png"
114 | M.layout_tiletop = M.themes_path .. "layouts/tiletopw.png"
115 | M.layout_spiral = M.themes_path .. "layouts/spiralw.png"
116 | M.layout_dwindle = M.themes_path .. "layouts/dwindlew.png"
117 | M.layout_cornernw = M.themes_path .. "layouts/cornernww.png"
118 | M.layout_cornerne = M.themes_path .. "layouts/cornernew.png"
119 | M.layout_cornersw = M.themes_path .. "layouts/cornersww.png"
120 | M.layout_cornerse = M.themes_path .. "layouts/cornersew.png"
121 |
122 | beautiful.init(M)
123 |
124 | return M
125 |
--------------------------------------------------------------------------------
/wibox/layout/init.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- Collection of layouts that can be used in widget boxes.
3 | --
4 | -- @author Uli Schlachter
5 | -- @copyright 2010 Uli Schlachter
6 | -- @classmod wibox.layout
7 | ---------------------------------------------------------------------------
8 | local base = require("wibox.widget.base")
9 |
10 | return setmetatable({
11 | fixed = require("wibox.layout.fixed");
12 | align = require("wibox.layout.align");
13 | flex = require("wibox.layout.flex");
14 | rotate = require("wibox.layout.rotate");
15 | manual = require("wibox.layout.manual");
16 | margin = require("wibox.layout.margin");
17 | mirror = require("wibox.layout.mirror");
18 | constraint = require("wibox.layout.constraint");
19 | scroll = require("wibox.layout.scroll");
20 | ratio = require("wibox.layout.ratio");
21 | stack = require("wibox.layout.stack");
22 | grid = require("wibox.layout.grid");
23 | overflow = require("wibox.layout.overflow");
24 | }, {__call = function(_, args) return base.make_widget_declarative(args) end})
25 |
--------------------------------------------------------------------------------
/wibox/layout/overflow.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | -- A layout that allows its children to take more space than what's available
3 | -- in the surrounding container. If the content does exceed the available
4 | -- size, a scrollbar is added and scrolling behavior enabled.
5 | --
6 | -- @DOC_wibox_layout_defaults_overflow_EXAMPLE@
7 | -- @author Lucas Schwiderski
8 | -- @copyright 2021 Lucas Schwiderski
9 | -- @layoutmod wibox.layout.overflow
10 | -- @supermodule wibox.layout.fixed
11 | ---------------------------------------------------------------------------
12 | local base = require('wibox.widget.base')
13 | local fixed = require('wibox.layout.fixed')
14 | local separator = require('wibox.widget.separator')
15 | local gtable = require('gears.table')
16 | local gshape = require('gears.shape')
17 | local gobject = require('gears.object')
18 | local beautiful = require('beautiful')
19 | local dpi = beautiful.xresources.apply_dpi
20 | local mousegrabber = mousegrabber
21 |
22 | local overflow = {
23 | mt = {}
24 | }
25 |
26 | -- Determine the required space to draw the layout's children and, if necessary,
27 | -- the scrollbar.
28 | function overflow:fit(context, orig_width, orig_height)
29 | local widgets = self._private.widgets
30 | local num_widgets = #widgets
31 | if num_widgets < 1 then
32 | return 0, 0
33 | end
34 |
35 | local width, height = orig_width, orig_height
36 | local scrollbar_width = self._private.scrollbar_width
37 | local scrollbar_enabled = self._private.scrollbar_enabled
38 | local used_in_dir, used_max = 0, 0
39 | local is_y = self._private.dir == "y"
40 | local avail_in_dir = is_y and orig_height or orig_width
41 |
42 | -- Set the direction covered by scrolling to the maximum value
43 | -- to allow widgets to take as much space as they want.
44 | if is_y then
45 | height = math.huge
46 | else
47 | width = math.huge
48 | end
49 |
50 | -- First, determine widget sizes.
51 | -- Only when the content doesn't fit and needs scrolling should
52 | -- we reduce content size to make space for a scrollbar.
53 | for _, widget in ipairs(widgets) do
54 | local w, h = base.fit_widget(self, context, widget, width, height)
55 |
56 | if is_y then
57 | used_max = math.max(used_max, w)
58 | used_in_dir = used_in_dir + h
59 | else
60 | used_max = math.max(used_max, h)
61 | used_in_dir = used_in_dir + w
62 | end
63 | end
64 |
65 | local spacing = self._private.spacing * (num_widgets - 1)
66 | used_in_dir = used_in_dir + spacing
67 |
68 | local need_scrollbar = scrollbar_enabled and used_in_dir > avail_in_dir
69 |
70 | -- Even if `used_max == orig_(width|height)` already, `base.fit_widget`
71 | -- will clamp return values, so we can "overextend" here.
72 | if need_scrollbar then
73 | used_max = used_max + scrollbar_width
74 | end
75 |
76 | if is_y then
77 | return used_max, used_in_dir
78 | else
79 | return used_in_dir, used_max
80 | end
81 | end
82 |
83 | -- Layout children, scrollbar and spacing widgets.
84 | -- Only those widgets that are currently visible will be placed.
85 | function overflow:layout(context, orig_width, orig_height)
86 | local result = {}
87 | local is_y = self._private.dir == "y"
88 | local widgets = self._private.widgets
89 | local avail_in_dir = is_y and orig_height or orig_width
90 | local scrollbar_width = self._private.scrollbar_width
91 | local scrollbar_enabled = self._private.scrollbar_enabled
92 | local scrollbar_position = self._private.scrollbar_position
93 | local width, height = orig_width, orig_height
94 | local widget_x, widget_y = 0, 0
95 | local used_in_dir, used_max = 0, 0
96 |
97 | -- Set the direction covered by scrolling to the maximum value
98 | -- to allow widgets to take as much space as they want.
99 | if is_y then
100 | height = math.huge
101 | else
102 | width = math.huge
103 | end
104 |
105 | -- First, determine widget sizes.
106 | -- Only when the content doesn't fit and needs scrolling should
107 | -- we reduce content size to make space for a scrollbar.
108 | for _, widget in pairs(widgets) do
109 | local w, h = base.fit_widget(self, context, widget, width, height)
110 |
111 | if is_y then
112 | used_max = math.max(used_max, w)
113 | used_in_dir = used_in_dir + h
114 | else
115 | used_max = math.max(used_max, h)
116 | used_in_dir = used_in_dir + w
117 | end
118 | end
119 |
120 | used_in_dir = used_in_dir + self._private.spacing * (#widgets - 1)
121 |
122 | -- Save size for scrolling behavior
123 | self._private.avail_in_dir = avail_in_dir
124 | self._private.used_in_dir = used_in_dir
125 |
126 | local need_scrollbar = used_in_dir > avail_in_dir and scrollbar_enabled
127 |
128 | local scroll_position = self._private.scroll_factor
129 |
130 | if need_scrollbar then
131 | local scrollbar_widget = self._private.scrollbar_widget
132 | local bar_x, bar_y = 0, 0
133 | local bar_w, bar_h
134 | -- The percentage of how much of the content can be visible within
135 | -- the available space
136 | local visible_percent = avail_in_dir / used_in_dir
137 | -- Make scrollbar length reflect `visible_percent`
138 | -- TODO: Apply a default minimum length
139 | local bar_length = math.floor(visible_percent * avail_in_dir)
140 | local bar_pos = (avail_in_dir - bar_length) * self._private.scroll_factor
141 |
142 | if is_y then
143 | bar_w, bar_h = base.fit_widget(self, context, scrollbar_widget, scrollbar_width, bar_length)
144 | bar_y = bar_pos
145 |
146 | if scrollbar_position == "left" then
147 | widget_x = widget_x + bar_w
148 | elseif scrollbar_position == "right" then
149 | bar_x = orig_width - bar_w
150 | end
151 |
152 | self._private.bar_length = bar_h
153 |
154 | width = width - bar_w
155 | else
156 | bar_w, bar_h = base.fit_widget(self, context, scrollbar_widget, bar_length, scrollbar_width)
157 | bar_x = bar_pos
158 |
159 | if scrollbar_position == "top" then
160 | widget_y = widget_y + bar_h
161 | elseif scrollbar_position == "bottom" then
162 | bar_y = orig_height - bar_h
163 | end
164 |
165 | self._private.bar_length = bar_w
166 |
167 | height = height - bar_h
168 | end
169 |
170 | table.insert(result, base.place_widget_at(scrollbar_widget, math.floor(bar_x), math.floor(bar_y),
171 | math.floor(bar_w), math.floor(bar_h)))
172 | end
173 |
174 | local pos, spacing = 0, self._private.spacing
175 | local interval = used_in_dir - avail_in_dir
176 |
177 | local spacing_widget = self._private.spacing_widget
178 | if spacing_widget then
179 | if is_y then
180 | local _
181 | _, spacing = base.fit_widget(self, context, spacing_widget, width, spacing)
182 | else
183 | spacing = base.fit_widget(self, context, spacing_widget, spacing, height)
184 | end
185 | end
186 |
187 | for i, w in ipairs(widgets) do
188 | local content_x, content_y
189 | local content_w, content_h = base.fit_widget(self, context, w, need_scrollbar and width - self._private.scrollbar_spacing or width, height)
190 |
191 | -- When scrolling down, the content itself moves up -> substract
192 | local scrolled_pos = pos - (scroll_position * interval)
193 |
194 | -- Stop processing completely once we're passed the visible portion
195 | if scrolled_pos > avail_in_dir then
196 | break
197 | end
198 |
199 | if is_y then
200 | content_x, content_y = widget_x, scrolled_pos
201 | pos = pos + content_h + spacing
202 |
203 | if self._private.fill_space then
204 | content_w = width
205 | end
206 | else
207 | content_x, content_y = scrolled_pos, widget_y
208 | pos = pos + content_w + spacing
209 |
210 | if self._private.fill_space then
211 | content_h = height
212 | end
213 | end
214 |
215 | local is_in_view = is_y and (scrolled_pos + content_h > 0) or (scrolled_pos + content_w > 0)
216 |
217 | if is_in_view then
218 | -- Add the spacing widget, but not before the first widget
219 | if i > 1 and spacing_widget then
220 | table.insert(result,
221 | base.place_widget_at(spacing_widget, -- The way how spacing is added for regular widgets
222 | -- and the `spacing_widget` is disconnected:
223 | -- The offset for regular widgets is added to `pos` one
224 | -- iteration _before_ the one where the widget is actually
225 | -- placed.
226 | -- Because of that, the placement for the spacing widget
227 | -- needs to substract that offset to be placed right after
228 | -- the previous regular widget.
229 | math.floor(is_y and content_x or (content_x - spacing)),
230 | math.floor(is_y and (content_y - spacing) or content_y),
231 | math.floor(is_y and content_w or spacing), math.floor(is_y and spacing or content_h)))
232 | end
233 | table.insert(result, base.place_widget_at(w, math.floor(content_x), math.floor(content_y),
234 | math.floor(need_scrollbar and content_w - self._private.scrollbar_spacing or content_w), math.floor(content_h)))
235 | end
236 | end
237 |
238 | return result
239 | end
240 |
241 | function overflow:before_draw_children(_, cr, width, height)
242 | -- Clip drawing for children to the space we're allowed to draw in
243 | cr:rectangle(0, 0, width, height)
244 | cr:clip()
245 | end
246 |
247 | --- The amount of units to advance per scroll event.
248 | --
249 | -- This affects calls to `scroll` and the default mouse wheel handler.
250 | --
251 | -- The default is `10`.
252 | --
253 | -- @property step
254 | -- @tparam number step The step size.
255 | function overflow:set_step(step)
256 | self._private.step = step
257 | -- We don't need to emit enything here, since changing step only really
258 | -- takes effect the next time the user scrolls
259 | end
260 |
261 | --- Scroll the layout's content by `amount * step`.
262 | --
263 | -- A positive amount scroll down/right, a negative amount scrolls up/left.
264 | --
265 | -- The amount of units scrolled is affected by `step`.
266 | --
267 | -- @method overflow:scroll
268 | -- @tparam number amount The amount to scroll by.
269 | -- @emits property::overflow::scroll_factor
270 | -- @emitstparam property::overflow::scroll_factor number scroll_factor The new
271 | -- scroll factor.
272 | -- @emits widget::layout_changed
273 | -- @emits widget::redraw_needed
274 | function overflow:scroll(amount)
275 | if amount == 0 then
276 | return
277 | end
278 | local interval = self._private.used_in_dir
279 | local delta = self._private.step / interval
280 |
281 | local factor = self._private.scroll_factor + (delta * amount)
282 | self:set_scroll_factor(factor)
283 | end
284 |
285 | --- The scroll factor.
286 | --
287 | -- The scroll factor represents how far the layout's content is currently
288 | -- scrolled. It is represented as a fraction from `0` to `1`, where `0` is the
289 | -- start of the content and `1` is the end.
290 | --
291 | -- @property scroll_factor
292 | -- @tparam number scroll_factor The scroll factor.
293 | -- @propemits true false
294 |
295 | function overflow:set_scroll_factor(factor)
296 | local current = self._private.scroll_factor
297 | local interval = self._private.used_in_dir - self._private.avail_in_dir
298 | if current == factor -- the content takes less space than what is available, i.e. everything
299 | -- is already visible
300 | or interval <= 0 -- the scroll factor is out of range
301 | or (current <= 0 and factor < 0) or (current >= 1 and factor > 1) then
302 | return
303 | end
304 |
305 | self._private.scroll_factor = math.min(1, math.max(factor, 0))
306 |
307 | self:emit_signal("widget::layout_changed")
308 | self:emit_signal("property::scroll_factor", factor)
309 | end
310 |
311 | function overflow:get_scroll_factor()
312 | return self._private.scroll_factor
313 | end
314 |
315 | --- The scrollbar width.
316 | --
317 | -- For horizontal scrollbars, this is the scrollbar height
318 | --
319 | -- The default is `5`.
320 | --
321 | -- @DOC_wibox_layout_overflow_scrollbar_width_EXAMPLE@
322 | --
323 | -- @property scrollbar_width
324 | -- @tparam number scrollbar_width The scrollbar width.
325 | -- @propemits true false
326 |
327 | function overflow:set_scrollbar_width(width)
328 | if self._private.scrollbar_width == width then
329 | return
330 | end
331 |
332 | self._private.scrollbar_width = width
333 |
334 | self:emit_signal("widget::layout_changed")
335 | self:emit_signal("property::scrollbar_width", width)
336 | end
337 |
338 | --- The scrollbar position.
339 | --
340 | -- For horizontal scrollbars, this can be `"top"` or `"bottom"`,
341 | -- for vertical scrollbars this can be `"left"` or `"right"`.
342 | -- The default is `"right"`/`"bottom"`.
343 | --
344 | -- @DOC_wibox_layout_overflow_scrollbar_position_EXAMPLE@
345 | --
346 | -- @property scrollbar_position
347 | -- @tparam string scrollbar_position The scrollbar position.
348 | -- @propemits true false
349 |
350 | function overflow:set_scrollbar_position(position)
351 | if self._private.scrollbar_position == position then
352 | return
353 | end
354 |
355 | self._private.scrollbar_position = position
356 |
357 | self:emit_signal("widget::layout_changed")
358 | self:emit_signal("property::scrollbar_position", position)
359 | end
360 |
361 | function overflow:get_scrollbar_position()
362 | return self._private.scrollbar_position
363 | end
364 |
365 | --- The scrollbar visibility.
366 | --
367 | -- If this is set to `false`, no scrollbar will be rendered, even if the layout's
368 | -- content overflows. Mouse wheel scrolling will work regardless.
369 | --
370 | -- The default is `true`.
371 | --
372 | -- @property scrollbar_enabled
373 | -- @tparam boolean scrollbar_enabled The scrollbar visibility.
374 | -- @propemits true false
375 |
376 | function overflow:set_scrollbar_enabled(enabled)
377 | if self._private.scrollbar_enabled == enabled then
378 | return
379 | end
380 |
381 | self._private.scrollbar_enabled = enabled
382 |
383 | self:emit_signal("widget::layout_changed")
384 | self:emit_signal("property::scrollbar_enabled", enabled)
385 | end
386 |
387 | function overflow:get_scrollbar_enabled()
388 | return self._private.scrollbar_enabled
389 | end
390 |
391 | -- Wraps a callback function for `mousegrabber` that is capable of
392 | -- updating the scroll factor.
393 | local function build_grabber(container, initial_x, initial_y, geo)
394 | local is_y = container._private.dir == "y"
395 | local bar_interval = container._private.avail_in_dir - container._private.bar_length
396 | local start_pos = container._private.scroll_factor * bar_interval
397 | local start = is_y and initial_y or initial_x
398 |
399 | -- Calculate a matrix transforming from screen coordinates into widget
400 | -- coordinates.
401 | -- This is required for mouse movement to work when the widget has been
402 | -- transformed by something like `wibox.container.rotate`.
403 | local matrix_from_device = geo.hierarchy:get_matrix_from_device()
404 | local wgeo = geo.drawable.drawable:geometry()
405 | local matrix = matrix_from_device:translate(-wgeo.x, -wgeo.y)
406 |
407 | return function(mouse)
408 | if not mouse.buttons[1] then
409 | return false
410 | end
411 |
412 | local x, y = matrix:transform_point(mouse.x, mouse.y)
413 | local pos = is_y and y or x
414 | container:set_scroll_factor((start_pos + (pos - start)) / bar_interval)
415 |
416 | return true
417 | end
418 | end
419 |
420 | -- Applies a mouse button signal using `build_grabber` to a scrollbar widget.
421 | local function apply_scrollbar_mouse_signal(container, w)
422 | w:connect_signal('button::press', function(_, x, y, button_id, _, geo)
423 | if button_id ~= 1 then
424 | return
425 | end
426 | mousegrabber.run(build_grabber(container, x, y, geo), "fleur")
427 | end)
428 | end
429 |
430 | --- The scrollbar widget.
431 | -- This widget is rendered as the scrollbar element.
432 | --
433 | -- The default is `wibox.widget.separator{ shape = gears.shape.rectangle }`.
434 | --
435 | -- @DOC_wibox_layout_overflow_scrollbar_widget_EXAMPLE@
436 | --
437 | -- @property scrollbar_widget
438 | -- @tparam widget scrollbar_widget The scrollbar widget.
439 | -- @propemits true false
440 |
441 | function overflow:set_scrollbar_widget(widget)
442 | local w = base.make_widget_from_value(widget)
443 |
444 | apply_scrollbar_mouse_signal(self, w)
445 |
446 | self._private.scrollbar_widget = w
447 |
448 | self:emit_signal("widget::layout_changed")
449 | self:emit_signal("property::scrollbar_widget", widget)
450 | end
451 |
452 | function overflow:get_scrollbar_widget()
453 | return self._private.scrollbar_widget
454 | end
455 |
456 |
457 | function overflow:set_scrollbar_spacing(spacing)
458 | self._private.scrollbar_spacing = spacing
459 |
460 | self:emit_signal("widget::layout_changed")
461 | self:emit_signal("property::scrollbar_spacing", spacing)
462 | end
463 |
464 | function overflow:get_scrollbar_spacing()
465 | return self._private.scrollbar_spacing
466 | end
467 |
468 | local function new(dir, ...)
469 | local ret = fixed[dir](...)
470 |
471 | gtable.crush(ret, overflow, true)
472 | ret.widget_name = gobject.modulename(2)
473 | -- Tell the widget system to prevent clicks outside the layout's extends
474 | -- to register with child widgets, even if they actually extend that far.
475 | -- This prevents triggering button presses on hidden/clipped widgets.
476 | ret.clip_child_extends = true
477 |
478 | -- Manually set the scroll factor here. We don't know the bounding size yet.
479 | ret._private.scroll_factor = 0
480 |
481 | -- Apply defaults. Bypass setters to avoid signals.
482 | ret._private.step = 10
483 | ret._private.fill_space = true
484 | ret._private.scrollbar_width = 5
485 | ret._private.scrollbar_enabled = true
486 | ret._private.scrollbar_position = dir == "vertical" and "right" or "bottom"
487 | ret._private.scrollbar_spacing = dpi(15)
488 |
489 | local scrollbar_widget = separator({
490 | shape = gshape.rectangle
491 | })
492 | apply_scrollbar_mouse_signal(ret, scrollbar_widget)
493 | ret._private.scrollbar_widget = scrollbar_widget
494 |
495 | ret:connect_signal('button::press', function(self, _, _, button)
496 | if button == 4 then
497 | self:scroll(-1)
498 | elseif button == 5 then
499 | self:scroll(1)
500 | end
501 | end)
502 |
503 | return ret
504 | end
505 |
506 | --- Returns a new horizontal overflow layout.
507 | -- Child widgets are placed similar to `wibox.layout.fixed`, except that
508 | -- they may take as much width as they want. If the total width of all child
509 | -- widgets exceeds the width available whithin the layout's outer container
510 | -- a scrollbar will be added and scrolling behavior enabled.
511 | -- @tparam widget ... Widgets that should be added to the layout.
512 | -- @constructorfct wibox.layout.overflow.horizontal
513 | function overflow.horizontal(...)
514 | return new("horizontal", ...)
515 | end
516 |
517 | --- Returns a new vertical overflow layout.
518 | -- Child widgets are placed similar to `wibox.layout.fixed`, except that
519 | -- they may take as much height as they want. If the total height of all child
520 | -- widgets exceeds the height available whithin the layout's outer container
521 | -- a scrollbar will be added and scrolling behavior enabled.
522 | -- @tparam widget ... Widgets that should be added to the layout.
523 | -- @constructorfct wibox.layout.overflow.horizontal
524 | function overflow.vertical(...)
525 | return new("vertical", ...)
526 | end
527 |
528 | return setmetatable(overflow, overflow.mt)
529 |
--------------------------------------------------------------------------------
/wibox/widget/hierarchy.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | -- Management of widget hierarchies. Each widget hierarchy object has a widget
3 | -- for which it saves e.g. size and transformation in its parent. Also, each
4 | -- widget has a number of children.
5 | --
6 | -- @author Uli Schlachter
7 | -- @copyright 2015 Uli Schlachter
8 | -- @classmod wibox.hierarchy
9 | ---------------------------------------------------------------------------
10 |
11 | local matrix = require("gears.matrix")
12 | local protected_call = require("gears.protected_call")
13 | local cairo = require("lgi").cairo
14 | local base = require("wibox.widget.base")
15 | local no_parent = base.no_parent_I_know_what_I_am_doing
16 |
17 | local hierarchy = {}
18 |
19 | local widgets_to_count = setmetatable({}, { __mode = "k" })
20 |
21 | --- Add a widget to the list of widgets for which hierarchies should count their
22 | -- occurrences. Note that for correct operations, the widget must not yet be
23 | -- visible in any hierarchy.
24 | -- @param widget The widget that should be counted.
25 | -- @staticfct wibox.hierarchy.count_widget
26 | function hierarchy.count_widget(widget)
27 | widgets_to_count[widget] = true
28 | end
29 |
30 | local function hierarchy_new(redraw_callback, layout_callback, callback_arg)
31 | local result = {
32 | _matrix = matrix.identity,
33 | _matrix_to_device = matrix.identity,
34 | _need_update = true,
35 | _widget = nil,
36 | _context = nil,
37 | _redraw_callback = redraw_callback,
38 | _layout_callback = layout_callback,
39 | _callback_arg = callback_arg,
40 | _size = {
41 | width = nil,
42 | height = nil
43 | },
44 | _draw_extents = {
45 | x = 0,
46 | y = 0,
47 | width = 0,
48 | height = 0
49 | },
50 | _parent = nil,
51 | _children = {},
52 | _widget_counts = {},
53 | }
54 |
55 | function result._redraw()
56 | redraw_callback(result, callback_arg)
57 | end
58 | function result._layout()
59 | local h = result
60 | while h do
61 | h._need_update = true
62 | h = h._parent
63 | end
64 | layout_callback(result, callback_arg)
65 | end
66 | function result._emit_recursive(widget, name, ...)
67 | local cur = result
68 | assert(widget == cur._widget)
69 | while cur do
70 | if cur._widget then
71 | cur._widget:emit_signal(name, ...)
72 | end
73 | cur = cur._parent
74 | end
75 | end
76 |
77 | for k, f in pairs(hierarchy) do
78 | if type(f) == "function" then
79 | result[k] = f
80 | end
81 | end
82 | return result
83 | end
84 |
85 | local hierarchy_update
86 | function hierarchy_update(self, context, widget, width, height, region, matrix_to_parent, matrix_to_device)
87 | if (not self._need_update) and self._widget == widget and
88 | self._context == context and
89 | self._size.width == width and self._size.height == height and
90 | matrix.equals(self._matrix, matrix_to_parent) and
91 | matrix.equals(self._matrix_to_device, matrix_to_device) then
92 | -- Nothing changed
93 | return
94 | end
95 |
96 | self._need_update = false
97 |
98 | local old_x, old_y, old_width, old_height
99 | local old_widget = self._widget
100 | if self._size.width and self._size.height then
101 | local x, y, w, h = matrix.transform_rectangle(self._matrix_to_device, 0, 0, self._size.width, self._size.height)
102 | old_x, old_y = math.floor(x), math.floor(y)
103 | old_width, old_height = math.ceil(x + w) - old_x, math.ceil(y + h) - old_y
104 | else
105 | old_x, old_y, old_width, old_height = 0, 0, 0, 0
106 | end
107 |
108 | -- Disconnect old signals
109 | if old_widget and old_widget ~= widget then
110 | self._widget:disconnect_signal("widget::redraw_needed", self._redraw)
111 | self._widget:disconnect_signal("widget::layout_changed", self._layout)
112 | self._widget:disconnect_signal("widget::emit_recursive", self._emit_recursive)
113 | end
114 |
115 | -- Save the arguments we need to save
116 | self._widget = widget
117 | self._context = context
118 | self._size.width = width
119 | self._size.height = height
120 | self._matrix = matrix_to_parent
121 | self._matrix_to_device = matrix_to_device
122 |
123 | -- Connect signals
124 | if old_widget ~= widget then
125 | widget:weak_connect_signal("widget::redraw_needed", self._redraw)
126 | widget:weak_connect_signal("widget::layout_changed", self._layout)
127 | widget:weak_connect_signal("widget::emit_recursive", self._emit_recursive)
128 | end
129 |
130 | -- Update children
131 | local old_children = self._children
132 | local layout_result = base.layout_widget(no_parent, context, widget, width, height)
133 | self._children = {}
134 | for _, w in ipairs(layout_result or {}) do
135 | local r = table.remove(old_children, 1)
136 | if not r then
137 | r = hierarchy_new(self._redraw_callback, self._layout_callback, self._callback_arg)
138 | r._parent = self
139 | end
140 | hierarchy_update(r, context, w._widget, w._width, w._height, region, w._matrix, w._matrix * matrix_to_device)
141 | table.insert(self._children, r)
142 | end
143 |
144 | -- Calculate the draw extents
145 | local x1, y1, x2, y2 = 0, 0, width, height
146 | if not widget.clip_child_extends then
147 | for _, h in ipairs(self._children) do
148 | local px, py, pwidth, pheight = matrix.transform_rectangle(h._matrix, h:get_draw_extents())
149 | x1 = math.min(x1, px)
150 | y1 = math.min(y1, py)
151 | x2 = math.max(x2, px + pwidth)
152 | y2 = math.max(y2, py + pheight)
153 | end
154 | end
155 | self._draw_extents = {
156 | x = x1, y = y1,
157 | width = x2 - x1,
158 | height = y2 - y1
159 | }
160 |
161 | -- Update widget counts
162 | self._widget_counts = {}
163 | if widgets_to_count[widget] and width > 0 and height > 0 then
164 | self._widget_counts[widget] = 1
165 | end
166 | for _, h in ipairs(self._children) do
167 | for w, count in pairs(h._widget_counts) do
168 | self._widget_counts[w] = (self._widget_counts[w] or 0) + count
169 | end
170 | end
171 |
172 | -- Check which part needs to be redrawn
173 |
174 | -- Are there any children which were removed? Their area needs a redraw.
175 | for _, child in ipairs(old_children) do
176 | local x, y, w, h = matrix.transform_rectangle(child._matrix_to_device, child:get_draw_extents())
177 | region:union_rectangle(cairo.RectangleInt{
178 | x = x, y = y, width = w, height = h
179 | })
180 | child._parent = nil
181 | end
182 |
183 | -- Did we change and need to be redrawn?
184 | local x, y, w, h = matrix.transform_rectangle(self._matrix_to_device, 0, 0, self._size.width, self._size.height)
185 | local new_x, new_y = math.floor(x), math.floor(y)
186 | local new_width, new_height = math.ceil(x + w) - new_x, math.ceil(y + h) - new_y
187 | if new_x ~= old_x or new_y ~= old_y or new_width ~= old_width or new_height ~= old_height or
188 | widget ~= old_widget then
189 | region:union_rectangle(cairo.RectangleInt{
190 | x = old_x, y = old_y, width = old_width, height = old_height
191 | })
192 | region:union_rectangle(cairo.RectangleInt{
193 | x = new_x, y = new_y, width = new_width, height = new_height
194 | })
195 | end
196 | end
197 |
198 | --- Create a new widget hierarchy that has no parent.
199 | -- @param context The context in which we are laid out.
200 | -- @param widget The widget that is at the base of the hierarchy.
201 | -- @param width The available width for this hierarchy.
202 | -- @param height The available height for this hierarchy.
203 | -- @param redraw_callback Callback that is called with the corresponding widget
204 | -- hierarchy on widget::redraw_needed on some widget.
205 | -- @param layout_callback Callback that is called with the corresponding widget
206 | -- hierarchy on widget::layout_changed on some widget.
207 | -- @param callback_arg A second argument that is given to the above callbacks.
208 | -- @return A new widget hierarchy
209 | -- @constructorfct wibox.hierarchy.new
210 | function hierarchy.new(context, widget, width, height, redraw_callback, layout_callback, callback_arg)
211 | local result = hierarchy_new(redraw_callback, layout_callback, callback_arg)
212 | result:update(context, widget, width, height)
213 | return result
214 | end
215 |
216 | --- Update a widget hierarchy with some new state.
217 | -- @param context The context in which we are laid out.
218 | -- @param widget The widget that is at the base of the hierarchy.
219 | -- @param width The available width for this hierarchy.
220 | -- @param height The available height for this hierarchy.
221 | -- @param[opt] region A region to use for accumulating changed parts
222 | -- @return A cairo region describing the changed parts (either the `region`
223 | -- argument or a new, internally created region).
224 | -- @method update
225 | function hierarchy:update(context, widget, width, height, region)
226 | region = region or cairo.Region.create()
227 | hierarchy_update(self, context, widget, width, height, region, self._matrix, self._matrix_to_device)
228 | return region
229 | end
230 |
231 | --- Get the widget that this hierarchy manages.
232 | -- @method get_widget
233 | function hierarchy:get_widget()
234 | return self._widget
235 | end
236 |
237 | --- Get a matrix that transforms to the parent's coordinate space from this
238 | -- hierarchy's coordinate system.
239 | -- @return A matrix describing the transformation.
240 | -- @method get_matrix_to_parent
241 | function hierarchy:get_matrix_to_parent()
242 | return self._matrix
243 | end
244 |
245 | --- Get a matrix that transforms to the base of this hierarchy's coordinate
246 | -- system (aka the coordinate system of the device that this
247 | -- hierarchy is applied upon) from this hierarchy's coordinate system.
248 | -- @return A matrix describing the transformation.
249 | -- @method get_matrix_to_device
250 | function hierarchy:get_matrix_to_device()
251 | return self._matrix_to_device
252 | end
253 |
254 | --- Get a matrix that transforms from the parent's coordinate space into this
255 | -- hierarchy's coordinate system.
256 | -- @return A matrix describing the transformation.
257 | -- @method get_matrix_from_parent
258 | function hierarchy:get_matrix_from_parent()
259 | local m = self:get_matrix_to_parent()
260 | return m:invert()
261 | end
262 |
263 | --- Get a matrix that transforms from the base of this hierarchy's coordinate
264 | -- system (aka the coordinate system of the device that this
265 | -- hierarchy is applied upon) into this hierarchy's coordinate system.
266 | -- @return A matrix describing the transformation.
267 | -- @method get_matrix_from_device
268 | function hierarchy:get_matrix_from_device()
269 | local m = self:get_matrix_to_device()
270 | return m:invert()
271 | end
272 |
273 | --- Get the extents that this hierarchy possibly draws to (in the current coordinate space).
274 | -- This includes the size of this element plus the size of all children
275 | -- (after applying the corresponding transformation).
276 | -- @return x, y, width, height
277 | -- @method get_draw_extents
278 | function hierarchy:get_draw_extents()
279 | local ext = self._draw_extents
280 | return ext.x, ext.y, ext.width, ext.height
281 | end
282 |
283 | --- Get the size that this hierarchy logically covers (in the current coordinate space).
284 | -- @return width, height
285 | -- @method get_size
286 | function hierarchy:get_size()
287 | local ext = self._size
288 | return ext.width, ext.height
289 | end
290 |
291 | --- Get a list of all children.
292 | -- @return List of all children hierarchies.
293 | -- @method get_children
294 | function hierarchy:get_children()
295 | return self._children
296 | end
297 |
298 | --- Count how often this widget is visible inside this hierarchy. This function
299 | -- only works with widgets registered via `count_widget`.
300 | -- @param widget The widget that should be counted
301 | -- @return The number of times that this widget is contained in this hierarchy.
302 | -- @method get_count
303 | function hierarchy:get_count(widget)
304 | return self._widget_counts[widget] or 0
305 | end
306 |
307 | --- Does the given cairo context have an empty clip (aka "no drawing possible")?
308 | local function empty_clip(cr)
309 | local x1, y1, x2, y2 = cr:clip_extents()
310 | return x2 - x1 == 0 or y2 - y1 == 0
311 | end
312 |
313 | --- Draw a hierarchy to some cairo context.
314 | -- This function draws the widgets in this widget hierarchy to the given cairo
315 | -- context. The context's clip is used to skip parts that aren't visible.
316 | -- @param context The context in which widgets are drawn.
317 | -- @param cr The cairo context that is used for drawing.
318 | -- @method draw
319 | function hierarchy:draw(context, cr)
320 | local widget = self:get_widget()
321 | if not widget._private.visible then
322 | return
323 | end
324 |
325 | cr:save()
326 | cr:transform(self:get_matrix_to_parent():to_cairo_matrix())
327 |
328 | -- Clip to the draw extents
329 | cr:rectangle(self:get_draw_extents())
330 | cr:clip()
331 |
332 | -- Draw if needed
333 | if not empty_clip(cr) then
334 | local opacity = widget:get_opacity()
335 | local function call(func, extra_arg1, extra_arg2)
336 | if not func then return end
337 | if not extra_arg2 then
338 | protected_call(func, widget, context, cr, self:get_size())
339 | else
340 | protected_call(func, widget, context, extra_arg1, extra_arg2, cr, self:get_size())
341 | end
342 | end
343 |
344 | -- Prepare opacity handling
345 | if opacity ~= 1 then
346 | cr:push_group()
347 | end
348 |
349 | -- Draw the widget
350 | cr:save()
351 | cr:rectangle(0, 0, self:get_size())
352 | cr:clip()
353 | call(widget.draw)
354 | cr:restore()
355 | -- Clear any path that the widget might have left
356 | cr:new_path()
357 |
358 | -- Draw its children (We already clipped to the draw extents above)
359 | call(widget.before_draw_children)
360 | for i, wi in ipairs(self:get_children()) do
361 | call(widget.before_draw_child, i, wi:get_widget())
362 | wi:draw(context, cr)
363 | call(widget.after_draw_child, i, wi:get_widget())
364 | end
365 | call(widget.after_draw_children)
366 | -- Clear any path that the widget might have left
367 | cr:new_path()
368 |
369 | -- Apply opacity
370 | if opacity ~= 1 then
371 | cr:pop_group_to_source()
372 | cr.operator = cairo.Operator.OVER
373 | cr:paint_with_alpha(opacity)
374 | end
375 | end
376 |
377 | cr:restore()
378 | end
379 |
380 | return hierarchy
381 |
--------------------------------------------------------------------------------