├── .editorconfig ├── .github ├── README.md └── assets │ ├── screenshot_01.png │ ├── screenshot_02.png │ ├── screenshot_03.png │ └── screenshot_04.png ├── .gitignore ├── .vscode ├── extensions.json └── launch.json ├── LICENSE ├── awful ├── mouse │ └── snap.lua └── placement.lua ├── core ├── binding.lua ├── client.lua ├── init.lua ├── layout.lua ├── mouse.lua ├── notification.lua ├── path.lua ├── rule.lua ├── screen.lua ├── selection.lua ├── style.lua ├── system.lua ├── tag.lua ├── widget.lua └── workspace.lua ├── develop.lua ├── globals.lua ├── layouts ├── init.lua └── tilted │ ├── column_strategy.lua │ ├── init.lua │ └── layout_descriptor.lua ├── rc.lua ├── rice ├── apps.lua ├── bindings │ ├── apps │ │ ├── feh.lua │ │ └── mpv.lua │ ├── client.lua │ ├── global.lua │ └── init.lua ├── config.lua ├── init.lua ├── layouts.lua ├── media.lua ├── places.lua ├── power.lua ├── rules │ ├── apps │ │ ├── 1password.lua │ │ ├── dragon-drop.lua │ │ ├── freetube.lua │ │ ├── jetbrains.lua │ │ ├── localsend.lua │ │ ├── proton-vpn.lua │ │ ├── qr.lua │ │ ├── simplex-chat.lua │ │ ├── smartgit.lua │ │ ├── speedcrunch.lua │ │ ├── spotify.lua │ │ ├── xephyr.lua │ │ └── xev.lua │ ├── global.lua │ └── init.lua ├── tags.lua └── workspaces.lua ├── scripts └── run.sh ├── services ├── desktop.lua ├── init.lua ├── magnifier.lua ├── media │ ├── init.lua │ └── playerctl.lua ├── network.lua ├── power.lua ├── torrent.lua ├── volume.lua ├── wallpaper.lua └── weather.lua ├── theme ├── icons │ ├── _blank.svg │ ├── apple-keyboard-command.svg │ ├── application-variable-outline.svg │ ├── application-variable.svg │ ├── apps.svg │ ├── arrange-bring-forward.svg │ ├── arrow-down-right-bold.svg │ ├── arrow-u-left-top.svg │ ├── awesomewm.svg │ ├── bluetooth-settings.svg │ ├── book-clock-outline.svg │ ├── book-clock.svg │ ├── calculator.svg │ ├── calendar-month.svg │ ├── chart-line.svg │ ├── check-bold.svg │ ├── check.svg │ ├── checkbox-blank-outline.svg │ ├── checkbox-blank.svg │ ├── checkbox-marked.svg │ ├── chevron-double-up.svg │ ├── chevron-down.svg │ ├── chevron-left.svg │ ├── chevron-right.svg │ ├── chevron-triple-up.svg │ ├── chevron-up.svg │ ├── circle-medium.svg │ ├── circle-opacity.svg │ ├── clock-fast.svg │ ├── clock-time-1.svg │ ├── clock-time-10.svg │ ├── clock-time-11.svg │ ├── clock-time-12.svg │ ├── clock-time-2.svg │ ├── clock-time-3.svg │ ├── clock-time-4.svg │ ├── clock-time-5.svg │ ├── clock-time-6.svg │ ├── clock-time-7.svg │ ├── clock-time-8.svg │ ├── clock-time-9.svg │ ├── close.svg │ ├── cogs.svg │ ├── console-line.svg │ ├── content-copy.svg │ ├── delete-clock-outline.svg │ ├── delete-clock.svg │ ├── delete-forever.svg │ ├── dock-left.svg │ ├── dock-top.svg │ ├── download.svg │ ├── emoticon-cool.svg │ ├── exit-run.svg │ ├── eye-off.svg │ ├── file-document-edit.svg │ ├── firefox.svg │ ├── folder-image.svg │ ├── folder.svg │ ├── fullscreen.svg │ ├── help.svg │ ├── home-thermometer.svg │ ├── identifier.svg │ ├── image-filter-center-focus.svg │ ├── image-size-select-actual.svg │ ├── keyboard.svg │ ├── lan-disconnect.svg │ ├── lan-pending.svg │ ├── layers-triple.svg │ ├── layouts │ │ ├── floating.svg │ │ ├── fullscreen.svg │ │ ├── max.svg │ │ ├── tiling.center.svg │ │ └── tiling.right.svg │ ├── lightbulb-on.svg │ ├── lock.svg │ ├── logout.svg │ ├── menu-right.svg │ ├── menu.svg │ ├── minus.svg │ ├── monitor.svg │ ├── move-resize.svg │ ├── music.svg │ ├── open-in-app.svg │ ├── open-in-new.svg │ ├── pause.svg │ ├── pin-off.svg │ ├── pin.svg │ ├── pirate.svg │ ├── play.svg │ ├── plus.svg │ ├── power-settings.svg │ ├── power.svg │ ├── radiobox-blank.svg │ ├── radiobox-marked.svg │ ├── refresh.svg │ ├── rename.svg │ ├── restart.svg │ ├── shuffle-variant.svg │ ├── skip-next.svg │ ├── skip-previous.svg │ ├── sleep.svg │ ├── speedometer-slow.svg │ ├── speedometer.svg │ ├── stop.svg │ ├── tag-arrow-down.svg │ ├── tag-multiple.svg │ ├── tag.svg │ ├── target.svg │ ├── theme-light-dark.svg │ ├── thermometer-lines.svg │ ├── thermometer.svg │ ├── timer-outline.svg │ ├── timer-play.svg │ ├── timer-sand.svg │ ├── timer-settings.svg │ ├── toggle-switch-off-outline.svg │ ├── toggle-switch-off.svg │ ├── toggle-switch-outline.svg │ ├── toggle-switch.svg │ ├── toolbox.svg │ ├── tortoise.svg │ ├── tune.svg │ ├── unfold-less-vertical.svg │ ├── upload.svg │ ├── video.svg │ ├── view-grid.svg │ ├── volume.svg │ ├── water-thermometer.svg │ ├── weather-pouring.svg │ ├── weather-rainy.svg │ ├── web.svg │ ├── window-close.svg │ ├── window-maximize.svg │ └── window-minimize.svg ├── init.lua └── theme.lua ├── ui ├── client.lua ├── controller.lua ├── init.lua ├── menu │ ├── client_switcher.lua │ ├── init.lua │ ├── layout_switcher.lua │ ├── main.lua │ └── templates │ │ ├── applications.lua │ │ ├── client │ │ ├── _common.lua │ │ ├── init.lua │ │ ├── main.lua │ │ ├── opacity.lua │ │ ├── screens.lua │ │ ├── signals.lua │ │ └── tags.lua │ │ ├── init.lua │ │ ├── media_player.lua │ │ ├── power │ │ ├── init.lua │ │ └── timer.lua │ │ ├── tag │ │ ├── _common.lua │ │ ├── init.lua │ │ ├── layout.lua │ │ └── main.lua │ │ └── wallpaper.lua ├── mouse │ ├── edge_snap.lua │ ├── init.lua │ └── snap.lua ├── notification.lua ├── osd │ ├── init.lua │ └── volume.lua ├── popup │ ├── calendar.lua │ ├── init.lua │ ├── tools.lua │ └── weather.lua ├── titlebar.lua ├── topbar │ ├── clientlist.lua │ ├── datetime.lua │ ├── init.lua │ ├── layoutbox.lua │ ├── media_player.lua │ ├── network.lua │ ├── power.lua │ ├── redshift.lua │ ├── systray.lua │ ├── taglist.lua │ ├── tools.lua │ ├── torrent.lua │ ├── volume.lua │ └── weather.lua └── wallpaper.lua ├── utils ├── color.lua ├── css.lua ├── geometry.lua ├── humanizer.lua ├── math.lua ├── pango.lua ├── string.lua ├── table.lua ├── thickness.lua └── tree.lua ├── wibox ├── layout │ └── fixed.lua └── widget │ └── calendar.lua └── widget ├── bindbox.lua ├── capsule.lua ├── clock_icon.lua └── mebox ├── init.lua └── templates ├── header.lua ├── info.lua ├── init.lua ├── item.lua └── separator.lua /.editorconfig: -------------------------------------------------------------------------------- 1 | # See https://github.com/CppCXY/EmmyLuaCodeStyle 2 | 3 | [*.lua] 4 | indent_style = space 5 | indent_size = 4 6 | continuation_indent = 4 7 | tab_width = 4 8 | end_of_line = lf 9 | detect_end_of_line = false 10 | insert_final_newline = true 11 | max_line_length = unset 12 | quote_style = double 13 | trailing_table_separator = smart 14 | call_arg_parentheses = keep 15 | remove_call_expression_list_finish_comma = false 16 | 17 | # [space] 18 | space_around_table_field_list = true 19 | space_before_attribute = false 20 | space_before_function_open_parenthesis = false 21 | space_before_function_call_open_parenthesis = false 22 | space_before_closure_open_parenthesis = false 23 | space_before_function_call_single_arg = true 24 | space_before_open_square_bracket = false 25 | space_inside_function_call_parentheses = false 26 | space_inside_function_param_list_parentheses = false 27 | space_inside_square_brackets = false 28 | space_around_table_append_operator = false 29 | space_before_inline_comment = 1 30 | ignore_spaces_inside_function_call = false 31 | ignore_space_after_colon = false 32 | 33 | # [operator space] 34 | space_around_math_operator = true 35 | space_after_comma = true 36 | space_after_comma_in_for_statement = true 37 | space_around_concat_operator = true 38 | 39 | # [align] 40 | align_call_args = false 41 | align_function_params = true 42 | align_continuous_assign_statement = true 43 | align_continuous_rect_table_field = true 44 | align_if_branch = false 45 | align_array_table = false 46 | 47 | # [indent] 48 | never_indent_before_if_condition = false 49 | never_indent_comment_on_if_branch = false 50 | 51 | # [line space] 52 | line_space_after_if_statement = keep 53 | line_space_after_do_statement = keep 54 | line_space_after_while_statement = keep 55 | line_space_after_repeat_statement = keep 56 | line_space_after_for_statement = keep 57 | line_space_after_local_or_assign_statement = keep 58 | line_space_after_function_statement = min(2) 59 | line_space_after_expression_statement = keep 60 | line_space_after_comment = keep 61 | 62 | # [line break] 63 | break_all_list_when_line_exceed = false 64 | auto_collapse_lines = false 65 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | # AwesomeWM rice 2 | 3 | (Yet another) Config for [Awesome](https://github.com/awesomeWM/awesome) window manager. 4 | 5 | 6 | # Dependencies 7 | 8 | The following package names are from the Arch repositories or AUR. 9 | 10 | ## Required 11 | 12 | - `awesome-git` - window manager (latest git version, **not** stable v4.3) 13 | - `pulsemixer` - controlling the system volume 14 | - `playerctl` - controlling media player 15 | - `xdg-open` (from `xdg-utils`) - opening files or URLs in the preferred applications 16 | - `xclip` - copying things to the clipboard 17 | - `yay/pacman/aurc` - Requires a package manager to install dependencies from the AUR and non-aur, can include [`Aurc`](https://github.com/statulr/aurc), [`Pacman`](https://gitlab.archlinux.org/pacman/pacman), or/and for some dependencies [`yay`](https://github.com/Jguer/yay) 18 | ## Optional 19 | 20 | - [`feh`](https://archlinux.org/packages/extra/x86_64/feh/) - setting wallpaper 21 | - [`luarocks`](https://luarocks.org/) - Requires luarocks and the lua package to install dkjson and any other potential lua-modules 22 | - `dkjson` - Reading JSON Lua Module (Required for Weather and Other Widgets) Install via ```sudo luarocks install dkjson``` 23 | - [`maim`](https://github.com/naelstrof/maim), [`xdotool`](https://github.com/jordansissel/xdotool) - taking a screenshot 24 | - [`slop`](https://archlinux.org/packages/extra/x86_64/slop/) - magnifier (required [`boxzoom`](https://github.com/naelstrof/slop/tree/master/shaderexamples) shader), also screen region selection 25 | - [`sct`](https://flak.tedunangst.com/post/sct-set-color-temperature) - setting the color temperature of the screen 26 | 27 | ## Recommended 28 | 29 | - [`rofi`](https://github.com/davatorium/rofi) - application launcher ([here](https://gist.github.com/kosorin/2e613eb2e09f4f619b3f9f6c3c688c6b) is the config to match the style of this rice) 30 | - [`picom`](https://github.com/yshui/picom) - compositor for a better look 31 | 32 | ## Fonts 33 | 34 | - Nerd Font - use any Nerd Font that you like 35 | Here are some recommendations: 36 | [Iosevka Nerd Font](https://archlinux.org/packages/extra/any/ttf-iosevka-nerd/) 37 | [JetBrains Mono Nerd Font](https://archlinux.org/packages/extra/any/ttf-jetbrains-mono-nerd/) 38 | [Firacode Nerd](https://archlinux.org/packages/extra/any/ttf-firacode-nerd/) 39 | 40 | 41 | # Installation 42 | 43 | Backup your current config and download the files from GitHub to the empty `~/.config/awesome` directory: 44 | 45 | $ git clone https://github.com/kosorin/awesome-rice.git ~/.config/awesome 46 | 47 | 48 | # Setup 49 | 50 | Some [dependencies](#dependencies) are not listed and can be changed in `config.lua` file. So go ahead and set up your preferred applications. 51 | 52 | In `theme/theme.lua` you can change font or icon theme. 53 | 54 | 55 | # Gallery 56 | 57 | ## Screenshots 58 | 59 | ![img](assets/screenshot_01.png) 60 | 61 | ![img](assets/screenshot_02.png) 62 | 63 | ![img](assets/screenshot_03.png) 64 | 65 | ![img](assets/screenshot_04.png) 66 | 67 | 68 | # Credits 69 | 70 | - Simon Stålenhag (wallpapers) 71 | -------------------------------------------------------------------------------- /.github/assets/screenshot_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kosorin/awesome-rice/b6813bd1bbb3fdd697a1b994784b72daaeaf405d/.github/assets/screenshot_01.png -------------------------------------------------------------------------------- /.github/assets/screenshot_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kosorin/awesome-rice/b6813bd1bbb3fdd697a1b994784b72daaeaf405d/.github/assets/screenshot_02.png -------------------------------------------------------------------------------- /.github/assets/screenshot_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kosorin/awesome-rice/b6813bd1bbb3fdd697a1b994784b72daaeaf405d/.github/assets/screenshot_03.png -------------------------------------------------------------------------------- /.github/assets/screenshot_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kosorin/awesome-rice/b6813bd1bbb3fdd697a1b994784b72daaeaf405d/.github/assets/screenshot_04.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio Code 2 | *.code-workspace 3 | .vscode/* 4 | !.vscode/settings.json 5 | !.vscode/tasks.json 6 | !.vscode/launch.json 7 | !.vscode/extensions.json 8 | 9 | # Lua extension 10 | .luarc.json 11 | 12 | # rc files 13 | /rc.*.lua 14 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "sumneko.lua", 4 | "tomblind.local-lua-debugger-vscode", 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run with Xephyr", 6 | "type": "lua-local", 7 | "request": "launch", 8 | "program": { 9 | "command": "${workspaceFolder}/scripts/run.sh", 10 | }, 11 | "args": [ 12 | "--config", 13 | "${workspaceFolder}/rc.lua", 14 | "--bin", 15 | "/usr/bin/awesome", 16 | "--lib", 17 | "/usr/share/awesome/lib", 18 | "--lib", 19 | "${workspaceFolder}", 20 | ], 21 | }, 22 | ], 23 | } -------------------------------------------------------------------------------- /awful/mouse/snap.lua: -------------------------------------------------------------------------------- 1 | return {} -- Must be empty to override default implementation! 2 | -------------------------------------------------------------------------------- /core/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | binding = require("core.binding"), 3 | client = require("core.client"), 4 | selection = require("core.selection"), 5 | layout = require("core.layout"), 6 | mouse = require("core.mouse"), 7 | notification = require("core.notification"), 8 | path = require("core.path"), 9 | rule = require("core.rule"), 10 | screen = require("core.screen"), 11 | style = require("core.style"), 12 | system = require("core.system"), 13 | tag = require("core.tag"), 14 | widget = require("core.widget"), 15 | workspace = require("core.workspace"), 16 | } 17 | -------------------------------------------------------------------------------- /core/layout.lua: -------------------------------------------------------------------------------- 1 | local setmetatable = setmetatable 2 | local ipairs = ipairs 3 | local type = type 4 | 5 | 6 | ---@class core.layout 7 | local M = {} 8 | 9 | ---@param layouts awful.layout[] 10 | ---@return awful.layout[] 11 | function M.initialize_list(layouts) 12 | return setmetatable(layouts, { 13 | __index = function(self, key) 14 | ---@cast self awful.layout[] 15 | if type(key) == "string" then 16 | for _, layout in ipairs(self) do 17 | if layout.name == key then 18 | return layout 19 | end 20 | end 21 | end 22 | end, 23 | }) 24 | end 25 | 26 | return M 27 | -------------------------------------------------------------------------------- /core/notification.lua: -------------------------------------------------------------------------------- 1 | local naughty = require("naughty") 2 | local selection = require("core.selection") 3 | 4 | 5 | local M = {} 6 | 7 | naughty.connect_signal("request::display_error", function(message, startup) 8 | local copy_action = naughty.action { 9 | name = "Copy message", 10 | } 11 | 12 | copy_action:connect_signal("invoked", function() 13 | selection.clipboard:copy(message) 14 | end) 15 | 16 | naughty.notification { 17 | urgency = "critical", 18 | title = "Oops, an error happened" .. (startup and " during startup!" or "!"), 19 | message = message, 20 | actions = { copy_action }, 21 | } 22 | end) 23 | 24 | return M 25 | -------------------------------------------------------------------------------- /core/path.lua: -------------------------------------------------------------------------------- 1 | local match = string.match 2 | local getenv = os.getenv 3 | local gfilesystem = require("gears.filesystem") 4 | 5 | 6 | ---@class Core.Path 7 | ---@field home string 8 | ---@field config string 9 | ---@field awesome string 10 | ---@field theme string 11 | local M = {} 12 | 13 | ---@param path string 14 | ---@return string 15 | function M.remove_trailing_slash(path) 16 | return match(path, "^(/?.-)/*$") 17 | end 18 | 19 | M.home = M.remove_trailing_slash(getenv("HOME") or "/home") 20 | M.config = M.remove_trailing_slash(getenv("XDG_CONFIG_HOME") or (M.home .. "/.config")) 21 | M.awesome = M.remove_trailing_slash(gfilesystem.get_configuration_dir()) 22 | M.theme = M.remove_trailing_slash(M.awesome .. "/theme") 23 | 24 | return M 25 | -------------------------------------------------------------------------------- /core/rule.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local table = table 3 | local type = type 4 | local ipairs = ipairs 5 | local open = io.open 6 | local awful = require("awful") 7 | local ruled = require("ruled") 8 | local gtimer = require("gears.timer") 9 | 10 | 11 | local M = {} 12 | 13 | function M.delayed_callback(callback, timeout) 14 | if type(callback) ~= "function" then 15 | return nil 16 | end 17 | return function(client) 18 | gtimer { 19 | timeout = tonumber(timeout) or 0.1, 20 | autostart = true, 21 | single_shot = true, 22 | callback = function() 23 | callback(client) 24 | end, 25 | } 26 | end 27 | end 28 | 29 | do 30 | local blacklisted_snids = setmetatable({}, { __mode = "v" }) 31 | 32 | ruled.client.add_rule_source("fix_snid", function(client) 33 | if client.startup_id then 34 | blacklisted_snids[client.startup_id] = blacklisted_snids[client.startup_id] or client 35 | return 36 | end 37 | 38 | if not client.pid then 39 | return 40 | end 41 | 42 | local file = open("/proc/" .. client.pid .. "/environ", "rb") 43 | if not file then 44 | return 45 | end 46 | 47 | local snid = file:read("*all"):match("STARTUP_ID=([^\0]*)\0") 48 | 49 | file:close() 50 | 51 | if not snid or blacklisted_snids[snid] then 52 | return 53 | end 54 | blacklisted_snids[snid] = client 55 | 56 | client.startup_id = snid 57 | end, { "awful.spawn", "awful.rules" }, {}) 58 | end 59 | 60 | do 61 | local dialog_placement = awful.placement.centered + awful.placement.no_offscreen 62 | 63 | ruled.client.add_rule_source("fix_dialog", function(client, properties) 64 | if client.type ~= "dialog" then 65 | return 66 | end 67 | 68 | if not properties.placement then 69 | local parent = client.transient_for 70 | if not parent and client.pid then 71 | local screen = properties.screen 72 | and (type(properties.screen) == "function" 73 | and capi.screen[properties.screen(client, properties)] 74 | or capi.screen[properties.screen]) 75 | or nil 76 | if screen then 77 | local possible_parents = {} 78 | for _, cc in ipairs(screen.clients) do 79 | if client ~= cc and client.pid == cc.pid then 80 | table.insert(possible_parents, cc) 81 | end 82 | end 83 | parent = possible_parents[1] 84 | end 85 | end 86 | 87 | properties.placement = function(c) 88 | dialog_placement(c, { parent = parent }) 89 | end 90 | end 91 | end, { "awful.spawn", "awful.rules" }, {}) 92 | end 93 | 94 | return M 95 | -------------------------------------------------------------------------------- /core/screen.lua: -------------------------------------------------------------------------------- 1 | local ipairs = ipairs 2 | 3 | 4 | local M = {} 5 | 6 | function M.clients_to_tag(screen, tag) 7 | if not screen or not tag or not tag.activated then 8 | return 9 | end 10 | for _, client in ipairs(screen.all_clients) do 11 | for _, client_tag in ipairs(client:tags()) do 12 | if client_tag.selected then 13 | client:move_to_tag(tag) 14 | break 15 | end 16 | end 17 | end 18 | end 19 | 20 | return M 21 | -------------------------------------------------------------------------------- /core/selection.lua: -------------------------------------------------------------------------------- 1 | local lgi = require("lgi") 2 | local Gdk = lgi.require("Gdk", "3.0") 3 | local Gtk = lgi.require("Gtk", "3.0") 4 | local gtable = require("gears.table") 5 | 6 | 7 | ---@class Selection 8 | ---@field clipboard selection 9 | ---@field primary selection 10 | local M = {} 11 | 12 | ---@class selection 13 | ---@field package _selection unknown 14 | local selection_object = {} 15 | 16 | ---@param value any 17 | function selection_object:copy(value) 18 | self._selection:set_text(tostring(value) or "", -1) 19 | end 20 | 21 | local function new_selection(selection_type) 22 | ---@type selection 23 | local self = { 24 | _selection = Gtk.Clipboard.get(selection_type), 25 | } 26 | 27 | gtable.crush(self, selection_object, true) 28 | 29 | return self 30 | end 31 | 32 | M.clipboard = new_selection(Gdk.SELECTION_CLIPBOARD) 33 | M.primary = new_selection(Gdk.SELECTION_PRIMARY) 34 | 35 | return M 36 | -------------------------------------------------------------------------------- /core/system.lua: -------------------------------------------------------------------------------- 1 | -- DEPENDENCIES: uptime 2 | 3 | ---@class core.system 4 | ---@field up_since integer # System startup timestamp. 5 | local M = {} 6 | 7 | do 8 | local year, month, day, hour, min, sec = io 9 | .popen("uptime --since") 10 | :read("*all") 11 | :match("(%d+)%-(%d+)%-(%d+) (%d+):(%d+):(%d+)") 12 | M.up_since = os.time { 13 | year = year, 14 | month = month, 15 | day = day, 16 | hour = hour, 17 | min = min, 18 | sec = sec, 19 | } 20 | end 21 | 22 | return M 23 | -------------------------------------------------------------------------------- /core/tag.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local type = type 3 | local ipairs = ipairs 4 | local ruled = require("ruled") 5 | local ascreen = require("awful.screen") 6 | local atag = require("awful.tag") 7 | local gtable = require("gears.table") 8 | 9 | 10 | local M = {} 11 | 12 | ---@param args table 13 | ---@return table 14 | function M.build(args) 15 | args = args or {} 16 | args.screen = args.screen or capi.screen.primary 17 | args.name = args.name or "" 18 | 19 | local tag = {} 20 | capi.awesome.emit_signal("tag::build", tag, args) 21 | gtable.crush(tag, args) 22 | return tag 23 | end 24 | 25 | ---@param tag_index integer 26 | ---@param screen? screen 27 | ---@return tag? 28 | function M.get_or_create(tag_index, screen) 29 | screen = screen or ascreen.focused() 30 | if not screen then 31 | return nil 32 | end 33 | local tag = screen.tags[tag_index] 34 | if not tag then 35 | tag = atag.add(nil, M.build { 36 | screen = screen, 37 | }) 38 | end 39 | return tag 40 | end 41 | 42 | function ruled.client.high_priority_properties.new_tag(client, value, properties) 43 | local value_type = type(value) 44 | 45 | local args 46 | if value_type == "boolean" then 47 | args = { 48 | name = client.class, 49 | screen = client.screen, 50 | } 51 | elseif value_type == "string" then 52 | args = { 53 | name = value, 54 | screen = client.screen, 55 | } 56 | elseif value_type == "table" then 57 | args = gtable.clone(value) 58 | args.name = args.name or client.class 59 | args.screen = args.screen or client.screen 60 | else 61 | return 62 | end 63 | 64 | local tag = atag.add(nil, M.build(args)) 65 | 66 | properties.screen = tag.screen 67 | client.screen = tag.screen 68 | client:tags { tag } 69 | return tag 70 | end 71 | 72 | capi.screen.connect_signal("tag::history::update", function(screen) 73 | for _, tag in ipairs(screen.tags) do 74 | if tag.selected then 75 | tag.visited = true 76 | elseif tag.volatile and tag.visited and #tag:clients() == 0 then 77 | tag:delete() 78 | end 79 | end 80 | end) 81 | 82 | return M 83 | -------------------------------------------------------------------------------- /core/widget.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local ipairs = ipairs 3 | 4 | 5 | local M = {} 6 | 7 | ---@class core.widget.find_geometry 8 | ---@field drawable wibox.drawable 9 | ---@field hierarchy wibox.hierarchy 10 | ---@field widget wibox.widget.base 11 | ---@field widget_width number 12 | ---@field widget_height number 13 | ---@field x number 14 | ---@field y number 15 | ---@field width number 16 | ---@field height number 17 | 18 | ---@param widget wibox.widget.base 19 | ---@param drawable wibox.drawable 20 | ---@param hierarchy wibox.hierarchy 21 | ---@return core.widget.find_geometry|nil 22 | local function find_geometry_core(widget, drawable, hierarchy) 23 | local hierarchy_widget = hierarchy:get_widget() 24 | if hierarchy_widget == widget then 25 | local width, height = hierarchy:get_size() 26 | local matrix = hierarchy:get_matrix_to_device() 27 | local x, y, w, h = matrix:transform_rectangle(0, 0, width, height) 28 | return { 29 | drawable = drawable, 30 | hierarchy = hierarchy, 31 | widget = hierarchy_widget, 32 | widget_width = width, 33 | widget_height = height, 34 | x = x, 35 | y = y, 36 | width = w, 37 | height = h, 38 | } 39 | end 40 | 41 | for _, child in ipairs(hierarchy:get_children()) do 42 | local geometry = find_geometry_core(widget, drawable, child) 43 | if geometry then 44 | return geometry 45 | end 46 | end 47 | end 48 | 49 | ---@param widget wibox.widget.base 50 | ---@param wibox wibox 51 | ---@return core.widget.find_geometry|nil 52 | function M.find_geometry(widget, wibox) 53 | local drawable = wibox and wibox._drawable 54 | local hierarchy = drawable and drawable._widget_hierarchy 55 | if not hierarchy then 56 | return 57 | end 58 | return find_geometry_core(widget, drawable, hierarchy) 59 | end 60 | 61 | local function is_under_pointer_core(widget, x, y, hierarchy) 62 | local matrix = hierarchy:get_matrix_from_device() 63 | local x1, y1 = matrix:transform_point(x, y) 64 | local x2, y2, w2, h2 = hierarchy:get_draw_extents() 65 | if x1 < x2 or x1 >= x2 + w2 then 66 | return 67 | end 68 | if y1 < y2 or y1 >= y2 + h2 then 69 | return 70 | end 71 | 72 | if widget == hierarchy:get_widget() then 73 | local width, height = hierarchy:get_size() 74 | return x1 >= 0 and y1 >= 0 and x1 <= width and y1 <= height 75 | end 76 | 77 | for _, child in ipairs(hierarchy:get_children()) do 78 | local result = is_under_pointer_core(widget, x, y, child) 79 | if result ~= nil then 80 | return result 81 | end 82 | end 83 | end 84 | 85 | function M.is_under_pointer(widget) 86 | local wibox = capi.mouse.current_wibox 87 | if not wibox then 88 | return 89 | end 90 | 91 | local drawable = wibox._drawable 92 | local hierarchy = drawable and drawable._widget_hierarchy 93 | if not hierarchy then 94 | return 95 | end 96 | 97 | local coords = capi.mouse:coords() 98 | local geometry = wibox:geometry() 99 | local border_width = wibox.border_width 100 | local x = coords.x - geometry.x - border_width 101 | local y = coords.y - geometry.y - border_width 102 | return is_under_pointer_core(widget, x, y, hierarchy) 103 | end 104 | 105 | return M 106 | -------------------------------------------------------------------------------- /core/workspace.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local atag = require("awful.tag") 3 | local ruled = require("ruled") 4 | local core_tag = require("core.tag") 5 | 6 | 7 | ---@class _Workspace 8 | ---@field items Workspace.Item[] 9 | local M = { 10 | items = {}, 11 | } 12 | 13 | do 14 | ---@class Workspace.Item 15 | ---@field key string 16 | ---@field factory fun(): tag 17 | ---@field container { value: tag } 18 | local Item = {} 19 | 20 | function Item:get_tag() 21 | local tag = self.container.value 22 | if not tag or not tag.activated then 23 | tag = atag.add(nil, core_tag.build(self:factory())) 24 | self.container.value = tag 25 | end 26 | return tag 27 | end 28 | 29 | ---@param key string 30 | ---@param factory fun(): tag 31 | function M.add(key, factory) 32 | M.items[key] = setmetatable({ 33 | key = key, 34 | factory = factory, 35 | container = setmetatable({}, { __mode = "v" }), 36 | }, { __index = Item }) 37 | end 38 | end 39 | 40 | ---@param client client 41 | ---@param value string 42 | ---@param properties table 43 | function ruled.client.high_priority_properties.workspace(client, value, properties) 44 | local item = M.items[value] 45 | local tag = item and item:get_tag() 46 | if not tag then 47 | return 48 | end 49 | 50 | properties.screen = tag.screen 51 | client.screen = tag.screen 52 | client:tags { tag } 53 | return tag 54 | end 55 | 56 | return M 57 | -------------------------------------------------------------------------------- /develop.lua: -------------------------------------------------------------------------------- 1 | DEBUG = (os.getenv("DEBUG") or "") ~= "" 2 | if DEBUG and os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then 3 | require("lldebugger").start() 4 | end 5 | 6 | local dump 7 | if DEBUG then 8 | local gdebug = require("gears.debug") 9 | 10 | ---@type fun(data: any, tag?: string, depth?: integer) 11 | dump = gdebug.dump 12 | else 13 | local gdebug = require("gears.debug") 14 | local notification = require("naughty.notification") 15 | 16 | ---@param data any 17 | ---@param tag? string 18 | ---@param depth? integer 19 | function dump(data, tag, depth) 20 | notification { 21 | title = "DUMP", 22 | text = gdebug.dump_return(data, tag, depth), 23 | timeout = 0, 24 | } 25 | end 26 | end 27 | Dump = dump 28 | 29 | ---@param c client 30 | function DumpClient(c) 31 | Dump({ 32 | name = c.name, 33 | class = c.class, 34 | instance = c.instance, 35 | role = c.role, 36 | type = c.type, 37 | }, "", 1) 38 | end 39 | -------------------------------------------------------------------------------- /globals.lua: -------------------------------------------------------------------------------- 1 | Capi = { 2 | ---@diagnostic disable: undefined-global 3 | awesome = awesome, 4 | button = button, 5 | client = client, 6 | dbus = dbus, 7 | drawable = drawable, 8 | drawin = drawin, 9 | key = key, 10 | keygrabber = keygrabber, 11 | mouse = mouse, 12 | mousegrabber = mousegrabber, 13 | root = root, 14 | screen = screen, 15 | selection = selection, 16 | tag = tag, 17 | window = window, 18 | ---@diagnostic enable: undefined-global 19 | } 20 | 21 | ---@type fun(value: number): number 22 | Dpi = require("beautiful.xresources").apply_dpi 23 | -------------------------------------------------------------------------------- /layouts/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | tilted = require("layouts.tilted"), 3 | } 4 | -------------------------------------------------------------------------------- /layouts/tilted/column_strategy.lua: -------------------------------------------------------------------------------- 1 | local floor = math.floor 2 | local fmod = math.fmod 3 | 4 | 5 | ---@class Tilted.ColumnStrategy 6 | ---@field get_column_index fun(column_display_index: integer, size: integer, reverse: boolean): integer 7 | ---@field get_column_display_index fun(column_index: integer, size: integer, reverse: boolean): integer 8 | 9 | local column_strategy = {} 10 | 11 | --[[ 12 | ``` 13 | +─────+─────+─────+─────+─────+ 14 | | 1 | 2 | 3 | 4 | 5 | 15 | +─────+─────+─────+─────+─────+ 16 | ``` 17 | ]] 18 | ---@type Tilted.ColumnStrategy 19 | column_strategy.linear = { 20 | get_column_index = function(column_display_index, size, reverse) 21 | return reverse 22 | and (size - column_display_index + 1) 23 | or column_display_index 24 | end, 25 | get_column_display_index = function(column_index, size, reverse) 26 | return reverse 27 | and (size - column_index + 1) 28 | or column_index 29 | end, 30 | } 31 | 32 | --[[ 33 | ``` 34 | +─────+─────+─────+─────+─────+ 35 | | 5 | 3 | 1 | 2 | 4 | 36 | +─────+─────+─────+─────+─────+ 37 | ``` 38 | ]] 39 | ---@type Tilted.ColumnStrategy 40 | column_strategy.center = { 41 | get_column_index = function(column_display_index, size, reverse) 42 | if size > 2 then 43 | if reverse then 44 | local half = floor(size / 2) 45 | if column_display_index <= half then 46 | return 2 * (half - column_display_index + 1) 47 | else 48 | local carry = fmod(size, 2) 49 | return carry + (2 * column_display_index) - 1 - size 50 | end 51 | else 52 | local half = floor((size + 1) / 2) 53 | if column_display_index > half then 54 | return 2 * (column_display_index - half) 55 | else 56 | local carry = fmod(size + 1, 2) 57 | return size - (2 * (column_display_index - 1)) - carry 58 | end 59 | end 60 | else 61 | return reverse 62 | and (size - column_display_index + 1) 63 | or column_display_index 64 | end 65 | end, 66 | get_column_display_index = function(column_index, size, reverse) 67 | if size > 2 then 68 | if reverse then 69 | if (column_index % 2) == 0 then 70 | local half = floor(size / 2) 71 | return half - (column_index / 2) + 1 72 | else 73 | local carry = fmod(size, 2) 74 | return (1 + size - carry + column_index) / 2 75 | end 76 | else 77 | if (column_index % 2) == 0 then 78 | local half = floor((size + 1) / 2) 79 | return half + (column_index / 2) 80 | else 81 | local carry = fmod(size + 1, 2) 82 | return (2 + size - carry - column_index) / 2 83 | end 84 | end 85 | else 86 | return reverse 87 | and (size - column_index + 1) 88 | or column_index 89 | end 90 | end, 91 | } 92 | 93 | return column_strategy 94 | -------------------------------------------------------------------------------- /rc.lua: -------------------------------------------------------------------------------- 1 | require("develop") 2 | require("globals") 3 | 4 | require("core") 5 | 6 | require("theme") 7 | require("rice") 8 | require("services") 9 | require("ui") 10 | 11 | ---@diagnostic disable: param-type-mismatch 12 | collectgarbage("setpause", 110) 13 | collectgarbage("setstepmul", 1000) 14 | ---@diagnostic enable: param-type-mismatch 15 | -------------------------------------------------------------------------------- /rice/apps.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local awful = require("awful") 3 | local core = require("core") 4 | local tilted = require("layouts.tilted") 5 | local beautiful = require("theme.theme") 6 | 7 | 8 | ---@class Rice.Apps 9 | ---@field menu AppMenu 10 | local apps = { 11 | menu = { 12 | favorites = { 13 | "Alacritty.desktop", 14 | "speedcrunch.desktop", 15 | { 16 | id = "firefox.desktop", 17 | name = "Firefox", 18 | }, 19 | "spotify.desktop", 20 | "freetube.desktop", 21 | { 22 | id = "code.desktop", 23 | name = "Code", 24 | }, 25 | }, 26 | fallback_category = { 27 | name = "Other", 28 | }, 29 | categories = { 30 | utility = { 31 | id = "Utility", 32 | name = "Accessories", 33 | icon_name = "applications-accessories", 34 | icon_color = beautiful.palette.green, 35 | }, 36 | development = { 37 | id = "Development", 38 | name = "Development", 39 | icon_name = "applications-development", 40 | icon_color = beautiful.palette.cyan, 41 | }, 42 | education = { 43 | id = "Education", 44 | name = "Education", 45 | icon_name = "applications-science", 46 | icon_color = beautiful.palette.gray, 47 | }, 48 | games = { 49 | id = { "Game", "Games" }, 50 | name = "Games", 51 | icon_name = "applications-games", 52 | icon_color = beautiful.palette.red, 53 | }, 54 | graphics = { 55 | id = "Graphics", 56 | name = "Graphics", 57 | icon_name = "applications-graphics", 58 | icon_color = beautiful.palette.yellow, 59 | }, 60 | internet = { 61 | id = "Network", 62 | name = "Internet", 63 | icon_name = "applications-internet", 64 | icon_color = beautiful.palette.blue, 65 | }, 66 | multimedia = { 67 | id = "AudioVideo", 68 | name = "Multimedia", 69 | icon_name = "applications-multimedia", 70 | icon_color = beautiful.palette.cyan, 71 | }, 72 | office = { 73 | id = "Office", 74 | name = "Office", 75 | icon_name = "applications-office", 76 | icon_color = beautiful.palette.white, 77 | }, 78 | science = { 79 | id = "Science", 80 | name = "Science", 81 | icon_name = "applications-science", 82 | icon_color = beautiful.palette.magenta, 83 | }, 84 | settings = { 85 | id = "Settings", 86 | name = "Settings", 87 | icon_name = "applications-utilities", 88 | icon_color = beautiful.palette.orange, 89 | }, 90 | tools = { 91 | id = "System", 92 | name = "System Tools", 93 | icon_name = "applications-system", 94 | icon_color = beautiful.palette.gray, 95 | }, 96 | }, 97 | }, 98 | } 99 | 100 | return apps 101 | -------------------------------------------------------------------------------- /rice/bindings/apps/feh.lua: -------------------------------------------------------------------------------- 1 | local binding = require("core.binding") 2 | local mod = binding.modifier 3 | local btn = binding.button 4 | 5 | ---@type BindboxGroup 6 | return { 7 | name = "feh", 8 | rule = { rule = { instance = "feh", class = "feh" } }, 9 | bg = "#70011a", 10 | { "Escape", "q", description = "Quit" }, 11 | { "x", description = "Close current window" }, 12 | { "f", description = "Toggle fullscreen" }, 13 | { "c", description = "Caption entry mode" }, 14 | { modifiers = { mod.control }, "Delete", description = "Delete current image file" }, 15 | groups = { 16 | { 17 | name = "Image", 18 | { "s", description = "Save the image" }, 19 | { "r", description = "Reload the image" }, 20 | { modifiers = { mod.control }, "r", description = "Render the image" }, 21 | { modifiers = { mod.shift }, binding.button.left, description = "Blur the image" }, 22 | { "<", ">", description = "Rotate 90 degrees" }, 23 | { modifiers = { mod.shift }, binding.button.middle, description = "Rotate" }, 24 | { "_", description = "Vertically flip" }, 25 | { "|", description = "Horizontally flip" }, 26 | { modifiers = { mod.control }, "Left", "Up", "Right", "Down", description = "Scroll/pan" }, 27 | { modifiers = { mod.alt }, "Left", "Up", "Right", "Down", description = "Scroll/pan by one page" }, 28 | { binding.button.left, description = "Pan" }, 29 | { binding.button.middle, "KP_Add", "KP_Subtract", "Up", "Down", description = "Zoom in/out" }, 30 | { "*", description = "Zoom to 100%" }, 31 | { "/", description = "Zoom to fit the window size" }, 32 | { "!", description = "Zoom to fill the window size" }, 33 | { modifiers = { mod.shift }, "z", description = "Toggle auto-zoom in fullscreen" }, 34 | { "k", description = "Toggle zoom and viewport keeping" }, 35 | { "g", description = "Toggle window size keeping" }, 36 | }, 37 | { 38 | name = "Filelist", 39 | { modifiers = { mod.shift }, "l", description = "Save the filelist" }, 40 | { binding.button.wheel_up, "space", "Right", "n", description = "Show next image" }, 41 | { binding.button.wheel_down, "BackSpace", "Left", "p", description = "Show previous image" }, 42 | { "Home", "End", description = "Show first/last image" }, 43 | { "Prior", "Next", description = "Go ~5% of the filelist" }, 44 | { "z", description = "Jump to a random image" }, 45 | { "Delete", description = "Remove the image" }, 46 | }, 47 | { 48 | name = "UI", 49 | { binding.button.right, "m", description = "Show menu" }, 50 | { "d", description = "Toggle filename display" }, 51 | { "e", description = "Toggle EXIF tag display" }, 52 | { "i", description = "Toggle info display" }, 53 | { "o", description = "Toggle pointer visibility" }, 54 | }, 55 | { 56 | name = "Slideshow", 57 | { "h", description = "Pause/continue the slideshow" }, 58 | }, 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /rice/bindings/apps/mpv.lua: -------------------------------------------------------------------------------- 1 | local binding = require("core.binding") 2 | local mod = binding.modifier 3 | local btn = binding.button 4 | 5 | ---@type BindboxGroup 6 | return { 7 | name = "mpv", 8 | rule = { rule = { instance = "gl", class = "mpv" } }, 9 | bg = "#5f2060", 10 | { "q", description = "Quit" }, 11 | { modifiers = { mod.shift }, "q", description = "Store the playback position and quit" }, 12 | { "f", description = "Toggle fullscreen" }, 13 | groups = { 14 | { 15 | name = "Playback", 16 | { "space", "p", description = "Toggle pause" }, 17 | { ",", ".", description = "Step backward/forward 1 frame" }, 18 | { modifiers = { mod.shift }, "Left", "Right", description = "Seek backward/forward 1 second" }, 19 | { "Left", "Right", description = "Seek backward/forward 5 seconds" }, 20 | { "Up", "Down", description = "Seek backward/forward 1 minute" }, 21 | { binding.button.wheel_up, binding.button.wheel_down, description = "Seek backward/forward 10 seconds" }, 22 | { "l", description = "Set/clear A-B loop points" }, 23 | { modifiers = { mod.shift }, "l", description = "Toggle infinite looping" }, 24 | { "[", "]", description = "Decrease/increase current playback speed by 10%" }, 25 | { "{", "}", description = "Halve/double current playback speed" }, 26 | { "BackSpace", description = "Reset playback speed to normal" }, 27 | }, 28 | { 29 | name = "Video", 30 | { "_", description = "Cycle through the available video tracks" }, 31 | { "w", "W", description = "Decrease/increase pan-and-scan range" }, 32 | { modifiers = { mod.shift }, "a", description = "Cycle aspect ratio override" }, 33 | { "1", "2", description = "Adjust contrast" }, 34 | { "3", "4", description = "Adjust brightness" }, 35 | { "5", "6", description = "Adjust gamma" }, 36 | { "7", "8", description = "Adjust saturation" }, 37 | { modifiers = { mod.alt }, "Left", "Up", "Right", "Down", description = "Move the video rectangle" }, 38 | { modifiers = { mod.alt }, "+", "-", description = "Zoom the video" }, 39 | { modifiers = { mod.alt }, "BackSpace", description = "Reset the pan/zoom settings" }, 40 | }, 41 | { 42 | name = "Audio", 43 | { "#", description = "Cycle through the available audio tracks" }, 44 | { "m", description = "Mute sound" }, 45 | { binding.button.wheel_left, "/", "9", description = "Decrease volume" }, 46 | { binding.button.wheel_right, "*", "0", description = "Increase volume" }, 47 | { modifiers = { mod.control }, "+", "-", description = "Adjust audio delay by +/- 0.1 seconds" }, 48 | }, 49 | { 50 | name = "Subtitles", 51 | { "v", description = "Toggle subtitle visibility" }, 52 | { "j", "J", description = "Cycle through the available subtitles" }, 53 | { "z", "Z", description = "Adjust subtitle delay by +/- 0.1 seconds" }, 54 | { 55 | modifiers = { mod.control }, 56 | "Left", 57 | "Right", 58 | description = "Seek to the previous/next subtitle", 59 | }, 60 | { 61 | modifiers = { mod.control, mod.shift }, 62 | "Left", 63 | "Right", 64 | description = "Adjust subtitle delay so that the previous/next subtitle is displayed now", 65 | }, 66 | { "r", "R", description = "Move subtitles up/down" }, 67 | { modifiers = { mod.shift }, "g", "f", description = "Adjust subtitle font size by +/- 10%" }, 68 | }, 69 | { 70 | name = "Playlist", 71 | { "<", ">", description = "Go backward/forward" }, 72 | { "Return", description = "Go forward" }, 73 | { binding.button.extra_back, binding.button.extra_forward, description = "Skip to previous/next entry" }, 74 | { "F8", description = "Show the playlist and the current position in it" }, 75 | }, 76 | { 77 | name = "Other", 78 | { modifiers = {}, "s", description = "Take a screenshot" }, 79 | { modifiers = { mod.shift }, "s", description = "Take a screenshot without subtitles" }, 80 | { modifiers = { mod.control }, "s", description = "Take a screenshot as the window shows it" }, 81 | { "o", "O", description = "Show/toggle OSD playback" }, 82 | { "i", "I", description = "Show/toggle an overlay displaying statistics" }, 83 | { "F9", description = "Show the list of audio and subtitle streams" }, 84 | { "`", description = "Show the console" }, 85 | }, 86 | }, 87 | } 88 | -------------------------------------------------------------------------------- /rice/bindings/init.lua: -------------------------------------------------------------------------------- 1 | local binding = require("core.binding") 2 | local mod = binding.modifier 3 | local btn = binding.button 4 | local bindbox = require("widget.bindbox") 5 | 6 | 7 | ---@class Rice.Bindings 8 | local bindings = {} 9 | 10 | bindbox.main:add_group(require("rice.bindings.apps.feh")) 11 | bindbox.main:add_group(require("rice.bindings.apps.mpv")) 12 | bindbox.main:add_groups { 13 | { 14 | name = "System", 15 | }, 16 | { 17 | name = "Awesome", 18 | }, 19 | { 20 | name = "Launcher", 21 | }, 22 | { 23 | name = "Screen", 24 | }, 25 | { 26 | name = "Layout", 27 | { modifiers = { mod.super, mod.control }, "space", description = "Layout switcher" }, 28 | }, 29 | { 30 | name = "Tag", 31 | groups = { 32 | { 33 | name = "Client", 34 | }, 35 | }, 36 | }, 37 | { 38 | name = "Client", 39 | { modifiers = { mod.alt }, "Tab", description = "Client switcher" }, 40 | groups = { 41 | { 42 | name = "State", 43 | }, 44 | { 45 | name = "Layer", 46 | }, 47 | }, 48 | }, 49 | { 50 | name = "Action", 51 | }, 52 | { 53 | name = "Volume", 54 | }, 55 | { 56 | name = "Media", 57 | }, 58 | { 59 | name = "Screenshot", 60 | }, 61 | } 62 | 63 | binding.add_global_range(require("rice.bindings.global")) 64 | binding.add_client_range(require("rice.bindings.client")) 65 | 66 | return bindings 67 | -------------------------------------------------------------------------------- /rice/config.lua: -------------------------------------------------------------------------------- 1 | -- DEPENDENCIES (see below) 2 | 3 | local core = require("core") 4 | 5 | 6 | local config = {} 7 | 8 | ---@class Features 9 | ---@field screenshot_tools boolean? 10 | ---@field magnifier_tools boolean? 11 | ---@field torrent_widget boolean? 12 | ---@field weather_widget boolean? 13 | ---@field redshift_widget boolean? 14 | ---@field wallpaper_menu boolean? 15 | config.features = { 16 | screenshot_tools = false, 17 | magnifier_tools = false, 18 | torrent_widget = false, 19 | weather_widget = false, 20 | redshift_widget = false, 21 | wallpaper_menu = false, 22 | } 23 | 24 | config.wm = { 25 | name = "awesome", 26 | } 27 | 28 | local terminal = "alacritty" 29 | local terminal_execute = terminal .. " -e " 30 | 31 | config.apps = { 32 | shell = "bash", 33 | terminal = terminal, 34 | editor = terminal_execute .. "micro", 35 | browser = "firefox", 36 | private_browser = "firefox --private-window", 37 | file_manager = terminal_execute .. "lf", 38 | calculator = "speedcrunch", 39 | mixer = terminal_execute .. "pulsemixer", 40 | bluetooth_control = terminal_execute .. "bluetoothctl", 41 | } 42 | 43 | config.actions = { 44 | qr_code_clipboard = "qrclip", 45 | show_launcher = "rofi -show", 46 | show_emoji_picker = core.path.config .. "/rofi/emoji-run.sh", 47 | } 48 | 49 | config.commands = {} 50 | 51 | function config.commands.open(path) 52 | return "xdg-open \"" .. path .. "\"" 53 | end 54 | 55 | 56 | local awful_utils = require("awful.util") 57 | awful_utils.shell = config.apps.shell 58 | 59 | return config 60 | -------------------------------------------------------------------------------- /rice/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | apps = require("rice.apps"), 3 | bindings = require("rice.bindings"), 4 | config = require("rice.config"), 5 | layouts = require("rice.layouts"), 6 | media = require("rice.media"), 7 | places = require("rice.places"), 8 | power = require("rice.power"), 9 | rules = require("rice.rules"), 10 | tags = require("rice.tags"), 11 | workspaces = require("rice.workspaces"), 12 | } 13 | -------------------------------------------------------------------------------- /rice/layouts.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local awful = require("awful") 3 | local core = require("core") 4 | local tilted = require("layouts.tilted") 5 | 6 | 7 | ---@class Rice.Layouts 8 | ---@field list awful.layout[] 9 | local layouts = { 10 | list = core.layout.initialize_list { 11 | tilted.new("tiling"), 12 | awful.layout.suit.max, 13 | awful.layout.suit.max.fullscreen, 14 | awful.layout.suit.floating, 15 | }, 16 | } 17 | 18 | capi.tag.connect_signal("request::default_layouts", function() 19 | awful.layout.append_default_layouts(layouts.list) 20 | end) 21 | 22 | return layouts 23 | -------------------------------------------------------------------------------- /rice/media.lua: -------------------------------------------------------------------------------- 1 | ---@class Rice.Media 2 | ---@field players (string|"%any")[] 3 | ---@field menu AppMenu.ItemCollection 4 | local media = { 5 | players = { "spotify", "%any" }, 6 | menu = { 7 | "spotify.desktop", 8 | "freetube.desktop", 9 | }, 10 | } 11 | 12 | return media 13 | -------------------------------------------------------------------------------- /rice/places.lua: -------------------------------------------------------------------------------- 1 | local core = require("core") 2 | 3 | return { 4 | screenshots = core.path.home .. "/inbox/screenshots", 5 | wallpapers = core.path.home .. "/media/look/wallpapers", 6 | } 7 | -------------------------------------------------------------------------------- /rice/power.lua: -------------------------------------------------------------------------------- 1 | local power = {} 2 | 3 | power.timer = { 4 | default_timeout = 3600, 5 | minimum_timeout = 15, 6 | alert_threshold = 60, 7 | } 8 | 9 | power.commands = { 10 | shutdown = "systemctl poweroff", 11 | reboot = "systemctl reboot", 12 | suspend = "systemctl suspend", 13 | kill_session = "loginctl kill-session ''", 14 | lock_session = "loginctl lock-session", 15 | lock_screen = "light-locker-command --lock", 16 | } 17 | 18 | return power 19 | -------------------------------------------------------------------------------- /rice/rules/apps/1password.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | rule = { 4 | class = "^1Password$", 5 | }, 6 | properties = { 7 | floating = true, 8 | titlebars_enabled = true, 9 | }, 10 | }, 11 | { 12 | rule = { 13 | class = "^1Password$", 14 | name = "Quick Access", 15 | }, 16 | properties = { 17 | skip_taskbar = true, 18 | titlebars_enabled = "toolbox", 19 | }, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /rice/rules/apps/dragon-drop.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local beautiful = require("theme.theme") 3 | 4 | return { 5 | { 6 | rule = { 7 | class = "Dragon-drop", 8 | }, 9 | properties = { 10 | floating = true, 11 | ontop = true, 12 | sticky = true, 13 | placement = awful.placement.centered, 14 | titlebars_enabled = "toolbox", 15 | border_color = beautiful.common.secondary_bright, 16 | }, 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /rice/rules/apps/freetube.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | rule = { 4 | class = "^FreeTube$", 5 | }, 6 | properties = { 7 | new_tag = { 8 | name = "FreeTube", 9 | }, 10 | }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /rice/rules/apps/jetbrains.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local beautiful = require("theme.theme") 3 | local core_rule = require("core.rule") 4 | 5 | return { 6 | { 7 | rule = { 8 | name = "^JetBrains Toolbox$", 9 | }, 10 | properties = { 11 | floating = true, 12 | titlebars_enabled = "toolbox", 13 | }, 14 | callback = core_rule.delayed_callback(function(client) 15 | awful.placement.top_right(client, { 16 | honor_workarea = true, 17 | honor_padding = false, 18 | margins = beautiful.popup.margins, 19 | }) 20 | end), 21 | }, 22 | { 23 | rule_every = { 24 | class = { 25 | "jetbrains-rider", 26 | "jetbrains-rustrover", 27 | }, 28 | name = { 29 | "^Welcome to JetBrains Rider$", 30 | "^Welcome to RustRover$", 31 | }, 32 | }, 33 | properties = { 34 | floating = false, 35 | titlebars_enabled = true, 36 | }, 37 | }, 38 | { 39 | rule_every = { 40 | class = { 41 | "jetbrains-rider", 42 | "jetbrains-rustrover", 43 | }, 44 | name = { "^splash$" }, 45 | }, 46 | properties = { 47 | skip_taskbar = true, 48 | floating = true, 49 | titlebars_enabled = false, 50 | placement = awful.placement.centered, 51 | }, 52 | }, 53 | { 54 | rule_every = { 55 | class = { 56 | "jetbrains-rider", 57 | "jetbrains-rustrover", 58 | }, 59 | name = { 60 | "^ $", 61 | " – ", 62 | }, 63 | }, 64 | properties = { 65 | shape = false, 66 | titlebars_enabled = false, 67 | }, 68 | }, 69 | } 70 | -------------------------------------------------------------------------------- /rice/rules/apps/localsend.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local beautiful = require("theme.theme") 3 | 4 | return { 5 | { 6 | rule = { 7 | class = "^Localsend$", 8 | }, 9 | properties = { 10 | titlebars_enabled = "toolbox", 11 | floating = true, 12 | ontop = true, 13 | sticky = true, 14 | placement = function(client) 15 | awful.placement.top_right(client, { 16 | honor_workarea = true, 17 | margins = beautiful.popup.margins, 18 | }) 19 | end, 20 | }, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /rice/rules/apps/proton-vpn.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local beautiful = require("theme.theme") 3 | 4 | return { 5 | { 6 | rule = { 7 | class = "^Protonvpn$", 8 | }, 9 | properties = { 10 | titlebars_enabled = false, 11 | floating = true, 12 | ontop = true, 13 | sticky = true, 14 | placement = function(client) 15 | awful.placement.top_right(client, { 16 | honor_workarea = true, 17 | margins = beautiful.popup.margins, 18 | }) 19 | end, 20 | }, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /rice/rules/apps/qr.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | 3 | return { 4 | { 5 | rule = { 6 | class = "qr_code_clipboard", 7 | }, 8 | properties = { 9 | floating = true, 10 | ontop = true, 11 | sticky = true, 12 | placement = awful.placement.centered, 13 | titlebars_enabled = "toolbox", 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /rice/rules/apps/simplex-chat.lua: -------------------------------------------------------------------------------- 1 | return { 2 | rule = { 3 | class = "chat-simplex-desktop-MainKt", 4 | type = "normal", 5 | }, 6 | properties = { 7 | workspace = "chat", 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /rice/rules/apps/smartgit.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | rule = { 4 | class = "^SmartGit$", 5 | }, 6 | properties = { 7 | workspace = "git", 8 | shape = false, 9 | titlebars_enabled = "toolbox", 10 | }, 11 | }, 12 | { 13 | rule = { 14 | class = "^SmartGit$", 15 | name = "SmartGit %d+(%.%d+)+?%s*$", 16 | }, 17 | properties = { 18 | titlebars_enabled = false, 19 | }, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /rice/rules/apps/speedcrunch.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | rule = { 4 | class = "SpeedCrunch", 5 | }, 6 | properties = { 7 | floating = true, 8 | ontop = true, 9 | titlebars_enabled = true, 10 | }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /rice/rules/apps/spotify.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | rule = { 4 | class = "^Spotify$", 5 | }, 6 | properties = { 7 | new_tag = { 8 | name = "Spotify", 9 | }, 10 | }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /rice/rules/apps/xephyr.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | rule = { 4 | class = "^Xephyr$", 5 | }, 6 | properties = { 7 | floating = false, 8 | switch_to_tags = true, 9 | new_tag = { 10 | name = "Xephyr", 11 | selected = true, 12 | }, 13 | }, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /rice/rules/apps/xev.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local beautiful = require("theme.theme") 3 | 4 | return { 5 | { 6 | rule = { 7 | name = "^Event Tester$", 8 | }, 9 | properties = { 10 | titlebars_enabled = "toolbox", 11 | floating = true, 12 | ontop = true, 13 | sticky = true, 14 | placement = function(client) 15 | awful.placement.bottom_left(client, { 16 | honor_workarea = true, 17 | margins = beautiful.edge_gap, 18 | }) 19 | end, 20 | }, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /rice/rules/global.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local beautiful = require("theme.theme") 3 | 4 | return { 5 | { 6 | id = "global", 7 | rule = {}, 8 | properties = { 9 | screen = awful.screen.preferred, 10 | focus = awful.client.focus.filter, 11 | titlebars_enabled = DEBUG, 12 | raise = true, 13 | shape = beautiful.client.shape, 14 | }, 15 | callback = function(client) 16 | awful.client.setslave(client) 17 | end, 18 | }, 19 | { 20 | id = "tools", 21 | rule_any = { 22 | floating = true, 23 | type = "dialog", 24 | }, 25 | properties = { 26 | floating = true, 27 | titlebars_enabled = "toolbox", 28 | }, 29 | }, 30 | { 31 | id = "floating", 32 | rule_any = { 33 | class = { 34 | "Arandr", 35 | }, 36 | role = { 37 | "pop-up", 38 | }, 39 | }, 40 | properties = { 41 | floating = true, 42 | titlebars_enabled = true, 43 | }, 44 | }, 45 | { 46 | id = "picture_in_picture", 47 | rule_any = { 48 | name = { 49 | "Picture in picture", 50 | "Picture-in-Picture", 51 | }, 52 | }, 53 | properties = { 54 | titlebars_enabled = "toolbox", 55 | floating = true, 56 | ontop = true, 57 | sticky = true, 58 | placement = function(client) 59 | awful.placement.bottom_right(client, { 60 | honor_workarea = true, 61 | margins = beautiful.edge_gap, 62 | }) 63 | end, 64 | }, 65 | }, 66 | { 67 | id = "no_size_hints", 68 | rule_any = { 69 | class = { 70 | "XTerm", 71 | }, 72 | }, 73 | properties = { 74 | size_hints_honor = false, 75 | }, 76 | }, 77 | { 78 | id = "urgent", 79 | rule_any = { 80 | class = { 81 | "^Gcr-prompter$", 82 | }, 83 | name = { 84 | "^Authenticate$", 85 | }, 86 | }, 87 | properties = { 88 | floating = true, 89 | ontop = true, 90 | sticky = true, 91 | titlebars_enabled = "toolbox", 92 | placement = awful.placement.centered, 93 | }, 94 | }, 95 | } 96 | -------------------------------------------------------------------------------- /rice/rules/init.lua: -------------------------------------------------------------------------------- 1 | local ruled = require("ruled") 2 | 3 | 4 | ---@class Rice.Rules 5 | local rules = {} 6 | 7 | ruled.client.connect_signal("request::rules", function() 8 | ruled.client.append_rules(require("rice.rules.global")) 9 | ruled.client.append_rules(require("rice.rules.apps.1password")) 10 | ruled.client.append_rules(require("rice.rules.apps.dragon-drop")) 11 | ruled.client.append_rules(require("rice.rules.apps.freetube")) 12 | ruled.client.append_rules(require("rice.rules.apps.jetbrains")) 13 | ruled.client.append_rules(require("rice.rules.apps.localsend")) 14 | ruled.client.append_rules(require("rice.rules.apps.proton-vpn")) 15 | ruled.client.append_rules(require("rice.rules.apps.qr")) 16 | ruled.client.append_rules(require("rice.rules.apps.simplex-chat")) 17 | ruled.client.append_rules(require("rice.rules.apps.smartgit")) 18 | ruled.client.append_rules(require("rice.rules.apps.speedcrunch")) 19 | ruled.client.append_rules(require("rice.rules.apps.spotify")) 20 | ruled.client.append_rules(require("rice.rules.apps.xephyr")) 21 | ruled.client.append_rules(require("rice.rules.apps.xev")) 22 | end) 23 | 24 | return rules 25 | -------------------------------------------------------------------------------- /rice/tags.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local pairs = pairs 3 | local awful = require("awful") 4 | local core_tag = require("core.tag") 5 | local layouts = require("rice.layouts") 6 | 7 | 8 | ---@class Rice.Tags 9 | ---@field names string[] # List of default tag names for each screen 10 | local tags = { 11 | names = { 12 | "Main", 13 | }, 14 | } 15 | 16 | capi.awesome.connect_signal("tag::build", function(tag, args) 17 | tag.layout = layouts.list[1] 18 | tag.gap_single_client = false 19 | tag.master_fill_policy = "master_width_factor" 20 | tag.master_width_factor = 0.7 21 | tag.volatile = true 22 | end) 23 | 24 | capi.screen.connect_signal("request::desktop_decoration", function(screen) 25 | for index, name in pairs(tags.names) do 26 | awful.tag.add(nil, core_tag.build { 27 | name = name, 28 | screen = screen, 29 | selected = index == 1, 30 | volatile = false, 31 | }) 32 | end 33 | end) 34 | 35 | return tags 36 | -------------------------------------------------------------------------------- /rice/workspaces.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local pairs = pairs 3 | local core_workspaces = require("core.workspace") 4 | 5 | 6 | ---@class Rice.Workspaces 7 | ---@field factories table 8 | local workspaces = { 9 | factories = { 10 | hearthstone = function() 11 | return { 12 | name = "Hearthstone", 13 | screen = capi.screen.primary, 14 | } 15 | end, 16 | chat = function() 17 | return { 18 | name = "Chat", 19 | } 20 | end, 21 | git = function() 22 | return { 23 | name = "Git", 24 | } 25 | end, 26 | }, 27 | } 28 | 29 | for key, factory in pairs(workspaces.factories) do 30 | core_workspaces.add(key, factory) 31 | end 32 | 33 | return workspaces 34 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | set -e 4 | 5 | args=("") 6 | display=":2.0" 7 | size="1800x1000" 8 | debug="1" 9 | awesome_config="" 10 | awesome_bin="" 11 | awesome_libs=() 12 | 13 | function error() { 14 | echo "Error: $1" >&2 15 | exit "${2:-1}" 16 | } 17 | 18 | while [[ $# -gt 0 ]]; do 19 | case "$1" in 20 | -c | --config) 21 | awesome_config="$2" 22 | shift 23 | shift 24 | ;; 25 | -b | --bin) 26 | awesome_bin="$2" 27 | shift 28 | shift 29 | ;; 30 | -l | --lib) 31 | awesome_libs+=("$2") 32 | shift 33 | shift 34 | ;; 35 | -d | --display) 36 | display="$2" 37 | shift 38 | shift 39 | ;; 40 | -s | --size) 41 | size="$2" 42 | shift 43 | shift 44 | ;; 45 | -n | --no-debug) 46 | debug="" 47 | shift 48 | ;; 49 | -*) 50 | error "Unknown option $1" 2 51 | ;; 52 | *) 53 | args+=("$1") 54 | shift 55 | ;; 56 | esac 57 | done 58 | 59 | if [[ -z "$awesome_config" ]]; then 60 | error "Missing rc.lua path" 61 | fi 62 | if [[ -z "$awesome_bin" ]]; then 63 | error "Missing bin path" 64 | fi 65 | if [[ ${#awesome_libs[@]} -eq 0 ]]; then 66 | error "Missing lib path" 67 | fi 68 | 69 | echo "Starting Xephyr (display=$display, size=$size)" 70 | Xephyr "$display" -ac -br -noreset -screen "$size" & 71 | xephyr_pid=$! 72 | while ! DISPLAY="$display" xset q &>/dev/null; do sleep 0.1; done 73 | echo "Xephyr pid: $xephyr_pid" 74 | 75 | echo "Awesome bin: $awesome_bin" 76 | echo "Awesome libs: ${awesome_libs[*]}" 77 | echo "Awesome config: $awesome_config" 78 | echo "Starting awesome" 79 | echo "================================================================" 80 | DEBUG="$debug" DISPLAY="$display" "$awesome_bin" -c "$awesome_config" -s "${awesome_libs[@]}" 81 | echo "================================================================" 82 | echo "Stopping Xephyr" 83 | kill $xephyr_pid &>/dev/null 84 | -------------------------------------------------------------------------------- /services/init.lua: -------------------------------------------------------------------------------- 1 | local config = require("rice.config") 2 | 3 | local services = { 4 | magnifier = config.features.magnifier_tools and require("services.magnifier") or nil, 5 | volume = require("services.volume"), 6 | weather = config.features.weather_widget and require("services.weather") or nil, 7 | network = require("services.network"), 8 | torrent = config.features.torrent_widget and require("services.torrent") or nil, 9 | wallpaper = config.features.wallpaper_menu and require("services.wallpaper") or nil, 10 | power = require("services.power"), 11 | media = require("services.media"), 12 | } 13 | 14 | for _, service in pairs(services) do 15 | if type(service.watch) == "function" then 16 | service.watch() 17 | end 18 | end 19 | 20 | return services 21 | -------------------------------------------------------------------------------- /services/magnifier.lua: -------------------------------------------------------------------------------- 1 | -- DEPENDENCIES (feature flag "magnifier_tools"): slop, xclip 2 | 3 | local config = require("rice.config") 4 | if not config.features.magnifier_tools then 5 | return 6 | end 7 | 8 | local format = string.format 9 | local awful = require("awful") 10 | local beautiful = require("theme.theme") 11 | local tcolor = require("utils.color") 12 | 13 | 14 | local magnifier = {} 15 | 16 | function magnifier.run(args) 17 | args = args or {} 18 | 19 | local command = "slop --quiet --tolerance 0" 20 | command = format("%s --tolerance %.0f", command, args.tolerance or 0) 21 | command = format("%s --highlight --bordersize %.0f --color %s", command, 22 | beautiful.screen_selection_border_width, 23 | tcolor.format_slop(beautiful.screen_selection_color)) 24 | 25 | if args.shader ~= false then 26 | command = format("%s --shader %s", command, args.shader or "boxzoom") 27 | end 28 | 29 | if args.format then 30 | command = format("%s --format %s", command, args.format) 31 | end 32 | 33 | command = format("%s | xclip -rmlastnl -selection clipboard", command) 34 | 35 | awful.spawn.with_shell(command) 36 | end 37 | 38 | return magnifier 39 | -------------------------------------------------------------------------------- /services/media/init.lua: -------------------------------------------------------------------------------- 1 | local playerctl = require("services.media.playerctl") 2 | local rice_media = require("rice.media") 3 | 4 | 5 | local media_service = {} 6 | 7 | media_service.player = playerctl.new { 8 | players = rice_media.players, 9 | metadata = { 10 | title = "xesam:title", 11 | artist = "xesam:artist", 12 | }, 13 | } 14 | 15 | return media_service 16 | -------------------------------------------------------------------------------- /services/network.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local time = os.time 3 | local open = io.open 4 | local format = string.format 5 | local gears = require("gears") 6 | 7 | 8 | local network_service = { 9 | config = { 10 | interval = 2, 11 | interface = "enp6s0", 12 | }, 13 | last_data = { 14 | time = 0, 15 | download = nil, 16 | upload = nil, 17 | }, 18 | status = { 19 | success = nil, 20 | connected = false, 21 | download = nil, 22 | upload = nil, 23 | }, 24 | timer = nil, 25 | } 26 | 27 | local function read_file(path, type) 28 | local file = open(path, "r") 29 | if not file then 30 | return nil 31 | end 32 | local value = file:read(type) 33 | file:close() 34 | return value 35 | end 36 | 37 | local function read_data() 38 | local connected = read_file(format("/sys/class/net/%s/operstate", network_service.config.interface), "l") == "up" 39 | local download, upload 40 | if connected then 41 | download = read_file(format("/sys/class/net/%s/statistics/rx_bytes", network_service.config.interface), "n") or 0 42 | upload = read_file(format("/sys/class/net/%s/statistics/tx_bytes", network_service.config.interface), "n") or 0 43 | end 44 | return connected, download, upload 45 | end 46 | 47 | local function update() 48 | local status = network_service.status 49 | local last_data = network_service.last_data 50 | local success, connected, download, upload = pcall(read_data) 51 | local now = time() 52 | 53 | if success then 54 | if connected then 55 | local diff = now - last_data.time 56 | local is_valid = diff > 0 and diff < 3 * network_service.config.interval 57 | and last_data.download 58 | and last_data.upload 59 | if is_valid then 60 | status.download = (download - last_data.download) / diff 61 | status.upload = (upload - last_data.upload) / diff 62 | else 63 | status.download = 0 64 | status.upload = 0 65 | end 66 | end 67 | end 68 | 69 | status.success = success 70 | status.connected = connected 71 | if not success or not connected then 72 | status.download = nil 73 | status.upload = nil 74 | end 75 | 76 | last_data.time = now 77 | last_data.download = download 78 | last_data.upload = upload 79 | 80 | capi.awesome.emit_signal("network::updated", status) 81 | end 82 | 83 | function network_service.watch() 84 | network_service.timer = network_service.timer or gears.timer { 85 | timeout = network_service.config.interval, 86 | call_now = true, 87 | callback = update, 88 | } 89 | network_service.timer:again() 90 | end 91 | 92 | return network_service 93 | -------------------------------------------------------------------------------- /services/power.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local os_execute = os.execute 3 | local os_time = os.time 4 | local gtimer = require("gears.timer") 5 | local naughty = require("naughty") 6 | local maxinteger = math.maxinteger 7 | local power = require("rice.power") 8 | 9 | 10 | local power_service = {} 11 | 12 | local function execute(command) 13 | if DEBUG then 14 | print("power timer command: ", command) 15 | else 16 | os_execute(command) 17 | end 18 | end 19 | 20 | function power_service.shutdown() 21 | execute(power.commands.shutdown) 22 | end 23 | 24 | function power_service.reboot() 25 | execute(power.commands.reboot) 26 | end 27 | 28 | function power_service.suspend() 29 | execute(power.commands.suspend) 30 | end 31 | 32 | function power_service.kill_session() 33 | execute(power.commands.kill_session) 34 | end 35 | 36 | function power_service.lock_session() 37 | execute(power.commands.lock_session) 38 | end 39 | 40 | function power_service.lock_screen() 41 | execute(power.commands.lock_screen) 42 | end 43 | 44 | do 45 | local current_timer 46 | local alert_notification 47 | 48 | function power_service.get_timer_status() 49 | local remaining_seconds 50 | if current_timer then 51 | remaining_seconds = (current_timer.start + current_timer.seconds) - os_time() 52 | if remaining_seconds < 0 then 53 | remaining_seconds = true 54 | end 55 | else 56 | remaining_seconds = false 57 | end 58 | 59 | return remaining_seconds 60 | end 61 | 62 | local function timer_tick() 63 | local status = power_service.get_timer_status() 64 | 65 | if (tonumber(status) or maxinteger) <= power.timer.alert_threshold then 66 | local reason = current_timer and current_timer.reason or "Execute" 67 | if not alert_notification then 68 | local execute_action = naughty.action { name = string.format("%s now", reason) } 69 | local stop_action = naughty.action { name = "Stop timer" } 70 | execute_action:connect_signal("invoked", power_service.execute_now) 71 | stop_action:connect_signal("invoked", power_service.stop_timer) 72 | alert_notification = naughty.notification { 73 | title = "Power timer", 74 | urgency = "critical", 75 | timeout = status, 76 | category = "awesome.power.timer", 77 | actions = { execute_action, stop_action }, 78 | } 79 | end 80 | alert_notification.message = string.format("%s in %i seconds", reason or "Execute", status) 81 | end 82 | 83 | if not status then 84 | if alert_notification then 85 | alert_notification:destroy() 86 | end 87 | alert_notification = nil 88 | end 89 | 90 | capi.awesome.emit_signal("power::timer", status) 91 | end 92 | 93 | function power_service.stop_timer() 94 | if not current_timer then 95 | return 96 | end 97 | 98 | current_timer.action_timer:stop() 99 | current_timer.countdown_timer:stop() 100 | current_timer = nil 101 | 102 | timer_tick() 103 | end 104 | 105 | function power_service.execute_now() 106 | if not current_timer then 107 | return 108 | end 109 | 110 | local action = current_timer.action 111 | 112 | power_service.stop_timer() 113 | action() 114 | end 115 | 116 | function power_service.start_timer(request) 117 | power_service.stop_timer() 118 | 119 | local timeout = tonumber(request.timeout) or power.timer.default_timeout 120 | if timeout < power.timer.minimum_timeout then 121 | timeout = power.timer.minimum_timeout 122 | end 123 | 124 | local action = request.action or power_service.shutdown 125 | 126 | current_timer = { 127 | action = action, 128 | reason = request.reason, 129 | start = os_time(), 130 | seconds = timeout, 131 | countdown_timer = gtimer { 132 | timeout = 1, 133 | callback = timer_tick, 134 | }, 135 | action_timer = gtimer { 136 | timeout = timeout, 137 | single_shot = true, 138 | callback = function() 139 | power_service.stop_timer() 140 | action() 141 | end, 142 | }, 143 | } 144 | 145 | current_timer.countdown_timer:start() 146 | current_timer.action_timer:start() 147 | 148 | timer_tick() 149 | 150 | return true 151 | end 152 | end 153 | 154 | return power_service 155 | -------------------------------------------------------------------------------- /services/torrent.lua: -------------------------------------------------------------------------------- 1 | -- DEPENDENCIES (feature flag "torrent_widget"): transmission-status, lua-dkjson 2 | 3 | local config = require("rice.config") 4 | if not config.features.torrent_widget then 5 | return 6 | end 7 | 8 | local capi = Capi 9 | local table = table 10 | local json = require("dkjson") 11 | local gears = require("gears") 12 | local awful = require("awful") 13 | 14 | 15 | local torrent_service = { 16 | status_codes = { 17 | idle = 0, 18 | seeding = 1, 19 | verifying = 2, 20 | leeching = 3, 21 | }, 22 | config = { 23 | interval = 5, 24 | error_interval = 300, 25 | }, 26 | last_response = { 27 | success = nil, 28 | data = { 29 | alternative_speed_enabled = false, 30 | status = 0, 31 | eta = nil, 32 | any_unknown_eta = false, 33 | downloaded_size = 0, 34 | total_size = 0, 35 | }, 36 | }, 37 | timer = nil, 38 | } 39 | 40 | local function update_response(data) 41 | local response = torrent_service.last_response 42 | response.time = os.time() 43 | if data then 44 | if response.success ~= true then 45 | torrent_service.timer.timeout = torrent_service.config.interval 46 | torrent_service.timer:again() 47 | end 48 | response.success = true 49 | gears.table.crush(response.data, data) 50 | else 51 | if response.success ~= false then 52 | torrent_service.timer.timeout = torrent_service.config.error_interval 53 | torrent_service.timer:again() 54 | end 55 | response.success = false 56 | end 57 | capi.awesome.emit_signal("torrent::updated", response) 58 | end 59 | 60 | local function validate_data(data) 61 | if type(data) == "table" then 62 | if not data.error then 63 | return nil 64 | else 65 | return tostring(data.error) 66 | end 67 | else 68 | return "Bad data" 69 | end 70 | end 71 | 72 | local function on_raw_data(stdout, stderr, exitreason, exitcode) 73 | local unknown_error, data, error 74 | 75 | if exitreason == "exit" and exitcode == 0 then 76 | unknown_error, data, _, error = pcall(json.decode, stdout) 77 | if not unknown_error and not error then 78 | error = "Torrent status error: Unknown error" 79 | end 80 | else 81 | error = "Torrent status error: " .. tostring(exitcode) .. ", " .. exitreason .. " => " .. stderr 82 | end 83 | 84 | error = error or validate_data(data) 85 | 86 | if error then 87 | gears.debug.print_error(error) 88 | end 89 | 90 | update_response(data) 91 | end 92 | 93 | local function update(reset_timer, options) 94 | if reset_timer then 95 | torrent_service.timer:again() 96 | end 97 | local command = "transmission-status" 98 | if options and #options > 0 then 99 | command = command .. " " .. table.concat(options, " ") 100 | end 101 | awful.spawn.easy_async(command, on_raw_data) 102 | end 103 | 104 | function torrent_service.update() 105 | update(true) 106 | end 107 | 108 | function torrent_service.start() 109 | update(true, { "--start" }) 110 | end 111 | 112 | function torrent_service.stop() 113 | update(true, { "--stop" }) 114 | end 115 | 116 | function torrent_service.alternative_speed(enable) 117 | if enable == nil then 118 | enable = not torrent_service.last_response.data.alternative_speed_enabled 119 | end 120 | update(true, { "-a", enable and 1 or 0 }) 121 | end 122 | 123 | function torrent_service.watch() 124 | torrent_service.timer = torrent_service.timer or gears.timer { 125 | timeout = torrent_service.config.interval, 126 | call_now = true, 127 | callback = function() update(false) end, 128 | } 129 | torrent_service.timer:again() 130 | end 131 | 132 | return torrent_service 133 | -------------------------------------------------------------------------------- /services/volume.lua: -------------------------------------------------------------------------------- 1 | -- DEPENDENCIES: pulsemixer 2 | 3 | local capi = Capi 4 | local tonumber = tonumber 5 | local string = string 6 | local gtimer = require("gears.timer") 7 | local awful = require("awful") 8 | 9 | 10 | local volume_service = { 11 | config = { 12 | interval = 3, 13 | limit = 150, 14 | app = "pulsemixer", 15 | }, 16 | data = nil, 17 | timer = nil, 18 | } 19 | 20 | local commands = {} 21 | 22 | ---@return string 23 | function commands.get_data() 24 | return " --get-volume --get-mute" 25 | end 26 | 27 | ---@param volume number 28 | ---@return string 29 | function commands.set_volume(volume) 30 | return " --set-volume " .. string.format("%.0f", volume) .. " --max-volume " .. volume_service.config.limit 31 | end 32 | 33 | ---@param step number 34 | ---@return string 35 | function commands.change_volume(step) 36 | step = step or 1 37 | return " --change-volume " .. (step > 0 and "+" or "") .. string.format("%.0f", step) .. " --max-volume " .. volume_service.config.limit 38 | end 39 | 40 | ---@return string 41 | function commands.toggle_mute() 42 | return " --toggle-mute" 43 | end 44 | 45 | local function parse_raw_data(raw_data) 46 | local volume = nil 47 | local muted = nil 48 | 49 | local l = 1 50 | for line in string.gmatch(raw_data, "([^\n]*)\n?") do 51 | if l == 1 then 52 | -- Just take first channel, ignore other channels 53 | local volume_text = line:match("^(%d+)") 54 | volume = tonumber(volume_text) 55 | elseif l == 2 then 56 | local muted_text = line:match("^(%d)$") 57 | muted = muted_text == "1" 58 | else 59 | break 60 | end 61 | l = l + 1 62 | end 63 | 64 | return { 65 | volume = volume, 66 | muted = muted, 67 | } 68 | end 69 | 70 | local function process_command_output(stdout, stderr, exitreason, exitcode) 71 | local data = nil 72 | if exitreason == "exit" and exitcode == 0 then 73 | data = parse_raw_data(stdout) 74 | end 75 | return data 76 | end 77 | 78 | local function update(command, skip_osd) 79 | awful.spawn.easy_async(command, function(...) 80 | volume_service.data = process_command_output(...) or {} 81 | volume_service.data.skip_osd = skip_osd 82 | capi.awesome.emit_signal("volume::update", volume_service.data) 83 | end) 84 | end 85 | 86 | function volume_service.set_volume(volume, skip_osd) 87 | update(volume_service.config.app .. commands.set_volume(volume) .. commands.get_data(), skip_osd) 88 | end 89 | 90 | function volume_service.change_volume(step, skip_osd) 91 | update(volume_service.config.app .. commands.change_volume(step) .. commands.get_data(), skip_osd) 92 | end 93 | 94 | function volume_service.toggle_mute(skip_osd) 95 | update(volume_service.config.app .. commands.toggle_mute() .. commands.get_data(), skip_osd) 96 | end 97 | 98 | function volume_service.watch() 99 | volume_service.timer = volume_service.timer or gtimer { 100 | timeout = volume_service.config.interval, 101 | call_now = true, 102 | callback = function() 103 | update(volume_service.config.app .. commands.get_data(), true) 104 | end, 105 | } 106 | volume_service.timer:again() 107 | end 108 | 109 | return volume_service 110 | -------------------------------------------------------------------------------- /services/wallpaper.lua: -------------------------------------------------------------------------------- 1 | -- DEPENDENCIES (feature flag "wallpaper_menu"): feh, cp, ls 2 | 3 | local config = require("rice.config") 4 | if not config.features.wallpaper_menu then 5 | return 6 | end 7 | 8 | local popen = io.popen 9 | local match = string.match 10 | local format = string.format 11 | local insert = table.insert 12 | local awful = require("awful") 13 | local gfilesystem = require("gears.filesystem") 14 | local core = require("core") 15 | local places = require("rice.places") 16 | 17 | 18 | local feh_prefix = ".fehbg" 19 | local feh_script = core.path.home .. "/" .. feh_prefix 20 | 21 | local wallpaper_service = {} 22 | 23 | function wallpaper_service.get_collections() 24 | local collections = {} 25 | 26 | local directory = places.wallpapers 27 | local file = popen(format('ls -a "%s"', directory)) 28 | if file then 29 | for filename in file:lines() do 30 | local name = match(filename, "^" .. feh_prefix .. ".(.+)") 31 | if name then 32 | insert(collections, { 33 | name = name, 34 | path = directory .. "/" .. filename, 35 | }) 36 | end 37 | end 38 | file:close() 39 | end 40 | 41 | return collections 42 | end 43 | 44 | function wallpaper_service.set_collection(collection) 45 | awful.spawn(format('cp "%s" "%s"', collection.path, feh_script)) 46 | wallpaper_service.restore() 47 | end 48 | 49 | function wallpaper_service.restore() 50 | if gfilesystem.file_executable(feh_script) then 51 | awful.spawn(feh_script) 52 | end 53 | end 54 | 55 | return wallpaper_service 56 | -------------------------------------------------------------------------------- /services/weather.lua: -------------------------------------------------------------------------------- 1 | -- DEPENDENCIES (feature flag "weather_widget"): curl, lua-dkjson 2 | 3 | local config = require("rice.config") 4 | if not config.features.weather_widget then 5 | return 6 | end 7 | 8 | local capi = Capi 9 | local format = string.format 10 | local gears = require("gears") 11 | local awful = require("awful") 12 | local json = require("dkjson") 13 | 14 | 15 | local weather_service = { 16 | config = { 17 | interval = 10 * 60, 18 | }, 19 | last_response = { 20 | time = 0, 21 | success = nil, 22 | data = { 23 | station = "", 24 | time = 0, 25 | humidity = 0, 26 | pressure = 0, 27 | dew_point = 0, 28 | temperature = 0, 29 | wind_speed = 0, 30 | wind_gust = 0, 31 | wind_chill = 0, 32 | wind_direction = 0, 33 | apparent_temperature = 0, 34 | uv = 0, 35 | solar_radiation = 0, 36 | precipitation_rate = 0, 37 | precipitation_day = 0, 38 | precipitation_week = 0, 39 | precipitation_month = 0, 40 | indoor_humidity = 0, 41 | indoor_temperature = 0, 42 | indoor_apparent_temperature = 0, 43 | }, 44 | }, 45 | timer = nil, 46 | } 47 | 48 | local function update_response(data) 49 | local response = weather_service.last_response 50 | response.time = os.time() 51 | if data then 52 | response.success = true 53 | gears.table.crush(response.data, data) 54 | else 55 | response.success = false 56 | end 57 | capi.awesome.emit_signal("weather::updated", response) 58 | end 59 | 60 | local function validate_data(data) 61 | if type(data) == "table" then 62 | if not data.error then 63 | return nil 64 | else 65 | return tostring(data.error) 66 | end 67 | else 68 | return "Bad data" 69 | end 70 | end 71 | 72 | local function on_raw_data(stdout, stderr, exitreason, exitcode) 73 | local data, error 74 | 75 | if exitreason == "exit" and exitcode == 0 then 76 | data, _, error = json.decode(stdout) 77 | else 78 | error = "Weather fetch error: " .. tostring(exitcode) .. ", " .. exitreason .. " => " .. stderr 79 | end 80 | 81 | error = error or validate_data(data) 82 | 83 | if error then 84 | gears.debug.print_error(error) 85 | end 86 | 87 | update_response(data) 88 | end 89 | 90 | function weather_service.update() 91 | -- TODO: Needs rework 92 | local data_url = weather_service.config.data_url 93 | if data_url then 94 | local command = format([[curl --silent --fail "%s"]], data_url) 95 | awful.spawn.easy_async(command, on_raw_data) 96 | end 97 | end 98 | 99 | function weather_service.watch() 100 | weather_service.timer = weather_service.timer or gears.timer { 101 | timeout = weather_service.config.interval, 102 | call_now = true, 103 | callback = weather_service.update, 104 | } 105 | weather_service.timer:again() 106 | end 107 | 108 | return weather_service 109 | -------------------------------------------------------------------------------- /theme/icons/_blank.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/apple-keyboard-command.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/application-variable-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/application-variable.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/apps.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/arrange-bring-forward.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/arrow-down-right-bold.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/arrow-u-left-top.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/awesomewm.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | -------------------------------------------------------------------------------- /theme/icons/bluetooth-settings.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/book-clock-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/book-clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/calculator.svg: -------------------------------------------------------------------------------- 1 | calculator -------------------------------------------------------------------------------- /theme/icons/calendar-month.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/chart-line.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/check-bold.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/check.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/checkbox-blank-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/checkbox-blank.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/checkbox-marked.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/chevron-double-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/chevron-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/chevron-triple-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/chevron-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/circle-medium.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/circle-opacity.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-fast.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-10.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-11.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-12.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-4.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-5.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-6.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-7.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-8.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/clock-time-9.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/cogs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/console-line.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/content-copy.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/delete-clock-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/delete-clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/delete-forever.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/dock-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/dock-top.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/emoticon-cool.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/exit-run.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/eye-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/file-document-edit.svg: -------------------------------------------------------------------------------- 1 | file-document-edit -------------------------------------------------------------------------------- /theme/icons/firefox.svg: -------------------------------------------------------------------------------- 1 | firefox -------------------------------------------------------------------------------- /theme/icons/folder-image.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/folder.svg: -------------------------------------------------------------------------------- 1 | folder -------------------------------------------------------------------------------- /theme/icons/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/help.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/home-thermometer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/identifier.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/image-filter-center-focus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/image-size-select-actual.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/keyboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/lan-disconnect.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/lan-pending.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/layers-triple.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/layouts/floating.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /theme/icons/layouts/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /theme/icons/layouts/max.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /theme/icons/layouts/tiling.center.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /theme/icons/layouts/tiling.right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /theme/icons/lightbulb-on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/logout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/menu-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/minus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/monitor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/move-resize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/music.svg: -------------------------------------------------------------------------------- 1 | music -------------------------------------------------------------------------------- /theme/icons/open-in-app.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/open-in-new.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/pin-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/pin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/pirate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/power-settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/power.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/radiobox-blank.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/radiobox-marked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/rename.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/restart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/shuffle-variant.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/skip-next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/skip-previous.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/sleep.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/speedometer-slow.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/speedometer.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/stop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/tag-arrow-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/tag-multiple.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/tag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/target.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/theme-light-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/thermometer-lines.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/thermometer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/timer-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/timer-play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/timer-sand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/timer-settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/toggle-switch-off-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/toggle-switch-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/toggle-switch-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/toggle-switch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/toolbox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/tortoise.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/tune.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /theme/icons/unfold-less-vertical.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/video.svg: -------------------------------------------------------------------------------- 1 | video -------------------------------------------------------------------------------- /theme/icons/view-grid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/volume.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /theme/icons/water-thermometer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/weather-pouring.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/weather-rainy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/web.svg: -------------------------------------------------------------------------------- 1 | web -------------------------------------------------------------------------------- /theme/icons/window-close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/window-maximize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme/icons/window-minimize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/client.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | 3 | 4 | require("awful.autofocus") 5 | 6 | capi.client.connect_signal("mouse::enter", function(client) 7 | client:activate { context = "mouse_enter", raise = false } 8 | end) 9 | -------------------------------------------------------------------------------- /ui/controller.lua: -------------------------------------------------------------------------------- 1 | ---@class Controller 2 | ---@field current { instance?: wibox } 3 | local M = { current = setmetatable({}, { __mode = "v" }) } 4 | 5 | ---@param wibox wibox 6 | function M.leave(wibox) 7 | local c = M.current 8 | if c.instance == wibox then 9 | c.instance = nil 10 | end 11 | end 12 | 13 | ---@param wibox wibox 14 | ---@return boolean 15 | function M.enter(wibox) 16 | local c = M.current 17 | if c.instance then 18 | return c.instance == wibox 19 | else 20 | c.instance = wibox 21 | return true 22 | end 23 | end 24 | 25 | return M 26 | -------------------------------------------------------------------------------- /ui/init.lua: -------------------------------------------------------------------------------- 1 | require("ui.notification") 2 | require("ui.client") 3 | require("ui.mouse") 4 | require("ui.osd") 5 | require("ui.menu") 6 | require("ui.topbar") 7 | require("ui.titlebar") 8 | require("ui.wallpaper") 9 | -------------------------------------------------------------------------------- /ui/menu/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | main = require("ui.menu.main"), 3 | client_switcher = require("ui.menu.client_switcher"), 4 | layout_switcher = require("ui.menu.layout_switcher"), 5 | } 6 | -------------------------------------------------------------------------------- /ui/menu/main.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local table = table 3 | local awful = require("awful") 4 | local beautiful = require("theme.theme") 5 | local config = require("rice.config") 6 | local gtimer = require("gears.timer") 7 | local mebox = require("widget.mebox") 8 | local bindbox = require("widget.bindbox") 9 | local menu_templates = require("ui.menu.templates") 10 | local app_menu = require("rice.apps").menu 11 | local dpi = Dpi 12 | 13 | 14 | return mebox { 15 | item_width = dpi(192), 16 | items_source = function() 17 | local items = {} 18 | 19 | local function add_separator() 20 | if #items > 0 and items[#items] ~= mebox.separator then 21 | table.insert(items, mebox.separator) 22 | end 23 | end 24 | 25 | if app_menu.favorites then 26 | for _, favorite in ipairs(menu_templates.applications.get_favorites_items()) do 27 | table.insert(items, favorite) 28 | end 29 | end 30 | 31 | if app_menu.categories then 32 | add_separator() 33 | table.insert(items, { 34 | text = "Applications", 35 | icon = beautiful.icon("apps.svg"), 36 | icon_color = beautiful.palette.orange, 37 | cache_submenu = false, 38 | submenu = menu_templates.applications.get_categories_menu, 39 | }) 40 | end 41 | 42 | if config.features.wallpaper_menu then 43 | add_separator() 44 | table.insert(items, { 45 | text = "Wallpaper", 46 | icon = beautiful.icon("image-size-select-actual.svg"), 47 | icon_color = beautiful.palette.green, 48 | submenu = menu_templates.wallpaper.shared, 49 | }) 50 | end 51 | 52 | add_separator() 53 | table.insert(items, { 54 | text = "Shortcuts", 55 | icon = beautiful.icon("apple-keyboard-command.svg"), 56 | icon_color = beautiful.palette.blue, 57 | callback = function() 58 | gtimer.delayed_call(function() 59 | bindbox.main:show() 60 | end) 61 | end, 62 | }) 63 | add_separator() 64 | table.insert(items, { 65 | text = "Exit", 66 | icon = beautiful.icon("power.svg"), 67 | icon_color = beautiful.palette.red, 68 | submenu = menu_templates.power.shared, 69 | }) 70 | 71 | return items 72 | end, 73 | } 74 | -------------------------------------------------------------------------------- /ui/menu/templates/client/_common.lua: -------------------------------------------------------------------------------- 1 | local beautiful = require("theme.theme") 2 | 3 | 4 | local M = {} 5 | 6 | ---@param menu Mebox 7 | function M.on_hide(menu) 8 | menu.client = nil 9 | end 10 | 11 | ---@param menu Mebox 12 | ---@param args Mebox.show.args 13 | function M.on_show(menu, args) 14 | local parent = menu._private.parent 15 | menu.client = parent and parent.client or args.client 16 | 17 | if not menu.client or not menu.client.valid then 18 | M.on_hide(menu) 19 | return false 20 | end 21 | 22 | local client = menu.client --[[@as client]] 23 | 24 | local function unmanage() 25 | client:disconnect_signal("request::unmanage", unmanage) 26 | menu:hide() 27 | end 28 | 29 | client:connect_signal("request::unmanage", unmanage) 30 | end 31 | 32 | ---comment 33 | ---@param text string 34 | ---@param property string 35 | ---@param checkbox_type? "checkmark"|"checkbox"|"radiobox"|"switch" 36 | ---@param icon? path 37 | ---@param icon_color? color 38 | ---@return table 39 | function M.build_simple_toggle(text, property, checkbox_type, icon, icon_color) 40 | return { 41 | text = text, 42 | checkbox_type = checkbox_type, 43 | icon = icon, 44 | icon_color = icon_color, 45 | on_show = function(item, menu) 46 | local client = menu.client --[[@as client]] 47 | item.checked = not not client[property] 48 | end, 49 | callback = function(item, menu) 50 | local client = menu.client --[[@as client]] 51 | client[property] = not item.checked 52 | end, 53 | } 54 | end 55 | 56 | return M 57 | -------------------------------------------------------------------------------- /ui/menu/templates/client/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | main = require("ui.menu.templates.client.main"), 3 | opacity = require("ui.menu.templates.client.opacity"), 4 | signals = require("ui.menu.templates.client.signals"), 5 | tags = require("ui.menu.templates.client.tags"), 6 | screens = require("ui.menu.templates.client.screens"), 7 | } 8 | -------------------------------------------------------------------------------- /ui/menu/templates/client/opacity.lua: -------------------------------------------------------------------------------- 1 | local tostring = tostring 2 | local wibox = require("wibox") 3 | local beautiful = require("theme.theme") 4 | local dpi = Dpi 5 | local mebox = require("widget.mebox") 6 | local binding = require("core.binding") 7 | local mod = binding.modifier 8 | local btn = binding.button 9 | local pango = require("utils.pango") 10 | local umath = require("utils.math") 11 | local config = require("rice.config") 12 | local common = require("ui.menu.templates.client._common") 13 | 14 | 15 | local M = {} 16 | 17 | ---@return Mebox.new.args 18 | function M.new() 19 | local value_widget 20 | local step = 0.05 21 | local min_opacity = 0 22 | local max_opacity = 1 23 | 24 | local function update_opacity_text(opacity) 25 | if not value_widget then 26 | return 27 | end 28 | opacity = tonumber(opacity) 29 | local text = opacity 30 | and tostring(umath.round(opacity * 100)) 31 | or "--" 32 | value_widget:set_markup(text .. pango.thin_space .. "%") 33 | end 34 | 35 | local function change_opacity(menu, value) 36 | local client = menu.client --[[@as client]] 37 | client.opacity = umath.clamp(client.opacity + value, min_opacity, max_opacity) 38 | update_opacity_text(menu.client.opacity) 39 | end 40 | 41 | local function set_opacity(menu, value) 42 | local client = menu.client --[[@as client]] 43 | client.opacity = value or max_opacity 44 | update_opacity_text(client.opacity) 45 | end 46 | 47 | ---@type Mebox.new.args 48 | local args = { 49 | item_width = beautiful.mebox.default_style.item_height, 50 | on_show = common.on_show, 51 | on_hide = common.on_hide, 52 | orientation = "horizontal", 53 | layout_navigator = function(menu, x, y, direction, context) 54 | if y ~= 0 then 55 | change_opacity(menu, -y * step) 56 | return 57 | end 58 | mebox.layout_navigators.direction(menu, x, y, direction, context) 59 | end, 60 | items_source = { 61 | { 62 | icon = beautiful.icon("minus.svg"), 63 | icon_color = beautiful.palette.white, 64 | callback = function(item, menu) 65 | change_opacity(menu, -step) 66 | return false 67 | end, 68 | }, 69 | { 70 | enabled = false, 71 | buttons_builder = function(_, menu) 72 | return binding.awful_buttons { 73 | binding.awful({}, binding.group.mouse_wheel, function(trigger) 74 | change_opacity(menu, trigger.y * step) 75 | end), 76 | } 77 | end, 78 | template = { 79 | widget = wibox.widget.textbox, 80 | forced_width = dpi(64), 81 | halign = "center", 82 | update_callback = function(_, _, menu) 83 | local client = menu.client --[[@as client]] 84 | update_opacity_text(client.opacity) 85 | end, 86 | }, 87 | on_ready = function(item_widget) 88 | value_widget = item_widget 89 | end, 90 | }, 91 | { 92 | icon = beautiful.icon("plus.svg"), 93 | icon_color = beautiful.palette.white, 94 | callback = function(item, menu) 95 | change_opacity(menu, step) 96 | return false 97 | end, 98 | }, 99 | mebox.separator, 100 | { 101 | width = dpi(100), 102 | text = "Reset", 103 | icon = beautiful.icon("arrow-u-left-top.svg"), 104 | icon_color = beautiful.palette.gray, 105 | callback = function(item, menu) 106 | set_opacity(menu, max_opacity) 107 | end, 108 | }, 109 | }, 110 | } 111 | 112 | return args 113 | end 114 | 115 | M.shared = M.new() 116 | 117 | return M 118 | -------------------------------------------------------------------------------- /ui/menu/templates/client/screens.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local ipairs = ipairs 3 | local beautiful = require("theme.theme") 4 | local dpi = Dpi 5 | local mebox = require("widget.mebox") 6 | local gtable = require("gears.table") 7 | local config = require("rice.config") 8 | local common = require("ui.menu.templates.client._common") 9 | 10 | 11 | local M = {} 12 | 13 | ---@return Mebox.new.args 14 | function M.new() 15 | ---@type Mebox.new.args 16 | local args = { 17 | item_width = dpi(100), 18 | on_show = common.on_show, 19 | on_hide = common.on_hide, 20 | items_source = function(menu) 21 | local client = menu.client --[[@as client]] 22 | 23 | ---@type MeboxItem.args[] 24 | local items = {} 25 | 26 | for screen in capi.screen do 27 | items[#items + 1] = { 28 | text = screen.index, 29 | icon = beautiful.icon("monitor.svg"), 30 | icon_color = beautiful.palette.white, 31 | checkbox_type = "radiobox", 32 | on_show = function(item, menu, args, context) 33 | if client.screen == screen then 34 | item.checked = true 35 | if context.source == "keyboard" then 36 | args.selected_index = item.index 37 | end 38 | else 39 | item.checked = false 40 | end 41 | end, 42 | callback = function() 43 | client:move_to_screen(screen) 44 | end, 45 | } 46 | end 47 | 48 | return items 49 | end, 50 | } 51 | 52 | return args 53 | end 54 | 55 | M.shared = M.new() 56 | 57 | return M 58 | -------------------------------------------------------------------------------- /ui/menu/templates/init.lua: -------------------------------------------------------------------------------- 1 | local config = require("rice.config") 2 | 3 | return { 4 | applications = require("ui.menu.templates.applications"), 5 | client = require("ui.menu.templates.client"), 6 | tag = require("ui.menu.templates.tag"), 7 | power = require("ui.menu.templates.power"), 8 | media_player = require("ui.menu.templates.media_player"), 9 | wallpaper = config.features.wallpaper_menu and require("ui.menu.templates.wallpaper") or nil, 10 | } 11 | -------------------------------------------------------------------------------- /ui/menu/templates/tag/_common.lua: -------------------------------------------------------------------------------- 1 | local beautiful = require("theme.theme") 2 | 3 | 4 | local M = {} 5 | 6 | ---@param menu Mebox 7 | function M.on_hide(menu) 8 | menu.tag = nil 9 | menu.taglist = nil 10 | end 11 | 12 | ---@param menu Mebox 13 | ---@param args Mebox.show.args 14 | function M.on_show(menu, args) 15 | local parent = menu._private.parent 16 | menu.tag = parent and parent.tag or args.tag 17 | menu.taglist = parent and parent.taglist or args.taglist 18 | 19 | if not menu.tag or not menu.tag.activated then 20 | M.on_hide(menu) 21 | return false 22 | end 23 | end 24 | 25 | ---comment 26 | ---@param text string 27 | ---@param property string 28 | ---@param checkbox_type? "checkmark"|"checkbox"|"radiobox"|"switch" 29 | ---@param icon? path 30 | ---@param icon_color? color 31 | ---@return table 32 | function M.build_simple_toggle(text, property, checkbox_type, icon, icon_color) 33 | return { 34 | text = text, 35 | checkbox_type = checkbox_type, 36 | icon = icon, 37 | icon_color = icon_color, 38 | on_show = function(item, menu) 39 | local tag = menu.tag --[[@as tag]] 40 | item.checked = not not tag[property] 41 | end, 42 | callback = function(item, menu) 43 | local tag = menu.tag --[[@as tag]] 44 | tag[property] = not item.checked 45 | end, 46 | } 47 | end 48 | 49 | return M 50 | -------------------------------------------------------------------------------- /ui/menu/templates/tag/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | main = require("ui.menu.templates.tag.main"), 3 | layout = require("ui.menu.templates.tag.layout"), 4 | } 5 | -------------------------------------------------------------------------------- /ui/menu/templates/tag/layout.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local wibox = require("wibox") 3 | local beautiful = require("theme.theme") 4 | local dpi = Dpi 5 | local capsule = require("widget.capsule") 6 | local pango = require("utils.pango") 7 | local hui = require("utils.thickness") 8 | local css = require("utils.css") 9 | local common = require("ui.menu.templates.tag._common") 10 | local mebox = require("widget.mebox") 11 | 12 | 13 | local M = {} 14 | 15 | ---@return Mebox.new.args 16 | function M.new() 17 | ---@type Mebox.new.args 18 | local args = { 19 | item_width = dpi(224), 20 | item_height = dpi(48), 21 | on_show = common.on_show, 22 | on_hide = common.on_hide, 23 | items_source = function(menu) 24 | local tag = menu.tag --[[@as tag]] 25 | if not tag then 26 | return { mebox.info("No tag selected") } 27 | end 28 | local screen = tag.screen 29 | if not screen then 30 | return { mebox.info("Unknown screen") } 31 | end 32 | local layouts = tag.layouts 33 | local count = layouts and #layouts or awful.layout.layouts 34 | if count == 0 then 35 | return { mebox.info("No layout available") } 36 | end 37 | 38 | ---@type MeboxItem.args[] 39 | local items = {} 40 | for i = 1, count do 41 | local layout = layouts[i] 42 | 43 | local name = layout.name or "" 44 | local checked = tag.layout == layout 45 | local style = beautiful.layouts[name] or {} 46 | 47 | items[i] = { 48 | text = style.text or name, 49 | icon = style.icon, 50 | checked = checked, 51 | callback = function() tag.layout = layout end, 52 | } 53 | end 54 | return items 55 | end, 56 | item_template = { 57 | id = "#container", 58 | widget = capsule, 59 | margins = hui.new { dpi(2), 0 }, 60 | paddings = hui.new { dpi(8), dpi(12) }, 61 | { 62 | layout = wibox.layout.align.horizontal, 63 | expand = "inside", 64 | nil, 65 | { 66 | layout = wibox.layout.fixed.horizontal, 67 | spacing = dpi(12), 68 | { 69 | id = "#icon", 70 | widget = wibox.widget.imagebox, 71 | resize = true, 72 | }, 73 | { 74 | id = "#text", 75 | widget = wibox.widget.textbox, 76 | }, 77 | }, 78 | { 79 | widget = wibox.container.margin, 80 | margins = hui.new { dpi(4), right = 0 }, 81 | { 82 | id = "#right_icon", 83 | widget = wibox.widget.imagebox, 84 | resize = true, 85 | }, 86 | }, 87 | }, 88 | update_callback = function(self, item, menu) 89 | self.forced_width = item.width or menu.item_width 90 | self.forced_height = item.height or menu.item_height 91 | 92 | local styles = item.selected 93 | and beautiful.mebox.item_styles.selected 94 | or beautiful.mebox.item_styles.normal 95 | local style = item.urgent 96 | and styles.urgent 97 | or styles.normal 98 | self:apply_style(style) 99 | 100 | local icon_widget = self:get_children_by_id("#icon")[1] 101 | if icon_widget then 102 | local color = style.fg 103 | local stylesheet = beautiful.build_layout_stylesheet(color) 104 | icon_widget:set_stylesheet(stylesheet) 105 | icon_widget:set_image(item.icon) 106 | end 107 | 108 | local text_widget = self:get_children_by_id("#text")[1] 109 | if text_widget then 110 | local text = item.text or "" 111 | text_widget:set_markup(pango.span { fgcolor = style.fg, pango.escape(text) }) 112 | end 113 | 114 | local right_icon_widget = self:get_children_by_id("#right_icon")[1] 115 | if right_icon_widget then 116 | local checkbox_type = item.checkbox_type or "radiobox" 117 | local checkbox_style = beautiful.mebox[checkbox_type][not not item.checked] 118 | local icon = checkbox_style.icon 119 | local color = checkbox_style.color 120 | 121 | if item.selected then 122 | color = style.fg 123 | end 124 | 125 | right_icon_widget:set_stylesheet(css.style { path = { fill = color } }) 126 | right_icon_widget:set_image(icon) 127 | end 128 | end, 129 | }, 130 | } 131 | 132 | return args 133 | end 134 | 135 | M.shared = M.new() 136 | 137 | return M 138 | -------------------------------------------------------------------------------- /ui/menu/templates/tag/main.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local insert = table.insert 3 | local beautiful = require("theme.theme") 4 | local naughty = require("naughty") 5 | local dpi = Dpi 6 | local mebox = require("widget.mebox") 7 | local screen_helper = require("core.screen") 8 | local config = require("rice.config") 9 | local common = require("ui.menu.templates.tag._common") 10 | local layout_menu_template = require("ui.menu.templates.tag.layout") 11 | 12 | 13 | local M = {} 14 | 15 | ---@return Mebox.new.args 16 | function M.new() 17 | ---@type Mebox.new.args 18 | local args = { 19 | item_width = dpi(212), 20 | on_show = common.on_show, 21 | on_hide = common.on_hide, 22 | items_source = function(menu) 23 | local tag = menu.tag 24 | local taglist = menu.taglist 25 | 26 | local items = {} 27 | 28 | insert(items, mebox.header("Client")) 29 | insert(items, { 30 | text = "Move Here", 31 | icon = beautiful.icon("arrow-down-right-bold.svg"), 32 | icon_color = beautiful.palette.gray, 33 | callback = function() 34 | local client = capi.client.focus 35 | if client then 36 | client:move_to_tag(tag) 37 | end 38 | end, 39 | }) 40 | insert(items, { 41 | text = "Move All Here", 42 | icon = beautiful.icon("arrow-down-right-bold.svg"), 43 | icon_color = beautiful.palette.gray, 44 | callback = function() screen_helper.clients_to_tag(tag.screen, tag) end, 45 | }) 46 | insert(items, mebox.separator) 47 | 48 | insert(items, mebox.header("Tag")) 49 | if taglist then 50 | insert(items, { 51 | text = "Rename", 52 | icon = beautiful.icon("rename.svg"), 53 | icon_color = beautiful.palette.green, 54 | callback = function() taglist:rename_tag_inline(tag) end, 55 | }) 56 | end 57 | insert(items, { 58 | text = "Layout", 59 | icon = beautiful.icon("view-grid.svg"), 60 | icon_color = beautiful.palette.blue, 61 | submenu = layout_menu_template.shared, 62 | }) 63 | insert(items, mebox.separator) 64 | 65 | insert(items, common.build_simple_toggle("Volatile", "volatile", nil, beautiful.icon("delete-clock.svg"), beautiful.palette.gray)) 66 | insert(items, mebox.separator) 67 | 68 | insert(items, { 69 | text = "Delete", 70 | icon = beautiful.icon("delete-forever.svg"), 71 | icon_color = beautiful.palette.red, 72 | callback = function() 73 | if not tag:delete() then 74 | naughty.notification { 75 | urgency = "low", 76 | title = "Awesome", 77 | text = "The tag could not deleted. Only empty tags can be deleted.", 78 | } 79 | end 80 | end, 81 | }) 82 | 83 | return items 84 | end, 85 | } 86 | 87 | return args 88 | end 89 | 90 | M.shared = M.new() 91 | 92 | return M 93 | -------------------------------------------------------------------------------- /ui/menu/templates/wallpaper.lua: -------------------------------------------------------------------------------- 1 | local config = require("rice.config") 2 | if not config.features.wallpaper_menu then 3 | return 4 | end 5 | 6 | local insert = table.insert 7 | local awful = require("awful") 8 | local beautiful = require("theme.theme") 9 | local mebox = require("widget.mebox") 10 | local dpi = Dpi 11 | local wallpaper_service = require("services.wallpaper") 12 | local places = require("rice.places") 13 | 14 | 15 | local M = {} 16 | 17 | ---@return Mebox.new.args 18 | function M.new() 19 | ---@type Mebox.new.args 20 | local args = { 21 | items_source = function() 22 | ---@type MeboxItem.args[] 23 | local items = { 24 | { 25 | flex = true, 26 | text = "Restore", 27 | icon = beautiful.icon("shuffle-variant.svg"), 28 | icon_color = beautiful.palette.gray, 29 | callback = function() 30 | wallpaper_service.restore() 31 | return false 32 | end, 33 | }, 34 | mebox.separator, 35 | } 36 | 37 | local contains_any_collection 38 | for _, collection in ipairs(wallpaper_service.get_collections()) do 39 | contains_any_collection = true 40 | insert(items, { 41 | flex = true, 42 | text = collection.name, 43 | callback = function() 44 | wallpaper_service.set_collection(collection) 45 | return false 46 | end, 47 | }) 48 | end 49 | if contains_any_collection then 50 | insert(items, mebox.separator) 51 | end 52 | 53 | insert(items, { 54 | flex = true, 55 | text = "Open Directory", 56 | icon = beautiful.icon("folder-image.svg"), 57 | icon_color = beautiful.palette.blue, 58 | callback = function() 59 | awful.spawn(config.commands.open(places.wallpapers)) 60 | end, 61 | }) 62 | 63 | return items 64 | end, 65 | } 66 | 67 | return args 68 | end 69 | 70 | M.shared = M.new() 71 | 72 | return M 73 | -------------------------------------------------------------------------------- /ui/mouse/init.lua: -------------------------------------------------------------------------------- 1 | local amouse = require("awful.mouse") 2 | local amresize = require("awful.mouse.resize") 3 | 4 | amouse.drag_to_tag.enabled = false 5 | 6 | local M = { 7 | edge_snap = require("ui.mouse.edge_snap"), 8 | snap = require("ui.mouse.snap"), 9 | } 10 | 11 | amresize.add_move_callback(function(client, geo, args) 12 | M.edge_snap.detect(client) 13 | return M.snap.move(client, M.edge_snap.try_restore(client) or geo) 14 | end, "mouse.move") 15 | 16 | amresize.add_leave_callback(function(client, geo, args) 17 | return M.edge_snap.apply(client, args) 18 | end, "mouse.move") 19 | 20 | amresize.add_move_callback(function(client, geo, args) 21 | return M.snap.resize(client, geo, args.corner) 22 | end, "mouse.resize") 23 | 24 | return M 25 | -------------------------------------------------------------------------------- /ui/osd/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | volume = require("ui.osd.volume"), 3 | } 4 | -------------------------------------------------------------------------------- /ui/popup/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | calendar = require("ui.popup.calendar"), 3 | tools = require("ui.popup.tools"), 4 | } 5 | -------------------------------------------------------------------------------- /ui/popup/tools.lua: -------------------------------------------------------------------------------- 1 | local awful = require("awful") 2 | local wibox = require("wibox") 3 | local gtable = require("gears.table") 4 | local tcolor = require("utils.color") 5 | local binding = require("core.binding") 6 | local mod = binding.modifier 7 | local btn = binding.button 8 | local beautiful = require("theme.theme") 9 | local dpi = Dpi 10 | local capsule = require("widget.capsule") 11 | local noice = require("core.style") 12 | local config = require("rice.config") 13 | local redshift_widget = require("ui.topbar.redshift") 14 | local ui_controller = require("ui.controller") 15 | 16 | 17 | ---@class ToolsPopup.module 18 | ---@operator call: ToolsPopup 19 | local M = { mt = {} } 20 | 21 | function M.mt:__call(...) 22 | return M.new(...) 23 | end 24 | 25 | 26 | ---@class ToolsPopup : awful.popup, stylable 27 | ---@field package _private ToolsPopup.private 28 | ---Style properties: 29 | ---@field paddings thickness 30 | M.object = {} 31 | ---@class ToolsPopup.private 32 | 33 | noice.define_style(M.object, { 34 | bg = { proxy = true }, 35 | fg = { proxy = true }, 36 | border_color = { proxy = true }, 37 | border_width = { proxy = true }, 38 | shape = { proxy = true }, 39 | placement = { proxy = true }, 40 | paddings = { property = "paddings" }, 41 | }) 42 | 43 | function M.object:show() 44 | if self.visible or not ui_controller.enter(self) then 45 | return 46 | end 47 | 48 | self.visible = true 49 | end 50 | 51 | function M.object:hide() 52 | self.visible = false 53 | ui_controller.leave(self) 54 | end 55 | 56 | function M.object:toggle() 57 | if self.visible then 58 | self:hide() 59 | else 60 | self:show() 61 | end 62 | end 63 | 64 | 65 | ---@class ToolsPopup.new.args 66 | 67 | ---@param args? ToolsPopup.new.args 68 | ---@return ToolsPopup 69 | function M.new(args) 70 | args = args or {} 71 | 72 | local self = awful.popup { 73 | ontop = true, 74 | visible = false, 75 | widget = { 76 | widget = capsule, 77 | enable_overlay = false, 78 | bg = tcolor.transparent, 79 | { 80 | id = "#container", 81 | forced_width = dpi(250), 82 | layout = wibox.layout.fixed.vertical, 83 | spacing = beautiful.wibar.spacing, 84 | }, 85 | }, 86 | } --[[@as ToolsPopup]] 87 | 88 | gtable.crush(self, M.object, true) 89 | 90 | self:initialize_style(beautiful.tools_popup.default_style, self.widget) 91 | 92 | self:apply_style(args) 93 | 94 | local container = self.widget:get_children_by_id("#container")[1] --[[@as wibox.layout]] 95 | if config.features.redshift_widget then 96 | container:add(wibox.widget { 97 | widget = wibox.container.constraint, 98 | strategy = "max", 99 | height = beautiful.wibar.item_height, 100 | redshift_widget(self, true), 101 | }) 102 | end 103 | 104 | return self 105 | end 106 | 107 | return setmetatable(M, M.mt) 108 | -------------------------------------------------------------------------------- /ui/topbar/init.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local awful = require("awful") 3 | local beautiful = require("theme.theme") 4 | local wibox = require("wibox") 5 | local dpi = Dpi 6 | local config = require("rice.config") 7 | local binding = require("core.binding") 8 | local mod = binding.modifier 9 | local btn = binding.button 10 | local layoutbox_widget = require("ui.topbar.layoutbox") 11 | local clientlist_widget = require("ui.topbar.clientlist") 12 | local taglist_widget = require("ui.topbar.taglist") 13 | local systray_widget = require("ui.topbar.systray") 14 | local torrent_widget = require("ui.topbar.torrent") 15 | local network_widget = require("ui.topbar.network") 16 | local volume_widget = require("ui.topbar.volume") 17 | local weather_widget = require("ui.topbar.weather") 18 | local datetime_widget = require("ui.topbar.datetime") 19 | local tools_widget = require("ui.topbar.tools") 20 | local power_widget = require("ui.topbar.power") 21 | local media_player_widget = require("ui.topbar.media_player") 22 | 23 | 24 | capi.screen.connect_signal("request::desktop_decoration", function(screen) 25 | ---@cast screen screen 26 | 27 | local is_primary = screen == capi.screen.primary 28 | local wibar = awful.wibar { 29 | position = "top", 30 | screen = screen, 31 | widget = { 32 | layout = wibox.layout.align.horizontal, 33 | expand = "outside", 34 | { 35 | id = "#left", 36 | layout = wibox.layout.fixed.horizontal, 37 | spacing = beautiful.wibar.spacing, 38 | }, 39 | { 40 | layout = wibox.container.margin, 41 | left = beautiful.wibar.spacing * 2, 42 | right = beautiful.wibar.spacing * 2, 43 | { 44 | id = "#middle", 45 | layout = wibox.layout.fixed.horizontal, 46 | spacing = beautiful.wibar.spacing, 47 | }, 48 | }, 49 | { 50 | id = "#right", 51 | layout = wibox.layout.fixed.horizontal, 52 | spacing = beautiful.wibar.spacing, 53 | reverse = true, 54 | }, 55 | }, 56 | } 57 | 58 | ---@class screen 59 | ---@field topbar table 60 | screen.topbar = { 61 | wibox = wibar, 62 | clientlist = clientlist_widget.new(wibar), 63 | systray = is_primary and systray_widget.new(wibar), 64 | taglist = taglist_widget.new(wibar), 65 | } 66 | 67 | local left = wibar:get_children_by_id("#left")[1] 68 | left:add(layoutbox_widget.new(wibar)) 69 | left:add(screen.topbar.clientlist) 70 | if is_primary then 71 | left:add(media_player_widget.new(wibar)) 72 | end 73 | 74 | local middle = wibar:get_children_by_id("#middle")[1] 75 | middle:add(screen.topbar.taglist) 76 | 77 | local right = wibar:get_children_by_id("#right")[1] 78 | if is_primary then 79 | if config.features.torrent_widget then 80 | right:add(torrent_widget.new(wibar)) 81 | end 82 | right:add(network_widget.new(wibar)) 83 | right:add(volume_widget.new(wibar)) 84 | if config.features.weather_widget then 85 | right:add(weather_widget.new(wibar)) 86 | end 87 | right:add(tools_widget.new(wibar)) 88 | right:add(systray_widget.new(wibar)) 89 | right:add(datetime_widget.new(wibar)) 90 | right:add(power_widget.new(wibar)) 91 | else 92 | right:add(power_widget.new(wibar)) 93 | end 94 | end) 95 | -------------------------------------------------------------------------------- /ui/topbar/layoutbox.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local awful = require("awful") 3 | local wibox = require("wibox") 4 | local beautiful = require("theme.theme") 5 | local binding = require("core.binding") 6 | local mod = binding.modifier 7 | local btn = binding.button 8 | local dpi = Dpi 9 | local capsule = require("widget.capsule") 10 | local alayout = require("awful.layout") 11 | local gtable = require("gears.table") 12 | local aplacement = require("awful.placement") 13 | local widget_helper = require("core.widget") 14 | local mebox = require("widget.mebox") 15 | local tag_layout_menu_template = require("ui.menu.templates.tag.layout") 16 | local main_menu = require("ui.menu.main") 17 | local hui = require("utils.thickness") 18 | 19 | 20 | local layoutbox = { mt = {} } 21 | 22 | function layoutbox:update_from_tag(tag) 23 | if tag.screen == self._private.wibar.screen then 24 | self:update() 25 | end 26 | end 27 | 28 | function layoutbox:update() 29 | local layout = alayout.get(self._private.wibar.screen) 30 | local name = alayout.getname(layout) 31 | local style = beautiful.layouts[name] or {} 32 | self.widget.text.text = style.icon and "" or name 33 | self.widget.icon.image = style.icon 34 | end 35 | 36 | function layoutbox.new(wibar) 37 | local self = wibox.widget { 38 | widget = capsule, 39 | margins = hui.new { 40 | top = beautiful.wibar.paddings.top, 41 | right = beautiful.capsule.default_style.margins.right, 42 | bottom = beautiful.wibar.paddings.bottom, 43 | left = beautiful.wibar.paddings.left, 44 | }, 45 | paddings = hui.new { 46 | beautiful.capsule.default_style.paddings.top, 47 | dpi(10), 48 | beautiful.capsule.default_style.paddings.bottom, 49 | }, 50 | { 51 | layout = wibox.layout.stack, 52 | { 53 | id = "text", 54 | widget = wibox.widget.textbox, 55 | }, 56 | { 57 | id = "icon", 58 | widget = wibox.widget.imagebox, 59 | stylesheet = beautiful.build_layout_stylesheet(), 60 | }, 61 | }, 62 | } 63 | 64 | gtable.crush(self, layoutbox, true) 65 | 66 | self._private.wibar = wibar 67 | 68 | self._private.menu = mebox(tag_layout_menu_template.shared) 69 | 70 | self.buttons = binding.awful_buttons { 71 | binding.awful({}, btn.left, function() 72 | main_menu:toggle({ 73 | placement = beautiful.wibar.build_placement(self, self._private.wibar), 74 | }, { source = "mouse" }) 75 | end), 76 | binding.awful({}, btn.right, function() 77 | self._private.menu:toggle({ 78 | tag = self._private.wibar.screen.selected_tag, 79 | placement = beautiful.wibar.build_placement(self, self._private.wibar), 80 | }, { source = "mouse" }) 81 | end), 82 | binding.awful({}, btn.middle, nil, function() 83 | if self._private.menu.visible then 84 | return 85 | end 86 | local tag = self._private.wibar.screen.selected_tag 87 | if tag then 88 | tag.layout = tag.layouts[1] or tag.layout 89 | end 90 | end), 91 | binding.awful({}, { 92 | { trigger = btn.wheel_up, direction = -1 }, 93 | { trigger = btn.wheel_down, direction = 1 }, 94 | }, function(trigger) 95 | if self._private.menu.visible then 96 | return 97 | end 98 | alayout.inc(trigger.direction) 99 | end), 100 | } 101 | 102 | capi.tag.connect_signal("property::layout", function(tag) self:update_from_tag(tag) end) 103 | capi.tag.connect_signal("property::selected", function(tag) self:update_from_tag(tag) end) 104 | 105 | self:update() 106 | 107 | return self 108 | end 109 | 110 | function layoutbox.mt:__call(...) 111 | return layoutbox.new(...) 112 | end 113 | 114 | return setmetatable(layoutbox, layoutbox.mt) 115 | -------------------------------------------------------------------------------- /ui/topbar/power.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local tonumber = tonumber 3 | local maxinteger = math.maxinteger 4 | local awful = require("awful") 5 | local wibox = require("wibox") 6 | local beautiful = require("theme.theme") 7 | local config = require("rice.config") 8 | local binding = require("core.binding") 9 | local pango = require("utils.pango") 10 | local css = require("utils.css") 11 | local mod = binding.modifier 12 | local btn = binding.button 13 | local dpi = Dpi 14 | local capsule = require("widget.capsule") 15 | local gtable = require("gears.table") 16 | local mebox = require("widget.mebox") 17 | local power_menu_template = require("ui.menu.templates.power") 18 | local power_service = require("services.power") 19 | local power = require("rice.power") 20 | local humanizer = require("utils.humanizer") 21 | local hui = require("utils.thickness") 22 | 23 | 24 | local power_widget = { mt = {} } 25 | 26 | local time_args = { 27 | formats = { 28 | year = { text = "yr" }, 29 | month = { text = "mo" }, 30 | week = { text = "wk" }, 31 | day = { text = "d" }, 32 | hour = { text = "h" }, 33 | minute = { text = "min" }, 34 | second = { text = "s", format = "%2d" }, 35 | }, 36 | part_count = 2, 37 | prefix = pango.thin_space, 38 | unit_separator = pango.thin_space, 39 | } 40 | 41 | function power_widget:refresh(status) 42 | local style = status 43 | and (((tonumber(status) or maxinteger) <= power.timer.alert_threshold) 44 | and beautiful.capsule.styles.palette.red 45 | or beautiful.capsule.styles.palette.orange) 46 | or beautiful.capsule.styles.normal 47 | self:apply_style(style) 48 | 49 | local icon_stylesheet = css.style { path = { fill = style.fg } } 50 | local icon_widget = self:get_children_by_id("#icon")[1] 51 | icon_widget:set_stylesheet(icon_stylesheet) 52 | 53 | local text_widget = self:get_children_by_id("#text")[1] 54 | if not status then 55 | text_widget.visible = false 56 | else 57 | local text 58 | if status == true then 59 | text = "..." 60 | else 61 | text = humanizer.relative_time(status, time_args) 62 | end 63 | text_widget:set_markup(text) 64 | text_widget.visible = true 65 | end 66 | end 67 | 68 | function power_widget.new(wibar) 69 | local self = wibox.widget { 70 | widget = capsule, 71 | margins = hui.new { 72 | top = beautiful.wibar.paddings.top, 73 | right = beautiful.wibar.paddings.right, 74 | bottom = beautiful.wibar.paddings.bottom, 75 | left = beautiful.capsule.default_style.margins.left, 76 | }, 77 | paddings = hui.new { 78 | top = beautiful.capsule.default_style.paddings.top, 79 | right = dpi(10), 80 | bottom = beautiful.capsule.default_style.paddings.bottom, 81 | left = dpi(10), 82 | }, 83 | { 84 | layout = wibox.layout.fixed.horizontal, 85 | { 86 | id = "#icon", 87 | widget = wibox.widget.imagebox, 88 | image = beautiful.icon("power.svg"), 89 | }, 90 | { 91 | id = "#text", 92 | widget = wibox.widget.textbox, 93 | }, 94 | }, 95 | } 96 | 97 | gtable.crush(self, power_widget, true) 98 | 99 | self._private.wibar = wibar 100 | 101 | self._private.menu = mebox(power_menu_template.shared) 102 | 103 | self.buttons = binding.awful_buttons { 104 | binding.awful({}, btn.right, function() 105 | self._private.menu:toggle { 106 | placement = beautiful.wibar.build_placement(self, self._private.wibar), 107 | } 108 | end), 109 | binding.awful({}, btn.middle, function() 110 | power_service.stop_timer() 111 | end), 112 | } 113 | 114 | capi.awesome.connect_signal("power::timer", function(status) self:refresh(status) end) 115 | 116 | self:refresh(false) 117 | 118 | return self 119 | end 120 | 121 | function power_widget.mt:__call(...) 122 | return power_widget.new(...) 123 | end 124 | 125 | return setmetatable(power_widget, power_widget.mt) 126 | -------------------------------------------------------------------------------- /ui/topbar/systray.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local beautiful = require("theme.theme") 3 | local wibox = require("wibox") 4 | local dpi = Dpi 5 | local capsule = require("widget.capsule") 6 | local gtable = require("gears.table") 7 | local hui = require("utils.thickness") 8 | 9 | 10 | local systray = { mt = {} } 11 | 12 | function systray:refresh() 13 | local app_count = capi.awesome.systray() 14 | self:set_visible(app_count > 0) 15 | end 16 | 17 | function systray.new(wibar) 18 | local self = wibox.widget { 19 | widget = capsule, 20 | enable_overlay = false, 21 | margins = hui.new { 22 | top = beautiful.wibar.paddings.top, 23 | right = beautiful.capsule.default_style.margins.right, 24 | bottom = beautiful.wibar.paddings.bottom, 25 | left = beautiful.capsule.default_style.margins.left, 26 | }, 27 | paddings = hui.new { dpi(4), dpi(10) }, 28 | wibox.widget.systray(), 29 | } 30 | 31 | gtable.crush(self, systray, true) 32 | 33 | self._private.wibar = wibar 34 | 35 | capi.awesome.connect_signal("systray::update", function() self:refresh() end) 36 | 37 | self:refresh() 38 | 39 | return self 40 | end 41 | 42 | function systray.mt:__call(...) 43 | return systray.new(...) 44 | end 45 | 46 | return setmetatable(systray, systray.mt) 47 | -------------------------------------------------------------------------------- /ui/topbar/tools.lua: -------------------------------------------------------------------------------- 1 | local capi = Capi 2 | local setmetatable = setmetatable 3 | local awful = require("awful") 4 | local wibox = require("wibox") 5 | local beautiful = require("theme.theme") 6 | local config = require("rice.config") 7 | local binding = require("core.binding") 8 | local mod = binding.modifier 9 | local btn = binding.button 10 | local dpi = Dpi 11 | local capsule = require("widget.capsule") 12 | local gtable = require("gears.table") 13 | local mebox = require("widget.mebox") 14 | local tools_popup = require("ui.popup.tools") 15 | local css = require("utils.css") 16 | local hui = require("utils.thickness") 17 | 18 | 19 | local power_widget = { mt = {} } 20 | 21 | function power_widget:refresh() 22 | local style = self._private.popup.visible 23 | and beautiful.capsule.styles.selected 24 | or beautiful.capsule.styles.normal 25 | self:apply_style(style) 26 | 27 | local icon_stylesheet = css.style { path = { fill = style.fg } } 28 | local icon_widget = self:get_children_by_id("#icon")[1] 29 | icon_widget:set_stylesheet(icon_stylesheet) 30 | end 31 | 32 | function power_widget.new(wibar) 33 | local self = wibox.widget { 34 | widget = capsule, 35 | margins = { 36 | top = beautiful.wibar.paddings.top, 37 | bottom = beautiful.wibar.paddings.bottom, 38 | }, 39 | paddings = hui.new { 40 | beautiful.capsule.default_style.paddings.top, 41 | dpi(10), 42 | beautiful.capsule.default_style.paddings.bottom, 43 | }, 44 | { 45 | layout = wibox.layout.stack, 46 | { 47 | id = "#icon", 48 | widget = wibox.widget.imagebox, 49 | image = beautiful.icon("toolbox.svg"), 50 | }, 51 | }, 52 | } 53 | 54 | gtable.crush(self, power_widget, true) 55 | 56 | self._private.wibar = wibar 57 | 58 | local popup_placement = beautiful.wibar.build_placement(self, self._private.wibar) 59 | 60 | self._private.popup = tools_popup.new { 61 | placement = popup_placement, 62 | } 63 | 64 | self._private.popup:connect_signal("property::visible", function() self:refresh() end) 65 | 66 | self.buttons = binding.awful_buttons { 67 | binding.awful({}, btn.left, function() 68 | self._private.popup:toggle() 69 | end), 70 | } 71 | 72 | self:refresh() 73 | 74 | return self 75 | end 76 | 77 | function power_widget.mt:__call(...) 78 | return power_widget.new(...) 79 | end 80 | 81 | return setmetatable(power_widget, power_widget.mt) 82 | -------------------------------------------------------------------------------- /ui/wallpaper.lua: -------------------------------------------------------------------------------- 1 | if not DEBUG then 2 | return 3 | end 4 | 5 | local capi = Capi 6 | local awful = require("awful") 7 | local beautiful = require("theme.theme") 8 | local wibox = require("wibox") 9 | 10 | 11 | capi.screen.connect_signal("request::wallpaper", function(screen) 12 | awful.wallpaper { 13 | screen = screen, 14 | widget = { 15 | widget = wibox.container.background, 16 | bg = { 17 | type = "linear", 18 | from = { 0, 0 }, 19 | to = { 0, screen.geometry.height }, 20 | stops = { 21 | { 0, beautiful.palette.blue_25 }, 22 | { 1, beautiful.palette.yellow_25 }, 23 | }, 24 | }, 25 | }, 26 | } 27 | end) 28 | -------------------------------------------------------------------------------- /utils/color.lua: -------------------------------------------------------------------------------- 1 | local rawset = rawset 2 | local tonumber = tonumber 3 | local math = math 4 | local string = string 5 | local gcolor = require("gears.color") 6 | local gdebug = require("gears.debug") 7 | 8 | 9 | local M = { 10 | black = "#000000", 11 | white = "#FFFFFF", 12 | transparent = "#00000000", 13 | unknown = "#FFFF00", -- Something bright, easy to spot 14 | } 15 | 16 | function M.change(value, args) 17 | if not args then 18 | return value or M.black 19 | end 20 | 21 | local r, g, b, a = gcolor.parse_color(value) 22 | if r == nil then 23 | return M.black 24 | end 25 | 26 | if args.lighten then 27 | if args.lighten > 0 then 28 | r = r + ((1 - r) * args.lighten) 29 | g = g + ((1 - g) * args.lighten) 30 | b = b + ((1 - b) * args.lighten) 31 | elseif args.lighten < 0 then 32 | r = r + (r * args.lighten) 33 | g = g + (g * args.lighten) 34 | b = b + (b * args.lighten) 35 | end 36 | end 37 | 38 | if args.alpha then 39 | a = args.alpha 40 | end 41 | 42 | local function get_channel_value(channel) 43 | channel = channel * 255 44 | return math.floor(channel < 0 and 0 or (channel > 255 and 255 or channel)) 45 | end 46 | 47 | r = get_channel_value(r) 48 | g = get_channel_value(g) 49 | b = get_channel_value(b) 50 | a = get_channel_value(a) 51 | 52 | if a < 255 then 53 | return string.format("#%02x%02x%02x%02x", r, g, b, a) 54 | else 55 | return string.format("#%02x%02x%02x", r, g, b) 56 | end 57 | end 58 | 59 | M.palette_metatable = {} 60 | 61 | function M.palette_metatable.__index(t, k) 62 | local name, value = string.match(k, "^([_%a]+)_(%d+)$") 63 | if not name then 64 | gdebug.print_warning("Unknown color '" .. k .. "'") 65 | rawset(t, k, M.unknown) 66 | return M.unknown 67 | end 68 | value = (tonumber(value) - 100) / 100 69 | local source_color = t[name] 70 | local new_color = M.change(source_color, { lighten = value }) 71 | 72 | rawset(t, k, new_color) 73 | return new_color 74 | end 75 | 76 | -- TODO: Move this somewhere else 77 | function M.format_slop(color) 78 | local r, g, b, a = gcolor.parse_color(color) 79 | return string.format("%.3f,%.3f,%.3f,%.3f", r, g, b, a) 80 | end 81 | 82 | return M 83 | -------------------------------------------------------------------------------- /utils/css.lua: -------------------------------------------------------------------------------- 1 | local pairs = pairs 2 | local tostring = tostring 3 | local format = string.format 4 | 5 | 6 | local css = {} 7 | 8 | ---Convert lua table to css styles. 9 | ---@param rules table 10 | ---@return string 11 | --- 12 | ---**Example:** 13 | --- 14 | --- local style = css.style { 15 | --- path = { 16 | --- stroke = "#ff0000", 17 | --- ["stroke-width"] = 5, 18 | --- }, 19 | --- [".foobar::before"] = { 20 | --- content = "\"lorem ipsum\"", 21 | --- }, 22 | --- } 23 | --- -- path { stroke: #ff0000; stroke-width: 5; } .foobar::before { content: "lorem ipsum"; } 24 | --- 25 | function css.style(rules) 26 | local result = "" 27 | for selector, declarations in pairs(rules) do 28 | result = format("%s %s {", result, selector) 29 | for property, value in pairs(declarations) do 30 | result = format("%s %s: %s;", result, property, tostring(value)) 31 | end 32 | result = format("%s } ", result) 33 | end 34 | return result 35 | end 36 | 37 | return css 38 | -------------------------------------------------------------------------------- /utils/geometry.lua: -------------------------------------------------------------------------------- 1 | local uthickness = require("utils.thickness") 2 | 3 | local M = {} 4 | 5 | ---@param geometry geometry 6 | ---@param thickness? thickness_value 7 | ---@return geometry 8 | function M.inflate(geometry, thickness) 9 | thickness = uthickness.new(thickness) 10 | return thickness and { 11 | x = geometry.x - thickness.left, 12 | y = geometry.y - thickness.top, 13 | width = geometry.width + thickness.left + thickness.right, 14 | height = geometry.height + thickness.top + thickness.bottom, 15 | } or geometry 16 | end 17 | 18 | ---@param geometry geometry 19 | ---@param thickness? thickness_value 20 | ---@return geometry 21 | function M.shrink(geometry, thickness) 22 | return M.inflate(geometry, -uthickness.new(thickness)) 23 | end 24 | 25 | return M 26 | -------------------------------------------------------------------------------- /utils/math.lua: -------------------------------------------------------------------------------- 1 | local math = math 2 | 3 | 4 | local M = {} 5 | 6 | ---@param value number 7 | ---@param min number 8 | ---@param max number 9 | ---@return number 10 | function M.clamp(value, min, max) 11 | if value < min then return min end 12 | if value > max then return max end 13 | return value 14 | end 15 | 16 | ---@param value number 17 | ---@return number 18 | function M.round(value) 19 | return math.floor(value + 0.5) 20 | end 21 | 22 | ---@param value number 23 | ---@param min number 24 | ---@param max number 25 | ---@param new_min number 26 | ---@param new_max number 27 | ---@return number 28 | function M.translate(value, min, max, new_min, new_max) 29 | local range = max - min 30 | local new_range = new_max - new_min 31 | return new_min + (value - min) * (new_range / range) 32 | end 33 | 34 | return M 35 | -------------------------------------------------------------------------------- /utils/pango.lua: -------------------------------------------------------------------------------- 1 | local type = type 2 | local pairs = pairs 3 | local ipairs = ipairs 4 | local gstring = require("gears.string") 5 | 6 | 7 | local pango = {} 8 | 9 | pango.thin_space = [[ ]] 10 | 11 | ---Convert lua table to pango "span". 12 | ---@param data table|string 13 | ---@param separator? string # Value separator. 14 | ---@return string 15 | --- 16 | ---**Example 1:** 17 | --- 18 | --- local markup = pango.span { 19 | --- fgcolor = "#ff0000", 20 | --- size = "small", 21 | --- "content", 22 | --- } 23 | --- -- content 24 | --- 25 | ---**Example 2:** 26 | --- 27 | --- local markup = pango.span({ 28 | --- weight = "light", 29 | --- "a", 30 | --- tostring(23), 31 | --- pango.big("X"), 32 | --- }, " / ") 33 | --- -- a / 23 / X 34 | --- 35 | function pango.span(data, separator) 36 | if type(data) == "table" then 37 | separator = separator or "" 38 | 39 | local t = "" 40 | for _, v in ipairs(data) do 41 | t = t .. separator .. v 42 | end 43 | 44 | local s = "" .. t .. "" 51 | elseif type(data) == "string" then 52 | return data 53 | end 54 | return "" 55 | end 56 | 57 | ---@param data string 58 | ---@return string 59 | function pango.escape(data) 60 | return gstring.xml_escape(data) 61 | end 62 | 63 | ---@param data string 64 | ---@return string 65 | function pango.b(data) 66 | return "" .. data .. "" 67 | end 68 | 69 | ---@param data string 70 | ---@return string 71 | function pango.big(data) 72 | return "" .. data .. "" 73 | end 74 | 75 | ---@param data string 76 | ---@return string 77 | function pango.i(data) 78 | return "" .. data .. "" 79 | end 80 | 81 | ---@param data string 82 | ---@return string 83 | function pango.s(data) 84 | return "" .. data .. "" 85 | end 86 | 87 | ---@param data string 88 | ---@return string 89 | function pango.sub(data) 90 | return "" .. data .. "" 91 | end 92 | 93 | ---@param data string 94 | ---@return string 95 | function pango.sup(data) 96 | return "" .. data .. "" 97 | end 98 | 99 | ---@param data string 100 | ---@return string 101 | function pango.small(data) 102 | return "" .. data .. "" 103 | end 104 | 105 | ---@param data string 106 | ---@return string 107 | function pango.tt(data) 108 | return "" .. data .. "" 109 | end 110 | 111 | ---@param data string 112 | ---@return string 113 | function pango.u(data) 114 | return "" .. data .. "" 115 | end 116 | 117 | return pango 118 | -------------------------------------------------------------------------------- /utils/string.lua: -------------------------------------------------------------------------------- 1 | local string = string 2 | 3 | 4 | local M = {} 5 | 6 | function M.trim(s) 7 | return s and string.gsub(s, "^%s*(.-)%s*$", "%1") or nil 8 | end 9 | 10 | function M.trim_start(s) 11 | return s and string.gsub(s, "^%s*(.-)", "%1") or nil 12 | end 13 | 14 | function M.trim_end(s) 15 | return s and string.gsub(s, "(.-)%s*$", "%1") or nil 16 | end 17 | 18 | function M.escape_pattern(s) 19 | return s and string.gsub(s, "([%.%+%-%*%?%^%$%(%)%[%]])", "%%%1") or nil 20 | end 21 | 22 | return M 23 | -------------------------------------------------------------------------------- /utils/table.lua: -------------------------------------------------------------------------------- 1 | local pairs = pairs 2 | local gtable = require("gears.table") 3 | 4 | 5 | local M = {} 6 | 7 | function M.crush_clone(source, crush) 8 | if not source then 9 | return crush or {} 10 | end 11 | if not crush then 12 | return gtable.clone(source, true) 13 | end 14 | 15 | local clone = {} 16 | local source_keys = {} 17 | for k in pairs(source) do 18 | source_keys[k] = true 19 | end 20 | for k, v in pairs(crush) do 21 | source_keys[k] = nil 22 | clone[k] = v 23 | end 24 | for k in pairs(source_keys) do 25 | local value = source[k] 26 | if type(value) == "table" then 27 | clone[k] = gtable.clone(value, true) 28 | else 29 | clone[k] = value 30 | end 31 | end 32 | return clone 33 | end 34 | 35 | function M.values(t) 36 | local result = {} 37 | for _, v in pairs(t) do 38 | result[#result + 1] = v 39 | end 40 | return result 41 | end 42 | 43 | return M 44 | -------------------------------------------------------------------------------- /utils/thickness.lua: -------------------------------------------------------------------------------- 1 | local rawget = rawget 2 | local rawset = rawset 3 | local setmetatable = setmetatable 4 | local getmetatable = getmetatable 5 | local type = type 6 | local format = string.format 7 | 8 | 9 | ---@class Thickness 10 | ---@field zero thickness 11 | local M = {} 12 | 13 | ---@alias thickness_value number|number[]|thickness 14 | 15 | ---@class thickness : { top: number, right: number, bottom: number, left: number } 16 | ---@operator add(thickness_value): thickness 17 | ---@operator sub(thickness_value): thickness 18 | ---@operator mul(thickness_value): thickness 19 | ---@operator div(thickness_value): thickness 20 | ---@operator idiv(thickness_value): thickness 21 | ---@operator unm(): thickness 22 | 23 | local thickness_mt = { 24 | __index = function(self, key) 25 | if key == 1 then 26 | local value = rawget(self, "top") 27 | rawset(self, 1, value) 28 | return value 29 | elseif key == 2 then 30 | local value = rawget(self, "right") 31 | rawset(self, 2, value) 32 | return value 33 | elseif key == 3 then 34 | local value = rawget(self, "bottom") 35 | rawset(self, 3, value) 36 | return value 37 | elseif key == 4 then 38 | local value = rawget(self, "left") 39 | rawset(self, 4, value) 40 | return value 41 | end 42 | end, 43 | __newindex = function() 44 | error("thickness is readonly") 45 | end, 46 | __tostring = function(self) 47 | return format("thickness { top=%d, right=%d, bottom=%d, left=%d }", self.top, self.right, self.bottom, self.left) 48 | end, 49 | } 50 | 51 | thickness_mt.__add = function(self, other) 52 | other = M.new(other) 53 | return setmetatable({ 54 | top = self.top + other.top, 55 | right = self.right + other.right, 56 | bottom = self.bottom + other.bottom, 57 | left = self.left + other.left, 58 | }, thickness_mt) 59 | end 60 | thickness_mt.__sub = function(self, other) 61 | other = M.new(other) 62 | return setmetatable({ 63 | top = self.top - other.top, 64 | right = self.right - other.right, 65 | bottom = self.bottom - other.bottom, 66 | left = self.left - other.left, 67 | }, thickness_mt) 68 | end 69 | thickness_mt.__mul = function(self, other) 70 | other = M.new(other) 71 | return setmetatable({ 72 | top = self.top * other.top, 73 | right = self.right * other.right, 74 | bottom = self.bottom * other.bottom, 75 | left = self.left * other.left, 76 | }, thickness_mt) 77 | end 78 | thickness_mt.__div = function(self, other) 79 | other = M.new(other) 80 | return setmetatable({ 81 | top = self.top / other.top, 82 | right = self.right / other.right, 83 | bottom = self.bottom / other.bottom, 84 | left = self.left / other.left, 85 | }, thickness_mt) 86 | end 87 | thickness_mt.__idiv = function(self, other) 88 | other = M.new(other) 89 | return setmetatable({ 90 | top = self.top // other.top, 91 | right = self.right // other.right, 92 | bottom = self.bottom // other.bottom, 93 | left = self.left // other.left, 94 | }, thickness_mt) 95 | end 96 | thickness_mt.__unm = function(self) 97 | return setmetatable({ 98 | top = -self.top, 99 | right = -self.right, 100 | bottom = -self.bottom, 101 | left = -self.left, 102 | }, thickness_mt) 103 | end 104 | 105 | ---@param value? thickness_value 106 | ---@return thickness # Returns the same table instance (i.e. the `value` parameter). 107 | function M.new(value) 108 | if not value then 109 | return M.zero 110 | end 111 | 112 | if type(value) == "table" then 113 | if getmetatable(value) == thickness_mt then 114 | return value 115 | end 116 | 117 | local top, right, bottom, left 118 | 119 | local length = #value 120 | if length == 1 then 121 | local v = value[1] 122 | top = v 123 | right = v 124 | bottom = v 125 | left = v 126 | elseif length == 2 then 127 | local y = value[1] 128 | local x = value[2] 129 | top = y 130 | right = x 131 | bottom = y 132 | left = x 133 | elseif length == 3 then 134 | local x = value[2] 135 | top = value[1] 136 | right = x 137 | bottom = value[3] 138 | left = x 139 | elseif length == 4 then 140 | top = value[1] 141 | right = value[2] 142 | bottom = value[3] 143 | left = value[4] 144 | end 145 | 146 | value.top = value.top or top or 0 147 | value.right = value.right or right or 0 148 | value.bottom = value.bottom or bottom or 0 149 | value.left = value.left or left or 0 150 | return setmetatable(value, thickness_mt) 151 | else 152 | return setmetatable({ 153 | top = value, 154 | right = value, 155 | bottom = value, 156 | left = value, 157 | }, thickness_mt) 158 | end 159 | end 160 | 161 | M.zero = M.new(0) 162 | 163 | return M 164 | -------------------------------------------------------------------------------- /widget/clock_icon.lua: -------------------------------------------------------------------------------- 1 | local math = math 2 | local string = string 3 | local css = require("utils.css") 4 | 5 | 6 | local clock_icon = {} 7 | 8 | local svg_pattern = [[ 9 | 10 | 11 | 12 | 13 | 14 | 15 | ]] 16 | 17 | local hour_hand_size = 0.45 18 | local minute_hand_size = 0.7 19 | 20 | local function fix_hand_position(value, size) 21 | return 0.5 + 0.5 * (value * size) 22 | end 23 | 24 | local function get_hand_x(value, size) 25 | return fix_hand_position(math.sin(value), size) 26 | end 27 | 28 | local function get_hand_y(value, size) 29 | return fix_hand_position(-math.cos(value), size) 30 | end 31 | 32 | function clock_icon.generate_svg(hours, minutes) 33 | if not hours or not minutes then 34 | local date = os.date("*t") 35 | hours = date.hour 36 | minutes = date.min 37 | end 38 | hours = math.floor(tonumber(hours) or 0) 39 | minutes = math.floor(tonumber(minutes) or 0) 40 | 41 | local hour = (((hours + (minutes / 60)) % 12) / 12) * 2 * math.pi 42 | local minute = (minutes / 60) * 2 * math.pi 43 | return string.format(svg_pattern, 44 | get_hand_x(hour, hour_hand_size), get_hand_y(hour, hour_hand_size), 45 | get_hand_x(minute, minute_hand_size), get_hand_y(minute, minute_hand_size)) 46 | end 47 | 48 | function clock_icon.generate_style(color) 49 | return css.style { circle = { fill = color } } 50 | end 51 | 52 | return clock_icon 53 | -------------------------------------------------------------------------------- /widget/mebox/templates/header.lua: -------------------------------------------------------------------------------- 1 | local beautiful = require("theme.theme") 2 | local wibox = require("wibox") 3 | local pango = require("utils.pango") 4 | local dpi = Dpi 5 | local hui = require("utils.thickness") 6 | 7 | 8 | return { 9 | widget = wibox.container.margin, 10 | margins = hui.new { dpi(6), dpi(8) }, 11 | { 12 | id = "#text", 13 | widget = wibox.widget.textbox, 14 | }, 15 | update_callback = function(self, item) 16 | local text_widget = self:get_children_by_id("#text")[1] 17 | if text_widget then 18 | local color = beautiful.common.fg_66 19 | local text = item.text or "" 20 | text_widget:set_markup(pango.span { 21 | size = "smaller", 22 | text_transform = "uppercase", 23 | fgcolor = color, 24 | weight = "bold", 25 | pango.escape(text), 26 | }) 27 | end 28 | end, 29 | } 30 | -------------------------------------------------------------------------------- /widget/mebox/templates/info.lua: -------------------------------------------------------------------------------- 1 | local beautiful = require("theme.theme") 2 | local wibox = require("wibox") 3 | local pango = require("utils.pango") 4 | local dpi = Dpi 5 | local hui = require("utils.thickness") 6 | 7 | 8 | return { 9 | widget = wibox.container.margin, 10 | margins = hui.new { dpi(12), dpi(16) }, 11 | { 12 | layout = wibox.container.place, 13 | halign = "center", 14 | valign = "center", 15 | { 16 | id = "#text", 17 | widget = wibox.widget.textbox, 18 | }, 19 | }, 20 | update_callback = function(self, item) 21 | local style = beautiful.mebox.item_styles.normal.normal 22 | local text_widget = self:get_children_by_id("#text")[1] 23 | if text_widget then 24 | local text = item.text 25 | text_widget:set_markup(pango.span { 26 | fgcolor = style.fg, 27 | weight = "bold", 28 | pango.i(pango.escape(text)), 29 | }) 30 | end 31 | end, 32 | } 33 | -------------------------------------------------------------------------------- /widget/mebox/templates/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | item = require("widget.mebox.templates.item"), 3 | header = require("widget.mebox.templates.header"), 4 | info = require("widget.mebox.templates.info"), 5 | separator = require("widget.mebox.templates.separator"), 6 | } 7 | -------------------------------------------------------------------------------- /widget/mebox/templates/item.lua: -------------------------------------------------------------------------------- 1 | local beautiful = require("theme.theme") 2 | local wibox = require("wibox") 3 | local capsule = require("widget.capsule") 4 | local css = require("utils.css") 5 | local pango = require("utils.pango") 6 | local config = require("rice.config") 7 | local dpi = Dpi 8 | local hui = require("utils.thickness") 9 | 10 | 11 | return { 12 | id = "#container", 13 | widget = capsule, 14 | margins = hui.new { dpi(2), 0 }, 15 | paddings = hui.new { dpi(6), dpi(8) }, 16 | { 17 | layout = wibox.layout.align.horizontal, 18 | expand = "inside", 19 | nil, 20 | { 21 | layout = wibox.layout.fixed.horizontal, 22 | spacing = dpi(12), 23 | { 24 | id = "#icon", 25 | widget = wibox.widget.imagebox, 26 | resize = true, 27 | }, 28 | { 29 | id = "#text", 30 | widget = wibox.widget.textbox, 31 | }, 32 | }, 33 | { 34 | widget = wibox.container.margin, 35 | right = -dpi(2), 36 | { 37 | visible = false, 38 | id = "#right_icon", 39 | widget = wibox.widget.imagebox, 40 | resize = true, 41 | }, 42 | }, 43 | }, 44 | update_callback = function(self, item, menu) 45 | self.forced_width = item.width or menu.item_width 46 | self.forced_height = item.height or menu.item_height 47 | 48 | self.enable_overlay = item.enabled 49 | self.opacity = item.opacity or (item.enabled and 1 or 0.5) 50 | 51 | local styles = item.selected 52 | and beautiful.mebox.item_styles.selected 53 | or beautiful.mebox.item_styles.normal 54 | local style = (not item.selected and item.style) or (item.urgent 55 | and styles.urgent 56 | or styles.normal) 57 | self:apply_style(style) 58 | 59 | local icon_widget = self:get_children_by_id("#icon")[1] 60 | if icon_widget then 61 | local paddings = menu.paddings 62 | icon_widget.forced_width = self.forced_height - paddings.top - paddings.bottom 63 | 64 | local icon = item.icon 65 | if icon == false then 66 | icon_widget.visible = false 67 | icon_widget:set_image(nil) 68 | else 69 | local color = item.icon_color 70 | if icon and color ~= false then 71 | if not color or item.selected then 72 | color = style.fg 73 | end 74 | local stylesheet = css.style { path = { fill = color } } 75 | icon_widget:set_stylesheet(stylesheet) 76 | else 77 | icon_widget:set_stylesheet(nil) 78 | end 79 | icon_widget:set_image(icon) 80 | end 81 | end 82 | 83 | local text_widget = self:get_children_by_id("#text")[1] 84 | if text_widget then 85 | local text = item.text or "" 86 | text_widget:set_markup(pango.span { fgcolor = style.fg, text }) 87 | end 88 | 89 | local right_icon_widget = self:get_children_by_id("#right_icon")[1] 90 | if right_icon_widget then 91 | local icon, color 92 | if item.checked ~= nil then 93 | local checkbox_type = item.checkbox_type or "checkbox" 94 | local checkbox_style = (type(checkbox_type) == "table" and checkbox_type or beautiful.mebox[checkbox_type])[not not item.checked] 95 | icon = checkbox_style.icon 96 | color = checkbox_style.color 97 | elseif item.submenu then 98 | icon = item.submenu_icon or beautiful.icon("chevron-right.svg") 99 | color = style.fg 100 | end 101 | 102 | if item.selected then 103 | color = style.fg 104 | end 105 | 106 | right_icon_widget.visible = not not icon 107 | if right_icon_widget.visible then 108 | right_icon_widget:set_stylesheet(css.style { path = { fill = color } }) 109 | right_icon_widget:set_image(icon) 110 | end 111 | end 112 | 113 | if item.flex then 114 | local width = self:fit({ 115 | screen = menu.screen, 116 | dpi = menu.screen.dpi, 117 | drawable = menu._drawable, 118 | }, menu.screen.geometry.width, menu.screen.geometry.height) 119 | if type(item.flex) == "function" then 120 | width = item.flex(width, self.forced_width) 121 | end 122 | self.forced_width = width 123 | end 124 | end, 125 | } 126 | -------------------------------------------------------------------------------- /widget/mebox/templates/separator.lua: -------------------------------------------------------------------------------- 1 | local beautiful = require("theme.theme") 2 | local wibox = require("wibox") 3 | local dpi = Dpi 4 | 5 | 6 | return { 7 | widget = wibox.widget.separator, 8 | orientation = "auto", 9 | color = beautiful.common.bg_bright, 10 | thickness = dpi(1), 11 | span_ratio = 1, 12 | update_callback = function(self, item, menu) 13 | -- This orientation is inverted from the actual orientation of the separator 14 | local orientation = item.orientation or menu.orientation 15 | local size = dpi(16) 16 | if orientation == "vertical" then 17 | self.forced_width = item.width or menu.item_width 18 | self.forced_height = size 19 | elseif orientation == "horizontal" then 20 | self.forced_width = size 21 | self.forced_height = item.height or menu.item_height 22 | else 23 | error(orientation) 24 | end 25 | end, 26 | } 27 | --------------------------------------------------------------------------------