You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
493 lines
19 KiB
493 lines
19 KiB
-- this file is part of reno desktop.
|
|
--
|
|
-- reno desktop is free software: you can redistribute it and/or modify it under the terms of the gnu general public license as published by the free software foundation, either version 3 of the license, or (at your option) any later version.
|
|
--
|
|
-- reno desktop is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. see the gnu general public license for more details.
|
|
--
|
|
-- you should have received a copy of the gnu general public license along with reno desktop. if not, see <https://www.gnu.org/licenses/>.
|
|
-- renotk (formerly awmtk2) - template/granular styling library for reno
|
|
local wibox = require("wibox")
|
|
local awful = require("awful")
|
|
local gears = require("gears")
|
|
local beautiful = require("beautiful")
|
|
beautiful.widgets = beautiful.widgets or {}
|
|
beautiful.templates = beautiful.templates or {}
|
|
|
|
local awmtk = {}
|
|
|
|
-- {{{ utils
|
|
awmtk.create_delta = function(name,instance_delta,class_delta,parent_delta)
|
|
-- to save memory, we create proxies for lower layers called "deltas"
|
|
-- this function creates that proxy layer using metatables
|
|
-- fun story - i used instance_delta instead of {} at first.
|
|
-- the results were horrifying and confusing.
|
|
return setmetatable({},{
|
|
__index = function(self,k)
|
|
-- per-instance overrides are top priority
|
|
if rawget(instance_delta,k) then
|
|
return rawget(instance_delta,k)
|
|
-- class-wide overrides are second in priority
|
|
elseif type(class_delta[name]) == "table"
|
|
and rawget(class_delta[name],k) then
|
|
return rawget(class_delta[name],k)
|
|
-- parent is fallback
|
|
elseif parent_delta[k] then
|
|
return parent_delta[k]
|
|
end
|
|
end
|
|
})
|
|
end
|
|
|
|
awmtk.create_style = function(name,parent,overrides)
|
|
-- a style is essentially a layer of deltas upon the previous (parent) style
|
|
local new_style = {}
|
|
local odelta = (overrides and overrides[name]) or {}
|
|
local cdelta = beautiful.widgets[name] or {}
|
|
for name,parent_class in pairs(parent) do
|
|
new_style[name] = awmtk.create_delta(
|
|
name,
|
|
odelta,
|
|
cdelta,
|
|
parent_class
|
|
)
|
|
end
|
|
return new_style
|
|
end
|
|
|
|
awmtk.create_template_lib = function(name,parent,overrides)
|
|
-- same thing but beautiful.templates
|
|
return awmtk.create_delta(
|
|
name,
|
|
overrides or {},
|
|
beautiful.templates or {},
|
|
parent
|
|
)
|
|
end
|
|
|
|
awmtk.build_templates = function(templates,style)
|
|
local new_templates = {}
|
|
for name,template in pairs(awmtk.proto_templates) do
|
|
new_templates[name] = templates[name](style)
|
|
end
|
|
return new_templates
|
|
end
|
|
|
|
awmtk.mask_object_call = function(obj,call)
|
|
local new = {}
|
|
for k,v in pairs(obj) do
|
|
new[k] = v
|
|
end
|
|
return setmetatable(new,{
|
|
__index = obj,
|
|
__call = call
|
|
})
|
|
end
|
|
|
|
awmtk.wrap_hooks = function(w,callbacks)
|
|
-- attach hooks to function
|
|
local mcall = (getmetatable(w) and getmetatable(w).__call) or w
|
|
local call_wrapper = function(...)
|
|
if callbacks and callbacks.on_create_pre then
|
|
callbacks.on_create_pre(...)
|
|
end
|
|
local widget = mcall(...)
|
|
if callbacks and callbacks.on_create then
|
|
callbacks.on_create(widget,...)
|
|
end
|
|
if callbacks and callbacks.on_ready then
|
|
callbacks._on_ready_called = false
|
|
local func = function()
|
|
if not callbacks._on_ready_called then
|
|
callbacks.on_ready(widget)
|
|
callbacks._on_ready_called = true
|
|
end
|
|
end
|
|
widget:connect_signal("widget::layout_changed",func)
|
|
widget:connect_signal("widget::layout_changed",function()
|
|
widget:disconnect_signal("widget::layout_changed",func)
|
|
end)
|
|
end
|
|
return widget
|
|
end
|
|
return (getmetatable(w) and awmtk.mask_object_call(w,call_wrapper)) or
|
|
call_wrapper
|
|
end
|
|
|
|
awmtk.merge = gears.table.join
|
|
-- }}}
|
|
|
|
|
|
-- {{{ default style
|
|
|
|
-- prototype style
|
|
-- notice that it's not awmtk.default style - it's used as a base for default.
|
|
awmtk.proto_style = {
|
|
base = setmetatable({
|
|
-- { backgrounds
|
|
-- custom background color for highlighting elements
|
|
bg_highlight = beautiful.bg_highlight or beautiful.bg_focus,
|
|
-- allow more complex themes to define background images
|
|
bgimage_focus = beautiful.bgimage_focus,
|
|
bgimage_normal = beautiful.bgimage_normal,
|
|
-- }
|
|
-- { borders
|
|
-- borders for popups
|
|
shape_border_width = beautiful.shape_border_width or 0,
|
|
shape_border_color = beautiful.shape_border_color or beautiful.bg_normal,
|
|
-- }
|
|
-- { callbacks
|
|
-- a tiny bit more complex thing to account for more extensibility
|
|
-- the stub functions do nothing - you should implement functionality inside theme
|
|
onpress = function() end,
|
|
onrelease = function() end,
|
|
-- }
|
|
-- { shapes
|
|
margins = 5,
|
|
spacing = 5,
|
|
shape = function(cr, width, height)
|
|
return require("gears").shape.rounded_rect(cr,width,height,5)
|
|
end,
|
|
-- }
|
|
},{__index = beautiful})
|
|
}
|
|
|
|
-- subclasses
|
|
awmtk.proto_style.container = awmtk.create_delta("container",{
|
|
},awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.button = awmtk.create_delta("button",{
|
|
margins = 3
|
|
},awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.icon = awmtk.create_delta("icon",{
|
|
margins = 1
|
|
},awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.textbox = awmtk.create_delta("textbox",{
|
|
font = beautiful.font or "sans 8"
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.separator = awmtk.create_delta("separator",{
|
|
thickness = 1,
|
|
color = beautiful.bg_focus,
|
|
border_width = 0
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.article = awmtk.create_delta("article", {
|
|
icon_size = 16,
|
|
small_font = beautiful.small_font or beautiful.font,
|
|
font_align = "left",
|
|
small_font_align = "left"
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.popup = awmtk.create_delta("popup", {
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.titlebar = awmtk.create_delta("titlebar", {
|
|
margins = 1
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.wibar = awmtk.create_delta("wibar", {
|
|
margins = 1
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.menu = awmtk.create_delta("menu", {
|
|
margins = 1
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.center = awmtk.create_delta("center", {
|
|
margins = 1
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.slider = awmtk.create_delta("slider", {
|
|
margins = 1
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
|
|
awmtk.proto_style.checkbox = awmtk.create_delta("checkbox", {
|
|
}, awmtk.proto_style,awmtk.proto_style.base)
|
|
-- }}}
|
|
|
|
-- {{{ generic templates
|
|
awmtk.proto_templates = {
|
|
-- templates are built first using the given style, then applied to contents
|
|
-- through returned function
|
|
container = function(style)
|
|
-- container is practically any "box" that contains buttons, textboxes,
|
|
-- and anything you want to put into the container. do not confuse with
|
|
-- popup - containers are designed to separate contents within a popup.
|
|
return function(layout,options)
|
|
return awmtk.merge({
|
|
{
|
|
layout,
|
|
margins = style.container.margins,
|
|
layout = wibox.container.margin
|
|
},
|
|
bgimage = style.container.bgimage_normal,
|
|
bg = style.container.bg_normal,
|
|
fg = style.container.fg_normal,
|
|
shape = style.container.shape,
|
|
shape_border_color = style.container.shape_border_color,
|
|
shape_border_width = style.container.shape_border_width,
|
|
widget = awmtk.wrap_hooks(wibox.container.background,options)
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
button = function(style)
|
|
-- self explanatory. notice that this does not bear any function -
|
|
-- only the visual part of the button. by design, onpress and onrelease
|
|
-- callbacks should be connected to button events for animations
|
|
return function(layout,options)
|
|
return awmtk.merge({
|
|
{
|
|
layout,
|
|
margins = style.button.margins,
|
|
layout = wibox.container.margin
|
|
},
|
|
bgimage = style.button.bgimage_normal,
|
|
bg = style.button.bg_normal,
|
|
fg = style.button.fg_normal,
|
|
shape = style.button.shape,
|
|
shape_border_color = style.button.shape_border_color,
|
|
shape_border_width = style.button.shape_border_width,
|
|
widget = awmtk.wrap_hooks(wibox.container.background,options)
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
textbox = function(style)
|
|
-- nothing fancy here, but you can set per-widget fonts using beautiful.
|
|
return function(options)
|
|
return awmtk.merge({
|
|
font = style.textbox.font,
|
|
widget = awmtk.wrap_hooks(wibox.widget.textbox,options)
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
hseparator = function(style)
|
|
-- wow, i guess?
|
|
return function(options)
|
|
return awmtk.merge({
|
|
widget = awmtk.wrap_hooks(wibox.widget.separator,options),
|
|
orientation = "horizontal",
|
|
thickness = style.separator.thickness,
|
|
color = style.separator.color,
|
|
border_width = style.separator.border_width
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
vseparator = function(style)
|
|
-- i'm running out of comments
|
|
return function(options)
|
|
return awmtk.merge({
|
|
widget = awmtk.wrap_hooks(wibox.widget.separator,options),
|
|
orientation = "vertical",
|
|
thickness = style.separator.thickness,
|
|
color = style.separator.color,
|
|
border_width = style.separator.border_width
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
article = function(style)
|
|
-- article is a template that combines 3 common pieces of a full item:
|
|
-- icon, name and description. designed to be placed within a container
|
|
-- or a button.
|
|
return function(options)
|
|
return awmtk.merge({
|
|
(options.icon and {
|
|
{
|
|
{
|
|
image = options.icon,
|
|
id = options.icon_id,
|
|
resize = options.resize,
|
|
widget = wibox.widget.imagebox
|
|
},
|
|
strategy = "exact",
|
|
height = options.icon_size or
|
|
style.article.icon_size,
|
|
width = options.icon_size or
|
|
style.article.icon_size,
|
|
widget = wibox.container.constraint
|
|
},
|
|
widget = wibox.container.place,
|
|
valign = "center",
|
|
halign = "center"
|
|
}),
|
|
{
|
|
{
|
|
markup = options.title or "",
|
|
id = options.title_id,
|
|
widget = wibox.widget.textbox,
|
|
font = style.article.font,
|
|
valign = style.article.title_valign or "center",
|
|
align = style.article.title_align or "left"
|
|
},
|
|
(options.description and {
|
|
markup = options.description or "",
|
|
id = options.desc_id,
|
|
widget = wibox.widget.textbox,
|
|
font = style.article.small_font,
|
|
valign = style.article.desc_valign or "center",
|
|
align = style.article.desc_align or "left"
|
|
}),
|
|
spacing = style.article.spacing,
|
|
layout = wibox.layout.flex.vertical
|
|
},
|
|
spacing = style.article.spacing,
|
|
layout = awmtk.wrap_hooks(
|
|
wibox.layout.fixed.horizontal,
|
|
options
|
|
)
|
|
}, options or {})
|
|
end
|
|
end,
|
|
|
|
center = function(style)
|
|
return function(layout,options)
|
|
options = options or {}
|
|
return awmtk.merge({
|
|
{
|
|
layout,
|
|
strategy = "exact",
|
|
height = options.height or
|
|
style.center.height,
|
|
width = options.width or
|
|
style.center.width,
|
|
widget = wibox.container.constraint
|
|
},
|
|
widget = awmtk.wrap_hooks(
|
|
wibox.container.place,
|
|
options
|
|
),
|
|
valign = "center",
|
|
halign = "center"
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
popup = function(style)
|
|
-- popup is a distinct template designed to accomodate the "root" of
|
|
-- a popup, allowing one to add titlebars to popups, for example.
|
|
return function(widget,options)
|
|
return awmtk.merge({
|
|
widget = {
|
|
widget,
|
|
margins = style.popup.margins,
|
|
layout = awmtk.wrap_hooks(wibox.container.margin,options)
|
|
},
|
|
bgimage = style.popup.bgimage_normal,
|
|
shape = style.popup.shape,
|
|
visible = false,
|
|
ontop = true
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
titlebar = function(style)
|
|
-- titlebar is a separate class specifically for window and popup
|
|
-- titlebars. the decision to make it a separate class was due to
|
|
-- the fact that much customization is done through default theme table
|
|
return function(layout,options)
|
|
-- if there's one thing that fascinates me, it's how much weird
|
|
-- bugs i manage to uncover by some sort of miraculous accident.
|
|
-- this one fixes a race condition in margins+(left/right/bottom/top) configuration scenario
|
|
local margins = style.titlebar.margins
|
|
if (style.titlebar.left or
|
|
style.titlebar.right or
|
|
style.titlebar.bottom or
|
|
style.titlebar.top) then
|
|
margins = nil
|
|
end
|
|
return awmtk.merge({
|
|
layout,
|
|
margins = margins,
|
|
layout = awmtk.wrap_hooks(wibox.container.margin,options),
|
|
left = style.titlebar.left,
|
|
right = style.titlebar.right,
|
|
bottom = style.titlebar.bottom,
|
|
top = style.titlebar.top
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
wibar = function(style)
|
|
-- just you regular old wibar, but as a style template.
|
|
return function(layout,options)
|
|
local margins = style.wibar.margins
|
|
if (style.wibar.left or
|
|
style.wibar.right or
|
|
style.wibar.bottom or
|
|
style.wibar.top) then
|
|
margins = nil
|
|
end
|
|
return awmtk.merge({
|
|
layout,
|
|
margins = margins,
|
|
layout = awmtk.wrap_hooks(wibox.container.margin,options),
|
|
left = style.wibar.left,
|
|
right = style.wibar.right,
|
|
bottom = style.wibar.bottom,
|
|
top = style.wibar.top
|
|
},options or {})
|
|
end
|
|
end,
|
|
|
|
slider = function(style)
|
|
-- slider widget but wired to work with the awmtk2 namespace system
|
|
return function(args)
|
|
return awmtk.merge({
|
|
handle_shape = style.slider.handle_shape or style.slider.shape,
|
|
handle_color = style.slider.bg_normal,
|
|
handle_margins = style.slider.handle_margins,
|
|
handle_width = style.slider.handle_width,
|
|
handle_border_color = style.slider.handle_border_color,
|
|
handle_border_width = style.slider.handle_border_width,
|
|
bar_shape = style.slider.bar_shape or style.slider.shape,
|
|
bar_height = style.slider.bar_height,
|
|
bar_color = style.slider.bg_focus,
|
|
bar_margins = style.slider.bar_margins,
|
|
bar_border_width = style.slider.bar_border_width,
|
|
bar_border_color = style.slider.bar_border_color,
|
|
forced_width = style.slider.width,
|
|
forced_height = style.slider.height,
|
|
widget = awmtk.wrap_hooks(wibox.widget.slider,args)
|
|
},args or {})
|
|
end
|
|
end,
|
|
|
|
checkbox = function(style)
|
|
return function(args)
|
|
return awmtk.merge({
|
|
color = style.checkbox.color or style.checkbox.bg_normal,
|
|
paddings = style.checkbox.paddings,
|
|
shape = style.checkbox.shape,
|
|
border_width = style.checkbox.border_width,
|
|
border_color = style.checkbox.border_color or style.checkbox.bg_normal,
|
|
bg = style.checkbox.bg or style.checkbox.bg_focus,
|
|
check_color = style.checkbox.check_color or style.checkbox.bg_normal,
|
|
check_shape = style.checkbox.check_shape,
|
|
widget = awmtk.wrap_hooks(wibox.widget.checkbox,args)
|
|
},args)
|
|
end
|
|
end
|
|
}
|
|
|
|
-- last but not least - we export a default template lib and default style.
|
|
-- this is done in order to allow overriding default style behaviour from theme
|
|
awmtk.default = awmtk.create_style("default",awmtk.proto_style,{})
|
|
awmtk.templates = awmtk.create_template_lib("templates",awmtk.proto_templates,{})
|
|
|
|
-- generic styles for widgets that need them
|
|
awmtk.generic = {}
|
|
awmtk.generic.menu = awmtk.create_style("generic_menu",awmtk.default,{})
|
|
awmtk.generic.button_list = awmtk.create_style("generic_button_list",awmtk.default,{})
|
|
awmtk.generic.iconified_widget = awmtk.create_style("generic_iconified_widget",awmtk.default,{})
|
|
awmtk.generic.status_widget = awmtk.create_style("generic_status_widget",awmtk.default,{})
|
|
awmtk.generic.oneline_widget = awmtk.create_style("generic_oneline_widget",awmtk.default,{})
|
|
awmtk.generic.composite_widget = awmtk.create_style("generic_composite_widget",awmtk.default,{})
|
|
awmtk.generic.popup = awmtk.create_style("generic_popup",awmtk.default,{})
|
|
-- }}}
|
|
return awmtk
|