awesome-wm-nice/widgets.lua

270 lines
9.5 KiB
Lua

local abutton = require "awful.button"
local atooltip = require "awful.tooltip"
local colors = require "awesome-wm-nice.colors"
local get_font_height = require("beautiful").get_font_height
local imagebox = require "wibox.widget.imagebox"
local shapes = require "awesome-wm-nice.shapes"
local textbox = require "wibox.widget.textbox"
local wibox = require "wibox"
local wcontainer_constraint = require "wibox.container.constraint"
local wcontainer_margin = require "wibox.container.margin"
local wcontainer_place = require "wibox.container.place"
local widgets = {}
local cache = {}
-- Legacy global variables
local _private = {}
_private.titlebar_height = 38
_private.titlebar_font = "Sans 11"
_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.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",
}
_private.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",
}
cache.close_color = "#ee4266"
cache.minimize_color = "#ffb400"
cache.maximize_color = "#4CBB17"
cache.floating_color = "#f6a2ed"
cache.ontop_color = "#f6a2ed"
cache.sticky_color = "#f6a2ed"
local title_color_dark = "#242424"
local title_color_light = "#fefefa"
local title_unfocused_opacity = 0.7
-- Returns a color that is analogous to the last color returned
-- To make sure that the "randomly" generated colors look cohesive, only the
-- first color is truly random, the rest are generated by offseting the hue by
-- +33 degrees
local next_color = colors.rand_hex()
local function get_next_color()
local prev_color = next_color
next_color = colors.rotate_hue(prev_color, 33)
return prev_color
end
-- Returns (or generates) a button image based on the given params
function widgets.create_button_image(name, is_focused, event, is_on)
local focus_state = is_focused and "focused" or "unfocused"
local key_img
-- If it is a toggle button, then the key has an extra param
if is_on ~= nil then
local toggle_state = is_on and "on" or "off"
key_img = ("%s_%s_%s_%s"):format(name, toggle_state, focus_state, event)
else
key_img = ("%s_%s_%s"):format(name, focus_state, event)
end
-- If an image already exists, then we are done
if cache[key_img] then
return cache[key_img]
end
-- The color key just has _color at the end
local key_color = key_img .. "_color"
-- If the user hasn't provided a color, then we have to generate one
if not cache[key_color] then
local key_base_color = name .. "_color"
-- Maybe the user has at least provided a base color? If not we just pick a pesudo-random color
local base_color = cache[key_base_color] or get_next_color()
cache[key_base_color] = base_color
local button_color = base_color
local H = colors.hex2hsv(base_color)
-- Unfocused buttons are desaturated and darkened (except when they are being hovered over)
if not is_focused and event ~= "hover" then
button_color = colors.hsv2hex(H, 0, 50)
end
-- Then the color is lightened if the button is being hovered over, or
-- darkened if it is being pressed, otherwise it is left as is
button_color = (event == "hover") and colors.lighten(button_color, 25)
or (event == "press") and colors.darken(
button_color,
25
)
or button_color
-- Save the generate color because why not lol
cache[key_color] = button_color
end
local button_size = _private.button_size
-- If it is a toggle button, we create an outline instead of a filled shape if it is in off state
-- _private[key_img] = (is_on ~= nil and is_on == false) and
-- shapes.circle_outline(
-- _private[key_color], button_size,
-- _private.button_border_width) or
-- shapes.circle_filled(
-- _private[key_color], button_size)
cache[key_img] = shapes.circle_filled(cache[key_color], button_size)
return cache[key_img]
end
-- Creates a titlebar button widget
function widgets.create_titlebar_button(c, name, button_callback, property)
local button_img = imagebox(nil, false)
if _private.tooltips_enabled then
local tooltip = atooltip {
timer_function = function()
local prop = name
.. (
property
and (c[property] and "_active" or "_inactive")
or ""
)
return _private.tooltip_messages[prop]
end,
delay_show = 0.5,
margins_leftright = 12,
margins_topbottom = 6,
timeout = 0.25,
align = "bottom_right",
}
tooltip:add_to_object(button_img)
end
local is_on, is_focused
local event = "normal"
local function update()
is_focused = c.active
-- If the button is for a property that can be toggled
if property then
is_on = c[property]
button_img.image = widgets.create_button_image(
name,
is_focused,
event,
is_on
)
else
button_img.image = widgets.create_button_image(
name,
is_focused,
event
)
end
end
-- Update the button when the client gains/loses focus
c:connect_signal("unfocus", update)
c:connect_signal("focus", update)
-- If the button is for a property that can be toggled, update it accordingly
if property then
c:connect_signal("property::" .. property, update)
end
-- Update the button on mouse hover/leave
button_img:connect_signal("mouse::enter", function()
event = "hover"
update()
end)
button_img:connect_signal("mouse::leave", function()
event = "normal"
update()
end)
-- The button is updated on both click and release, but the call back is executed on release
button_img.buttons = abutton({}, abutton.names.LEFT, function()
event = "press"
update()
end, function()
if button_callback then
event = "normal"
button_callback()
else
event = "hover"
end
update()
end)
button_img.id = "button_image"
update()
return wibox.widget {
widget = wcontainer_place,
{
widget = wcontainer_margin,
top = _private.button_margin_top
or _private.button_margin_vertical
or _private.button_margin,
bottom = _private.button_margin_bottom
or _private.button_margin_vertical
or _private.button_margin,
left = _private.button_margin_left
or _private.button_margin_horizontal
or _private.button_margin,
right = _private.button_margin_right
or _private.button_margin_horizontal
or _private.button_margin,
{
button_img,
widget = wcontainer_constraint,
height = _private.button_size,
width = _private.button_size,
strategy = "exact",
},
},
}
end
-- Returns a titlebar widget for the given client
function widgets.create_titlebar_title(c)
local client_color = c._nice_base_color
local title_widget = wibox.widget {
align = "center",
ellipsize = "middle",
opacity = c.active and 1 or title_unfocused_opacity,
valign = "center",
widget = textbox,
}
local function update()
local text_color = colors.is_contrast_acceptable(
title_color_light,
client_color
) and title_color_light or title_color_dark
title_widget.markup =
("<span foreground='%s' font='%s'>%s</span>"):format(
text_color,
_private.titlebar_font,
c.name
)
end
c:connect_signal("property::name", update)
c:connect_signal("unfocus", function()
title_widget.opacity = title_unfocused_opacity
end)
c:connect_signal("focus", function()
title_widget.opacity = 1
end)
update()
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 = wcontainer_margin,
top = margin_vertical,
bottom = margin_vertical,
}
end
return widgets