Fixed titlebar title wrapping issue. Added option to disable titlebar for maximized windows. Added options to specify titlebar mouse button bindings

This commit is contained in:
mut-ex 2020-09-03 17:24:38 -04:00
parent 9196481fd8
commit 625469fd9c
5 changed files with 386 additions and 188 deletions

212
README.md
View File

@ -2,9 +2,9 @@
nice is an easy to use, highly configurable extension for **[Awesome WM](https://awesomewm.org/)** that adds beautiful window decorations (and extra functionality!) to clients. It... nice is an easy to use, highly configurable extension for **[Awesome WM](https://awesomewm.org/)** that adds beautiful window decorations (and extra functionality!) to clients. It...
* ...adds a **subtle 3D look**, and soft, **rounded anti-aliased, corners** to windows * ...adds a **subtle 3D look**, and soft, **rounded anti-aliased, corners** to windows
* ...picks the window **decoration color based on the client content for a seamless look** , and **adjusts the window title text color** accordingly * ...picks the window **decoration color based on the client content for a seamless look** , and adjusts the window title text color accordingly
* ...**auto-generates titlebar buttons** (and their states) for you based on the colors your pick *or* you can let it pick the colors for you! * ...**auto-generates titlebar buttons** (and their states) for you based on the colors your pick *or* you can let it pick the colors for you!
* ...allows you to **customize** which **titlebar buttons** to include, their order, and their layout * ...allows you to customize which titlebar buttons to include, their order, and their layout
* ...adds the **ability to maximize/unmaximize** floating windows by **double clicking the titlebar**, and of course, **moving them by clicking and holding** * ...adds the **ability to maximize/unmaximize** floating windows by **double clicking the titlebar**, and of course, **moving them by clicking and holding**
* ...adds the ability to **"roll up"** and **"roll down"** the client window like a **window shade**! Scroll up over the titlebar to **instantly hide the window contents but keep the title bar** right where it is. And then either scroll down or click the titlebar to make the window contents visible again! * ...adds the ability to **"roll up"** and **"roll down"** the client window like a **window shade**! Scroll up over the titlebar to **instantly hide the window contents but keep the title bar** right where it is. And then either scroll down or click the titlebar to make the window contents visible again!
@ -14,9 +14,9 @@ nice is an easy to use, highly configurable extension for **[Awesome WM](https:/
### Prerequisites ### Prerequisites
* You need to be using **[Awesome WM](https://awesomewm.org/)** as your window manager, and should already have a working basic configuration file. I have developed and tested nice only on **awesome v4.3 git version** * You need **[Awesome WM](https://awesomewm.org/)** with a working basic configuration. **Currently nice only works on Awesome v4.3 git version**. I am working on support for the stable release
* I **highly** suggest using a compositor such as **[picom](https://github.com/yshui/picom)**. My recommended shadow settings are given below * You also need **[picom](https://github.com/yshui/picom)**. Make sure you have `shadow-ignore-shaped = false` in your configuration otherwise picom will not draw shadows. My recommended shadow settings are given below:
``` ```
shadow = true; shadow = true;
@ -29,15 +29,22 @@ nice is an easy to use, highly configurable extension for **[Awesome WM](https:/
"_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'", "_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'",
"_GTK_FRAME_EXTENTS@:c" "_GTK_FRAME_EXTENTS@:c"
]; ];
shadow-ignore-shaped = false
``` ```
* For GTK apps, I suggest adding the following line to **~/.config/gtk-3.0/settings.ini** under the **[Settings]** section * For **GTK** applications add the following line to **~/.config/gtk-3.0/settings.ini** under the **[Settings]** section to hide client-side window control buttons:
``` ```
gtk-decoration-layout=menu: gtk-decoration-layout=menu:
``` ```
* Within you Awesome configuration, make sure that you do not already have code in place that request default titlebars for clients. Something like this:
```lua
client.connect_signal("request::titlebars", function(c) ... end) -- Remove this
```
* Additionally, nice only adds window decorations to clients that have the `titlebars_enabled` property set to true. So configure your client rules accordingly.
### Installation ### Installation
@ -52,70 +59,165 @@ $ git clone https://github.com/mut-ex/awesome-wm-nice.git nice
## Usage ## Usage
To use nice, you first need to load the module. You can do so by placing the following line right after `beautiful.init(...)` To use nice, you first need to load the module. To do that, put the following line right after `beautiful.init(...)`
```lua ```lua
local nice = require("nice") local nice = require("nice")
nice() nice()
``` ```
If you are fine using the default configuration, you are all done! However if you like, you can override the defaults you wish to change by passing your own configuration. There are a lot of parameters you can change! The commented out lines in the code block before represent the default values If you are fine using the default configuration, you are all done!
nice will automatically detect and change the window decoration color to match the client. However...
* To pick the window decoration color yourself, right-click the titlebar and select **'Manually Pick Color'**
* To update the window decoration colors, right-click on the titlebar and select **'Redo Window Decorations'**
* Scroll-up with your mouse over the titlebar to "roll up" the window shade. Scroll-down over the titlebar, or left-click to "roll down" the window shade
* nice saves its color rules in the **color_rules** file within the module directory. If you wish you can manually edit it, or delete the file if you want to start again.
## Configuration
You can override the defaults by passing your own configuration. For example
```lua ```lua
local nice = require("nice") local nice = require("nice")
nice = { nice {
-- * == Titlebar specific == * titlebar_color = "#00ff00",
-- titlebar_color = "#1E1E24"
-- titlebar_height = 38
-- titlebar_radius = 9
-- titlebar_margin_left = 0
-- titlebar_margin_right = 0
-- titlebar_font = "Sans 10"
-- titlebar_items = { -- You only need to pass the parameter you are changing
-- left = {"close", "minimize", "maximize"}, context_menu_theme = {
-- middle = {"title"}, width = 300,
-- right = {"sticky", "ontop", "floating"}, },
-- }
-- context_menu_theme = { -- Swap the designated buttons for resizing, and opening the context menu
-- bg_normal = "#5e6472", mb_resize = nice.MB_MIDDLE,
-- fg_normal = "#fefefa", mb_contextmenu = nice.MB_RIGHT,
-- bg_focus = "#aed9e0", }
-- fg_focus = "#242424", ```
-- border_color = "#00000000",
-- border_width = 0,
-- height = 35,
-- width = 250,
-- font = "Sans 10",
-- }
-- window_shade_enabled = false
-- * == Button specific == * Below you will find further details explaining the configuration parameters for nice.
-- button_margin_horizontal = 5
-- button_margin_top = 2
-- button_size = 16
-- close_color = "#ee4266"
-- minimize_color = "#ffb400"
-- maximize_color = "#4CBB17"
-- floating_color = "#f6a2ed"
-- ontop_color = "#f6a2ed"
-- sticky_color = "#f6a2ed"
-- tooltips_enabled = true | Parameter | Type | Description | Default |
| --------------------- | :--: | ----------- | ------------------- |
| `titlebar_height` | integer | The height of the titlebar | `38` |
| `titlebar_radius` | integer | The radius of the top left and top right corners of the titlebar. Should be `>= 3` and `<= titlebar_height` | `9` |
| `titlebar_color` | string | The default color of the titlebar and window decorations. Should be a hex color string | `"#1e1e24"` |
| `titlebar_padding_left` | integer | The padding on the left side of the titlebar | `0` |
| `titlebar_padding_right` | integer | The padding on the right side of the titlebar | `0` |
| `titlebar_font` | string | The font and font size for text within the titlebar. See the default value for an example of the format | `"Sans 11"` |
| `win_shade_enabled` | boolean | Whether the window shade feature should be enabled | `true` |
| `no_titlebar_maximized` | boolean | Whether the titlebar should be hidden for maximized windows | `false` |
| `mb_move` | integer or named constant | Mouse button to move a window. | `nice.MB_LEFT` |
| `mb_contextmenu` | integer or named constant | Mouse button to open the nice context menu | `nice.MB_MIDDLE` |
| `mb_resize` | integer or named constant | Mouse button to resize a window | `nice.MB_RIGHT` |
| `mb_win_shade_rollup` | integer or named constant | Mouse button to roll up/hide window contents | `nice.MB_SCROLL_UP` |
| `mb_win_shade_rolldown` | integer or named constant | Mouse button to roll down/show window contents | `nice.MB_SCROLL_DOWN` |
| `button_size` | integer | The size (diameter) of the titlebar buttons | 16 |
| `button_margin_horizontal` | integer | The horizontal margin around each titlebar button. `button_margin_left` and `button_margin_right`can override this parameter. | 5 |
| `button_margin_vertical` | integer | The vertical margin above and below each titlebar button. `button_margin_top` and `button_margin_bottom` can override this parameter. | nil |
| `button_margin_top` | integer | The margin above each titlebar button | 2 |
| `button_margin_bottom` | integer | The margin below each titlebar button | nil |
| `button_margin_left` | integer | The margin to the left of each titlebar button | 0 |
| `button_margin_right` | integer | The margin to the right of each titlebar button | 0 |
| `tooltips_enabled` | boolean | If tooltip hints should be shown when the mouse cursor is hovered over a titlebar button | nil |
| `close_color` | string | The base color for the close button | "#ee4266 |
| `minimize_color` | string | The base color for the minimize button | "#ffb400" |
| `maximize_color` | string | The base color for the maximize button | "#4cbb17" |
| `floating_color` | string | The base color for the floating mode toggle button | "#f6a2ed" |
| `ontop_color` | string | The base color for the on top mode toggle button | "#f6a2ed" |
| `sticky_color` | string | The base color for the sticky mode toggle button | "#f6a2ed" |
-- tooltip_messages = { In addition to the above mentioned parameters, there some more parameters that require a little more explanation:
-- close = "close",
-- minimize = "minimize", ### titlebar_items
-- floating_active = "enable tiling mode",
-- floating_inactive = "enable floating mode", `titlebar_items` — Specifies the titlebar items to include
-- maximize_active = "unmaximize",
-- maximize_inactive = "maximize", * It should be a table with the following keys:
-- ontop_active = "don't keep above other windows", * `left` — Specifies the item(s) to place on the left side of the titlebar
-- ontop_inactive = "keep above other windows", * `middle` — Specifies the item(s) to place in the middle of the titlebar
-- sticky_active = "disable sticky mode", * `right` — Specifies the items(s) to place on the right side of the titlebar
-- sticky_inactive = "enable sticky mode", * Multiple items should be passed as an array of identifiers. For a single item simply passing the identifier is sufficient
-- } * Valid titlebar item identifiers are:
* `"close"`
* `"minimize"`
* `"maximize"`
* `"floating"`
* `"ontop"`
* `"sticky"`
* `"title"`
* Default value for `titlebar_items` is:
```lua
titlebar_items = {
left = {"close", "minimize", "maximize"},
middle = "title",
right = {"sticky", "ontop", "floating"},
}
```
### context_menu_theme
`context_menu_theme` — Specifies theming parameters for the context (default right-click) menu
* It should be a table with the following keys:
* `bg_focus` — Background color of focused menu item
* `bg_normal` — Background color of not-focused menu items
* `border_color` — Color of the border around the entire menu
* `border_width` — Width of the border around the entire menu
* `fg_focus` — Foreground color of focused menu item
* `fg_normal` — Foreground color of not-focused menu items
* `font` — Font used for menu text
* `height` — Height of each menu list item
* `width` — Width of the menu
* Default value for `context_menu_theme` is:
```lua
context_menu_theme = {
bg_focus = "#aed9e0",
bg_normal = "#5e6472",
border_color = "#00000000",
border_width = 0,
fg_focus = "#242424",
fg_normal = "#fefefa",
font = "Sans 11",
height = 27.5,
width = 250,
}
```
### tooltip_messages
`tooltip_messages` — Specifies the hints that are shown when the mouse cursor is hovered over a titlebar button
* It should be a table with the following keys:
* `close` — Text shown when hovering over the close button
* `minimize` — Text shown when hovering over the minimize button
* `maximize_active` — Text shown when hovering over the maximize button when the window is maximized
* `maximize_inactive` — Text shown when hovering over the maximize button when the window is not maximized
* `floating_active` — Text shown when hovering over the floating button when the window is floating
* `floating_inactive` — Text shown when hovering over the floating button when the window is tiled
* `ontop_active` — Text shown when hovering over the ontop button when the window is set to be above other windows
* `ontop_inactive` — Text shown when hovering over the ontop button when the window is not set to be above other windows
* `sticky_active` — Text shown when hovering over the sticky button when the window is set to be available on all tags
* `sticky_inactive` — Text shown when hovering over the sticky button when the window is not to be available on all tags
The default value for `tooltip_messages` is:
```lua
tooltip_messages = {
close = "close",
minimize = "minimize",
maximize_active = "unmaximize",
maximize_inactive = "maximize",
floating_active = "enable tiling mode",
floating_inactive = "enable floating mode",
ontop_active = "don't keep above other windows",
ontop_inactive = "keep above other windows",
sticky_active = "disable sticky mode",
sticky_inactive = "enable sticky mode",
} }
``` ```

View File

@ -7,27 +7,29 @@ local max = math.max
local min = math.min local min = math.min
local pow = math.pow local pow = math.pow
local random = math.random local random = math.random
local debug = require("helpers").debug -- local debug = require("helpers").debug
local gcolor = require("gears.color")
local parse_color = gcolor.parse_color
-- Returns a value that is clipped to interval edges if it falls outside the interval -- Returns a value that is clipped to interval edges if it falls outside the interval
local function clip(num, min_num, max_num) return local function clip(num, min_num, max_num) return
max(min(num, max_num), min_num) end max(min(num, max_num), min_num) end
-- Converts the given hex color to normalized rgba -- Converts the given hex color to normalized rgba
local function hex2rgb(color) local function hex2rgb(color)
color = color:gsub("#", "") -- color = color:gsub("#", "")
local strlen = color:len() -- local strlen = color:len()
if strlen == 6 then -- if strlen == 6 then
return tonumber("0x" .. color:sub(1, 2)) / 255, -- return tonumber("0x" .. color:sub(1, 2)) / 255,
tonumber("0x" .. color:sub(3, 4)) / 255, -- tonumber("0x" .. color:sub(3, 4)) / 255,
tonumber("0x" .. color:sub(5, 6)) / 255, 1 -- tonumber("0x" .. color:sub(5, 6)) / 255, 1
end -- end
if strlen == 8 then -- if strlen == 8 then
return tonumber("0x" .. color:sub(1, 2)) / 255, -- return tonumber("0x" .. color:sub(1, 2)) / 255,
tonumber("0x" .. color:sub(3, 4)) / 255, -- tonumber("0x" .. color:sub(3, 4)) / 255,
tonumber("0x" .. color:sub(5, 6)) / 255, -- tonumber("0x" .. color:sub(5, 6)) / 255,
tonumber("0x" .. color:sub(7, 8)) / 255 -- tonumber("0x" .. color:sub(7, 8)) / 255
end -- end
return parse_color(color)
end end
-- Converts the given hex color to hsv -- Converts the given hex color to hsv

263
init.lua
View File

@ -1,9 +1,13 @@
----
-- ============================================================ -- ============================================================
local ret, helpers = pcall(require, "helpers")
local debug = ret and helpers.debug or function() end
local ipairs = ipairs
-- > Awesome WM LIBS -- > Awesome WM LIBS
local awful = require("awful") local awful = require("awful")
local atooltip = awful.tooltip local atooltip = awful.tooltip
local wibox = require("wibox") local wibox = require("wibox")
local get_font_height = require("beautiful").get_font_height
-- Widgets -- Widgets
local imagebox = wibox.widget.imagebox local imagebox = wibox.widget.imagebox
local textbox = wibox.widget.textbox local textbox = wibox.widget.textbox
@ -14,38 +18,40 @@ local wlayout_fixed_horizontal = wlayout.fixed.horizontal
-- Containers -- Containers
local wcontainer = wibox.container local wcontainer = wibox.container
local wcontainer_background = wcontainer.background local wcontainer_background = wcontainer.background
local wcontainer_place = wcontainer.place
local wcontainer_constraint = wcontainer.constraint local wcontainer_constraint = wcontainer.constraint
local wcontainer_margin = wcontainer.margin
local wcontainer_place = wcontainer.place
-- Gears -- Gears
local gsurface = require("gears.surface") local gsurface = require("gears.surface")
local gtimer = require("gears.timer") local gtimer = require("gears.timer")
local gtimer_weak_start_new = gtimer.weak_start_new local gtimer_weak_start_new = gtimer.weak_start_new
-- > MATH LIBS -- > Math
local math = math local math = math
local max = math.max local max = math.max
local abs = math.abs local abs = math.abs
local rad = math.rad local rad = math.rad
local floor = math.floor local floor = math.floor
-- > LGI LIBS -- > LGI
local lgi = require("lgi") local lgi = require("lgi")
local cairo = lgi.cairo local cairo = lgi.cairo
local gdk = lgi.Gdk local gdk = lgi.Gdk
local pixbuf_get_from_window = gdk.pixbuf_get_from_window
local get_default_root_window = gdk.get_default_root_window local get_default_root_window = gdk.get_default_root_window
local pixbuf_get_from_surface = gdk.pixbuf_get_from_surface local pixbuf_get_from_surface = gdk.pixbuf_get_from_surface
local pixbuf_get_from_window = gdk.pixbuf_get_from_window
-- > NICE LIBS -- > NICE LIBS
-- Colors -- Colors
local colors = require("nice.colors") local colors = require("nice.colors")
local color_lighten = colors.lighten
local color_darken = colors.darken local color_darken = colors.darken
local color_lighten = colors.lighten
local is_contrast_acceptable = colors.is_contrast_acceptable local is_contrast_acceptable = colors.is_contrast_acceptable
local relative_luminance = colors.relative_luminance local relative_luminance = colors.relative_luminance
-- Shapes -- Shapes
local shapes = require("nice.shapes") local shapes = require("nice.shapes")
local create_corner_top_left = shapes.create_corner_top_left local create_corner_top_left = shapes.create_corner_top_left
local create_edge_top_middle = shapes.create_edge_top_middle
local create_edge_left = shapes.create_edge_left local create_edge_left = shapes.create_edge_left
local create_edge_top_middle = shapes.create_edge_top_middle
local gradient = shapes.duotone_gradient_vertical local gradient = shapes.duotone_gradient_vertical
gdk.init({}) gdk.init({})
-- => Local settings -- => Local settings
@ -66,7 +72,13 @@ local function rel_lighten(lum) return lum * 90 + 10 end
local function rel_darken(lum) return -(lum * 70) + 100 end local function rel_darken(lum) return -(lum * 70) + 100 end
-- ------------------------------------------------------------ -- ------------------------------------------------------------
local nice = {} local nice = {
MB_LEFT = 1,
MB_MIDDLE = 2,
MB_RIGHT = 3,
MB_SCROLL_UP = 4,
MB_SCROLL_DOWN = 5,
}
-- => Defaults -- => Defaults
-- ============================================================ -- ============================================================
@ -75,41 +87,52 @@ _private.max_width = 0
_private.max_height = 0 _private.max_height = 0
-- Titlebar -- Titlebar
_private.titlebar_color = "#1E1E24"
_private.titlebar_height = 38 _private.titlebar_height = 38
_private.titlebar_radius = 9 _private.titlebar_radius = 9
_private.titlebar_color = "#1E1E24"
_private.titlebar_margin_left = 0 _private.titlebar_margin_left = 0
_private.titlebar_margin_right = 0 _private.titlebar_margin_right = 0
_private.titlebar_font = "Inter Regular 10" _private.titlebar_font = "Sans 11"
_private.titlebar_items = { _private.titlebar_items = {
left = {"close", "minimize", "maximize"}, left = {"close", "minimize", "maximize"},
middle = {"title"}, middle = "title",
right = {"sticky", "ontop", "floating"}, right = {"sticky", "ontop", "floating"},
} }
_private.context_menu_theme = { _private.context_menu_theme = {
bg_normal = "#5e6472",
fg_normal = "#fefefa",
bg_focus = "#aed9e0", bg_focus = "#aed9e0",
fg_focus = "#242424", bg_normal = "#5e6472",
border_color = "#00000000", border_color = "#00000000",
border_width = 0, border_width = 0,
height = 35, fg_focus = "#242424",
fg_normal = "#fefefa",
font = "Sans 11",
height = 27.5,
width = 250, width = 250,
font = "Inter Regular 10",
} }
_private.window_shade_enabled = true _private.win_shade_enabled = true
-- Button _private.no_titlebar_maximized = false
_private.button_margin_horizontal = 5 _private.mb_move = nice.MB_LEFT
_private.button_margin_top = 2 _private.mb_contextmenu = nice.MB_MIDDLE
_private.mb_resize = nice.MB_RIGHT
_private.mb_win_shade_rollup = nice.MB_SCROLL_UP
_private.mb_win_shade_rolldown = nice.MB_SCROLL_DOWN
-- Titlebar Items
_private.button_size = 16 _private.button_size = 16
_private.button_margin_horizontal = 5
-- _private.button_margin_vertical
_private.button_margin_top = 2
-- _private.button_margin_bottom = 0
-- _private.button_margin_left = 0
-- _private.button_margin_right = 0
_private.tooltips_enabled = true _private.tooltips_enabled = true
_private.tooltip_messages = { _private.tooltip_messages = {
close = "close", close = "close",
minimize = "minimize", minimize = "minimize",
floating_active = "enable tiling mode",
floating_inactive = "enable floating mode",
maximize_active = "unmaximize", maximize_active = "unmaximize",
maximize_inactive = "maximize", maximize_inactive = "maximize",
floating_active = "enable tiling mode",
floating_inactive = "enable floating mode",
ontop_active = "don't keep above other windows", ontop_active = "don't keep above other windows",
ontop_inactive = "keep above other windows", ontop_inactive = "keep above other windows",
sticky_active = "disable sticky mode", sticky_active = "disable sticky mode",
@ -310,7 +333,7 @@ local function create_titlebar_button(c, name, button_callback, property)
end) end)
-- The button is updated on both click and release, but the call back is executed on release -- The button is updated on both click and release, but the call back is executed on release
button_img.buttons = awful.button( button_img.buttons = awful.button(
{}, awful.button.names.LEFT, function() {}, nice.MB_LEFT, function()
event = "press" event = "press"
update() update()
end, function() end, function()
@ -322,13 +345,12 @@ local function create_titlebar_button(c, name, button_callback, property)
end end
update() update()
end) end)
local margin = _private.button_spacing
button_img.id = "button_image" button_img.id = "button_image"
update() update()
return wibox.widget { return wibox.widget {
widget = wcontainer_place, widget = wcontainer_place,
{ {
widget = wcontainer.margin, widget = wcontainer_margin,
top = _private.button_margin_top or _private.button_margin_vertical or top = _private.button_margin_top or _private.button_margin_vertical or
_private.button_margin, _private.button_margin,
bottom = _private.button_margin_bottom or bottom = _private.button_margin_bottom or
@ -348,8 +370,7 @@ local function create_titlebar_button(c, name, button_callback, property)
} }
end end
-- Creates a client title widget local function get_titlebar_mouse_bindings(c)
local function create_titlebar_title(c)
local client_color = c._nice_base_color local client_color = c._nice_base_color
local shade_enabled = _private.window_shade_enabled local shade_enabled = _private.window_shade_enabled
-- Add functionality for double click to (un)maximize, and single click and hold to move -- Add functionality for double click to (un)maximize, and single click and hold to move
@ -357,7 +378,7 @@ local function create_titlebar_title(c)
local tolerance = double_click_jitter_tolerance local tolerance = double_click_jitter_tolerance
local buttons = { local buttons = {
awful.button( awful.button(
{}, awful.button.names.LEFT, function() {}, _private.mb_move, function()
local cx, cy = _G.mouse.coords().x, _G.mouse.coords().y local cx, cy = _G.mouse.coords().x, _G.mouse.coords().y
local delta = double_click_time_window_ms / 1000 local delta = double_click_time_window_ms / 1000
clicks = clicks + 1 clicks = clicks + 1
@ -381,14 +402,13 @@ local function create_titlebar_title(c)
delta, function() clicks = 0 end) delta, function() clicks = 0 end)
end), end),
awful.button( awful.button(
{}, awful.button.names.RIGHT, function() {}, _private.mb_contextmenu, function()
local menu_items = {} local menu_items = {}
local function add_item(text, callback) local function add_item(text, callback)
-- right_click_menu(right_click_menu, {text, callback})
menu_items[#menu_items + 1] = {text, callback} menu_items[#menu_items + 1] = {text, callback}
end end
-- TODO: Add client control options as menu enteries for options that haven't had their buttons added -- TODO: Add client control options as menu entries for options that haven't had their buttons added
add_item( add_item(
"Redo Window Decorations", function() "Redo Window Decorations", function()
c._nice_base_color = get_dominant_color(c) c._nice_base_color = get_dominant_color(c)
@ -409,14 +429,6 @@ local function create_titlebar_title(c)
return true return true
end, "crosshair") end, "crosshair")
end) end)
-- if c._nice_window_shade then
-- local win_shade = c._nice_window_shade
-- add_item(
-- not win_shade.visible and "Roll Up" or "Roll Down",
-- function()
-- _private.shade_toggle(c)
-- end)
-- end
add_item("Nevermind...", function() end) add_item("Nevermind...", function() end)
if c._nice_right_click_menu then if c._nice_right_click_menu then
c._nice_right_click_menu:hide() c._nice_right_click_menu:hide()
@ -428,90 +440,107 @@ local function create_titlebar_title(c)
} }
c._nice_right_click_menu:show() c._nice_right_click_menu:show()
end), end),
awful.button(
{}, _private.mb_resize, function()
c:activate{context = "mouse_click", action = "mouse_resize"}
end),
} }
if _private.window_shade_enabled then if _private.window_shade_enabled then
buttons[#buttons + 1] = awful.button( buttons[#buttons + 1] = awful.button(
{}, awful.button.names.SCROLL_UP, {}, _private.mb_win_shade_rollup,
function() function()
_private.shade_roll_up(c) _private.shade_roll_up(c)
end) end)
buttons[#buttons + 1] = awful.button( buttons[#buttons + 1] = awful.button(
{}, awful.button.names.SCROLL_DOWN, {}, _private.mb_win_shade_rolldown,
function() function()
_private.shade_roll_down(c) _private.shade_roll_down(c)
end) end)
end end
return buttons
end
-- Returns a titlebar widget for the given client
local function create_titlebar_title(c)
local client_color = c._nice_base_color
local title_widget = wibox.widget { local title_widget = wibox.widget {
align = "center", align = "center",
buttons = buttons,
ellipsize = "middle", ellipsize = "middle",
font = _private.titlebar_font,
opacity = c.active and 1 or title_unfocused_opacity, opacity = c.active and 1 or title_unfocused_opacity,
valign = "center", valign = "center",
widget = textbox, widget = textbox,
} }
local function update() local function update()
local text_color = is_contrast_acceptable( local text_color = is_contrast_acceptable(
title_color_light, client_color) and title_color_light, client_color) and
title_color_light or title_color_dark title_color_light or title_color_dark
title_widget.markup = title_widget.markup =
("<span foreground='%s' font='%s'>%s</span>"):format( ("<span foreground='%s' font='%s'>%s</span>"):format(
text_color, _private.titlebar_font or "IBM Plex Sans 11", c.name) text_color, _private.titlebar_font, c.name)
end end
c:connect_signal("property::name", update) c:connect_signal("property::name", update)
-- c:connect_signal("property::_nice_color", update)
c:connect_signal( c:connect_signal(
"unfocus", function() "unfocus", function()
title_widget.opacity = title_unfocused_opacity title_widget.opacity = title_unfocused_opacity
end) end)
c:connect_signal("focus", function() title_widget.opacity = 1 end) c:connect_signal("focus", function() title_widget.opacity = 1 end)
update() update()
return {title_widget, widget = wcontainer.margin, left = 4, right = 4} local titlebar_font_height = get_font_height(_private.titlebar_font)
local leftover_space = _private.titlebar_height - titlebar_font_height
local margin_vertical = leftover_space > 1 and leftover_space / 2 or 0
return {
title_widget,
widget = wibox.container.margin,
top = margin_vertical,
bottom = margin_vertical,
}
end end
-- Creates titlebar items for a given group of item names -- Returns a titlebar item
local function create_titlebar_items(c, group) local function get_titlebar_item(c, name)
if not group then return nil end
local titlebar_group_items = wibox.widget {
layout = wlayout_fixed_horizontal,
}
local item
for _, name in ipairs(group) do
if name == "close" then if name == "close" then
item = create_titlebar_button( return create_titlebar_button(c, name, function() c:kill() end)
c, name, function() c:kill() end)
elseif name == "maximize" then elseif name == "maximize" then
item = create_titlebar_button( return create_titlebar_button(
c, name, function() c, name, function() c.maximized = not c.maximized end,
c.maximized = not c.maximized "maximized")
end, "maximized")
elseif name == "minimize" then elseif name == "minimize" then
item = create_titlebar_button( return create_titlebar_button(
c, name, function() c.minimized = true end) c, name, function() c.minimized = true end)
elseif name == "ontop" then elseif name == "ontop" then
item = create_titlebar_button( return create_titlebar_button(
c, name, function() c.ontop = not c.ontop end, "ontop") c, name, function() c.ontop = not c.ontop end, "ontop")
elseif name == "floating" then elseif name == "floating" then
item = create_titlebar_button( return create_titlebar_button(
c, name, function() c, name, function()
c.floating = not c.floating c.floating = not c.floating
if c.floating then c.maximized = false end if c.floating then c.maximized = false end
end, "floating") end, "floating")
elseif name == "sticky" then elseif name == "sticky" then
item = create_titlebar_button( return create_titlebar_button(
c, name, function() c, name, function()
c.sticky = not c.sticky c.sticky = not c.sticky
return c.sticky return c.sticky
end, "sticky") end, "sticky")
elseif name == "title" then elseif name == "title" then
item = create_titlebar_title(c) return create_titlebar_title(c)
end end
if #group == 1 then return item end end
titlebar_group_items:add(item)
-- Creates titlebar items for a given group of item names
local function create_titlebar_items(c, group)
if not group then return nil end
if type(group) == "string" then return get_titlebar_item(c, group) end
local titlebar_group_items = wibox.widget {
layout = wlayout_fixed_horizontal,
}
local item
for _, name in ipairs(group) do
item = get_titlebar_item(c, name)
if item then titlebar_group_items:add(item) end
end end
return titlebar_group_items return titlebar_group_items
end end
@ -528,18 +557,14 @@ local function add_window_shade(c, src_top, src_bottom)
w.height = _private.titlebar_height + 3 w.height = _private.titlebar_height + 3
w.ontop = true w.ontop = true
w.visible = false w.visible = false
w.shape= shapes.rounded_rect { w.shape = shapes.rounded_rect {
tl = _private.titlebar_radius+1, tl = _private.titlebar_radius + 1,
tr = _private.titlebar_radius+1, tr = _private.titlebar_radius + 1,
bl = 4, bl = 4,
br = 4, br = 4,
} }
w.widget = wibox.widget { w.widget = wibox.widget {
{ {src_top, src_bottom, layout = wlayout.fixed.vertical},
src_top,
src_bottom,
layout = wlayout.fixed.vertical,
},
widget = wcontainer_background, widget = wcontainer_background,
bg = "transparent", bg = "transparent",
} }
@ -549,15 +574,14 @@ local function add_window_shade(c, src_top, src_bottom)
c._nice_window_shade.visible = false c._nice_window_shade.visible = false
c._nice_window_shade = nil c._nice_window_shade = nil
end end
collectgarbage()
collectgarbage()
end) end)
c._nice_window_shade = w c._nice_window_shade = w
end end
-- Shows the window contents -- Shows the window contents
function _private.shade_roll_down(c) function _private.shade_roll_down(c)
c.minimized = false c:activate()
c._nice_window_shade.visible = false c._nice_window_shade.visible = false
end end
@ -606,14 +630,12 @@ function _private.add_window_decorations(c)
stroke_inner_bottom_lighten_mul) stroke_inner_bottom_lighten_mul)
-- Outer strokes -- Outer strokes
local stroke_color_outer_top = darken( local stroke_color_outer_top = darken(
darken_amount * darken_amount *
stroke_outer_top_darken_mul) stroke_outer_top_darken_mul)
local stroke_color_outer_sides = darken(darken_amount) local stroke_color_outer_sides = darken(darken_amount)
local stroke_color_outer_bottom = darken(darken_amount) local stroke_color_outer_bottom = darken(darken_amount)
local titlebar_height = _private.titlebar_height local titlebar_height = _private.titlebar_height
local background_fill_top = gradient( local background_fill_top = gradient(
lighten(titlebar_gradient_c1_lighten), lighten(titlebar_gradient_c1_lighten),
client_color, titlebar_height, 0, client_color, titlebar_height, 0,
titlebar_gradient_c2_offset) titlebar_gradient_c2_offset)
@ -652,19 +674,23 @@ function _private.add_window_decorations(c)
local titlebar = awful.titlebar( local titlebar = awful.titlebar(
c, {size = titlebar_height, bg = "transparent"}) c, {size = titlebar_height, bg = "transparent"})
-- Arrange the graphics -- Arrange the graphics
titlebar.widget = { titlebar:setup{
imagebox(corner_top_left_img, false), imagebox(corner_top_left_img, false),
{ {
{ {
{ {
create_titlebar_items(c, _private.titlebar_items.left), create_titlebar_items(c, _private.titlebar_items.left),
widget = wcontainer.margin, widget = wcontainer_margin,
left = _private.titlebar_margin_left, left = _private.titlebar_margin_left,
}, },
{
create_titlebar_items(c, _private.titlebar_items.middle), create_titlebar_items(c, _private.titlebar_items.middle),
buttons = get_titlebar_mouse_bindings(c),
layout = wibox.layout.flex.horizontal,
},
{ {
create_titlebar_items(c, _private.titlebar_items.right), create_titlebar_items(c, _private.titlebar_items.right),
widget = wcontainer.margin, widget = wcontainer_margin,
right = _private.titlebar_margin_right, right = _private.titlebar_margin_right,
}, },
layout = wlayout_align_horizontal, layout = wlayout_align_horizontal,
@ -723,7 +749,6 @@ function _private.add_window_decorations(c)
}, },
} }
local corner_bottom_left_img = shapes.flip( local corner_bottom_left_img = shapes.flip(
create_corner_top_left { create_corner_top_left {
color = client_color, color = client_color,
radius = bottom_edge_height, radius = bottom_edge_height,
@ -770,9 +795,34 @@ function _private.add_window_decorations(c)
if _private.window_shade_enabled then if _private.window_shade_enabled then
add_window_shade(c, titlebar.widget, bottom.widget) add_window_shade(c, titlebar.widget, bottom.widget)
end end
if _private.no_titlebar_maximized then
c:connect_signal(
"property::maximized", function()
if c.maximized then
local curr_screen_geo = client.focus.screen.geometry
awful.titlebar.hide(c)
c.shape = nil
c:geometry{
x = 0,
y = 0,
width = curr_screen_geo.width,
height = curr_screen_geo.height,
}
else
awful.titlebar.show(c)
-- Shape the client
c.shape = shapes.rounded_rect {
tl = _private.titlebar_radius,
tr = _private.titlebar_radius,
bl = 4,
br = 4,
}
end
end)
end
-- Clean up -- Clean up
collectgarbage() collectgarbage("collect")
collectgarbage()
end end
local function update_max_screen_dims() local function update_max_screen_dims()
@ -784,9 +834,38 @@ local function update_max_screen_dims()
_private.max_height = max_height * 1.5 _private.max_height = max_height * 1.5
_private.max_width = max_width * 1.5 _private.max_width = max_width * 1.5
end end
--[[
]] local function validate_mb_bindings()
local action_mbs = {
"mb_move",
"mb_contextmenu",
"mb_resize",
"mb_win_shade_rollup",
"mb_win_shade_rolldown",
}
local mb_specified = {false, false, false, false, false}
local mb
local mb_conflict_test
for i, action_mb in ipairs(action_mbs) do
mb = _private[action_mb]
if mb then
assert(mb >= 1 and mb <= 5, "Invalid mouse button specified!")
mb_conflict_test = mb_specified[mb]
if not mb_conflict_test then
mb_specified[mb] = action_mb
else
error(
("%s and %s can not be bound to the same mouse button"):format(
action_mb, mb_conflict_test))
end
else
end
end
end
function nice.initialize(args) function nice.initialize(args)
update_max_screen_dims() update_max_screen_dims()
_G.screen.connect_signal("list", update_max_screen_dims) _G.screen.connect_signal("list", update_max_screen_dims)
@ -809,6 +888,8 @@ function nice.initialize(args)
end end
end end
validate_mb_bindings()
_G.client.connect_signal( _G.client.connect_signal(
"request::titlebars", function(c) "request::titlebars", function(c)
-- Callback -- Callback
@ -839,7 +920,7 @@ function nice.initialize(args)
c:connect_signal( c:connect_signal(
"request::activate", c._cb_add_window_decorations) "request::activate", c._cb_add_window_decorations)
end end
-- Shape the client, unnecessary if there are no shadows under the clients. -- Shape the client
c.shape = shapes.rounded_rect { c.shape = shapes.rounded_rect {
tl = _private.titlebar_radius, tl = _private.titlebar_radius,
tr = _private.titlebar_radius, tr = _private.titlebar_radius,

View File

@ -10,6 +10,7 @@ local cairo = lgi.cairo
local math = math local math = math
local rad = math.rad local rad = math.rad
local floor = math.floor local floor = math.floor
local min = math.min
-- Returns a shape function for a rounded rectangle with independently configurable corner radii -- Returns a shape function for a rounded rectangle with independently configurable corner radii
local function rounded_rect(args) local function rounded_rect(args)
@ -41,8 +42,7 @@ local function circle_filled(color, size)
-- cr:set_source_rgba(hex2rgb(darken(color, 25))) -- cr:set_source_rgba(hex2rgb(darken(color, 25)))
-- cr.line_width = 1 -- cr.line_width = 1
-- cr:stroke() -- cr:stroke()
collectgarbage()
collectgarbage()
return surface return surface
end end
@ -58,6 +58,23 @@ local function duotone_gradient_vertical(color_1, color_2, height, offset_1,
return fill_pattern return fill_pattern
end end
-- Returns a horizontal gradient pattern going from cololr_1 -> color_2
local function duotone_gradient_horizontal(color, width)
local fill_pattern = cairo.Pattern.create_linear(0, 0, width, 0)
local r, g, b, a
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0.5, r, g, b, a)
r, g, b, a = hex2rgb("#00000000")
fill_pattern:add_color_stop_rgba(0.6, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0.7, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(1, r, g, b, a)
return fill_pattern
end
-- Flips the given surface around the specified axis -- Flips the given surface around the specified axis
local function flip(surface, axis) local function flip(surface, axis)
local width = surface:get_width() local width = surface:get_width()
@ -80,8 +97,7 @@ local function flip(surface, axis)
cr.source = source_pattern cr.source = source_pattern
cr:rectangle(0, 0, width, height) cr:rectangle(0, 0, width, height)
cr:paint() cr:paint()
collectgarbage()
collectgarbage()
return flipped return flipped
end end
@ -135,8 +151,7 @@ local function create_corner_top_left(args)
width = args.stroke_width_inner, width = args.stroke_width_inner,
source = args.stroke_source_inner, source = args.stroke_source_inner,
} }
collectgarbage()
collectgarbage()
return surface return surface
end end
@ -168,8 +183,7 @@ local function create_edge_top_middle(args)
add_stroke( add_stroke(
args.stroke_width_outer, args.stroke_offset_outer, args.stroke_width_outer, args.stroke_offset_outer,
args.stroke_color_outer) args.stroke_color_outer)
collectgarbage()
collectgarbage()
return surface return surface
end end
@ -196,8 +210,7 @@ local function create_edge_left(args)
cr.line_width = args.stroke_width_outer -- 1 cr.line_width = args.stroke_width_outer -- 1
cr:set_source_rgb(hex2rgb(args.stroke_color_outer)) cr:set_source_rgb(hex2rgb(args.stroke_color_outer))
cr:stroke() cr:stroke()
collectgarbage()
collectgarbage()
return surface return surface
end end

View File

@ -2,7 +2,7 @@
Courtesy of: http://lua-users.org/wiki/SaveTableToFile Courtesy of: http://lua-users.org/wiki/SaveTableToFile
]] local function exportstring(s) return string.format("%q", s) end ]] local function exportstring(s) return string.format("%q", s) end
-- // The Save Function -- The Save Function
local function save(tbl, filename) local function save(tbl, filename)
local charS, charE = " ", "\n" local charS, charE = " ", "\n"
local file, err = io.open(filename, "wb") local file, err = io.open(filename, "wb")