-- 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 . -- 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.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 = wibox.container.background },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 = wibox.container.background },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 = wibox.widget.textbox, },options or {}) end end, hseparator = function(style) -- Wow, i guess? return function(options) return awmtk.merge({ widget = wibox.widget.separator, 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 = wibox.widget.separator, 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 = wibox.layout.fixed.horizontal, }, 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 = wibox.container.place, 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 = wibox.container.margin }, 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 = wibox.container.margin, 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 = wibox.container.margin, 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 = wibox.widget.slider },args or {}) end end, checkbox = function(style) return function(args) return awmtk.merge({ color = style.checkbox.bg_focus, padding = 2, shape = style.checkbox.shape, border_width = style.checkbox.shape_border_width, bg = style.checkbox.shape_border_color, widget = wibox.widget.checkbox },args or {}) 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