Reno is the second iteration of the AWMTK-powered AwesomeWM config.
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

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. -- this file is part of reno desktop.
  2. --
  3. -- 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.
  4. --
  5. -- 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.
  6. --
  7. -- you should have received a copy of the gnu general public license along with reno desktop. if not, see <https://www.gnu.org/licenses/>.
  8. -- renotk (formerly awmtk2) - template/granular styling library for reno
  9. local wibox = require("wibox")
  10. local awful = require("awful")
  11. local gears = require("gears")
  12. local beautiful = require("beautiful")
  13. beautiful.widgets = beautiful.widgets or {}
  14. beautiful.templates = beautiful.templates or {}
  15. local awmtk = {}
  16. -- {{{ utils
  17. awmtk.create_delta = function(name,instance_delta,class_delta,parent_delta)
  18. -- to save memory, we create proxies for lower layers called "deltas"
  19. -- this function creates that proxy layer using metatables
  20. -- fun story - i used instance_delta instead of {} at first.
  21. -- the results were horrifying and confusing.
  22. return setmetatable({},{
  23. __index = function(self,k)
  24. -- per-instance overrides are top priority
  25. if rawget(instance_delta,k) then
  26. return rawget(instance_delta,k)
  27. -- class-wide overrides are second in priority
  28. elseif type(class_delta[name]) == "table"
  29. and rawget(class_delta[name],k) then
  30. return rawget(class_delta[name],k)
  31. -- parent is fallback
  32. elseif parent_delta[k] then
  33. return parent_delta[k]
  34. end
  35. end
  36. })
  37. end
  38. awmtk.create_style = function(name,parent,overrides)
  39. -- a style is essentially a layer of deltas upon the previous (parent) style
  40. local new_style = {}
  41. local odelta = (overrides and overrides[name]) or {}
  42. local cdelta = beautiful.widgets[name] or {}
  43. for name,parent_class in pairs(parent) do
  44. new_style[name] = awmtk.create_delta(
  45. name,
  46. odelta,
  47. cdelta,
  48. parent_class
  49. )
  50. end
  51. return new_style
  52. end
  53. awmtk.create_template_lib = function(name,parent,overrides)
  54. -- same thing but beautiful.templates
  55. return awmtk.create_delta(
  56. name,
  57. overrides or {},
  58. beautiful.templates or {},
  59. parent
  60. )
  61. end
  62. awmtk.build_templates = function(templates,style)
  63. local new_templates = {}
  64. for name,template in pairs(awmtk.proto_templates) do
  65. new_templates[name] = templates[name](style)
  66. end
  67. return new_templates
  68. end
  69. awmtk.mask_object_call = function(obj,call)
  70. local new = {}
  71. for k,v in pairs(obj) do
  72. new[k] = v
  73. end
  74. return setmetatable(new,{
  75. __index = obj,
  76. __call = call
  77. })
  78. end
  79. awmtk.wrap_hooks = function(w,callbacks)
  80. -- attach hooks to function
  81. local mcall = (getmetatable(w) and getmetatable(w).__call) or w
  82. local call_wrapper = function(...)
  83. if callbacks and callbacks.on_create_pre then
  84. callbacks.on_create_pre(...)
  85. end
  86. local widget = mcall(...)
  87. if callbacks and callbacks.on_create then
  88. callbacks.on_create(widget,...)
  89. end
  90. if callbacks and callbacks.on_ready then
  91. callbacks._on_ready_called = false
  92. local func = function()
  93. if not callbacks._on_ready_called then
  94. callbacks.on_ready(widget)
  95. callbacks._on_ready_called = true
  96. end
  97. end
  98. widget:connect_signal("widget::layout_changed",func)
  99. widget:connect_signal("widget::layout_changed",function()
  100. widget:disconnect_signal("widget::layout_changed",func)
  101. end)
  102. end
  103. return widget
  104. end
  105. return (getmetatable(w) and awmtk.mask_object_call(w,call_wrapper)) or
  106. call_wrapper
  107. end
  108. awmtk.merge = gears.table.join
  109. -- }}}
  110. -- {{{ default style
  111. -- prototype style
  112. -- notice that it's not awmtk.default style - it's used as a base for default.
  113. awmtk.proto_style = {
  114. base = setmetatable({
  115. -- { backgrounds
  116. -- custom background color for highlighting elements
  117. bg_highlight = beautiful.bg_highlight or beautiful.bg_focus,
  118. -- allow more complex themes to define background images
  119. bgimage_focus = beautiful.bgimage_focus,
  120. bgimage_normal = beautiful.bgimage_normal,
  121. -- }
  122. -- { borders
  123. -- borders for popups
  124. shape_border_width = beautiful.shape_border_width or 0,
  125. shape_border_color = beautiful.shape_border_color or beautiful.bg_normal,
  126. -- }
  127. -- { callbacks
  128. -- a tiny bit more complex thing to account for more extensibility
  129. -- the stub functions do nothing - you should implement functionality inside theme
  130. onpress = function() end,
  131. onrelease = function() end,
  132. -- }
  133. -- { shapes
  134. margins = 5,
  135. spacing = 5,
  136. shape = function(cr, width, height)
  137. return require("gears").shape.rounded_rect(cr,width,height,5)
  138. end,
  139. -- }
  140. },{__index = beautiful})
  141. }
  142. -- subclasses
  143. awmtk.proto_style.container = awmtk.create_delta("container",{
  144. },awmtk.proto_style,awmtk.proto_style.base)
  145. awmtk.proto_style.button = awmtk.create_delta("button",{
  146. margins = 3
  147. },awmtk.proto_style,awmtk.proto_style.base)
  148. awmtk.proto_style.icon = awmtk.create_delta("icon",{
  149. margins = 1
  150. },awmtk.proto_style,awmtk.proto_style.base)
  151. awmtk.proto_style.textbox = awmtk.create_delta("textbox",{
  152. font = beautiful.font or "sans 8"
  153. }, awmtk.proto_style,awmtk.proto_style.base)
  154. awmtk.proto_style.separator = awmtk.create_delta("separator",{
  155. thickness = 1,
  156. color = beautiful.bg_focus,
  157. border_width = 0
  158. }, awmtk.proto_style,awmtk.proto_style.base)
  159. awmtk.proto_style.article = awmtk.create_delta("article", {
  160. icon_size = 16,
  161. small_font = beautiful.small_font or beautiful.font,
  162. font_align = "left",
  163. small_font_align = "left"
  164. }, awmtk.proto_style,awmtk.proto_style.base)
  165. awmtk.proto_style.popup = awmtk.create_delta("popup", {
  166. }, awmtk.proto_style,awmtk.proto_style.base)
  167. awmtk.proto_style.titlebar = awmtk.create_delta("titlebar", {
  168. margins = 1
  169. }, awmtk.proto_style,awmtk.proto_style.base)
  170. awmtk.proto_style.wibar = awmtk.create_delta("wibar", {
  171. margins = 1
  172. }, awmtk.proto_style,awmtk.proto_style.base)
  173. awmtk.proto_style.menu = awmtk.create_delta("menu", {
  174. margins = 1
  175. }, awmtk.proto_style,awmtk.proto_style.base)
  176. awmtk.proto_style.center = awmtk.create_delta("center", {
  177. margins = 1
  178. }, awmtk.proto_style,awmtk.proto_style.base)
  179. awmtk.proto_style.slider = awmtk.create_delta("slider", {
  180. margins = 1
  181. }, awmtk.proto_style,awmtk.proto_style.base)
  182. awmtk.proto_style.checkbox = awmtk.create_delta("checkbox", {
  183. }, awmtk.proto_style,awmtk.proto_style.base)
  184. -- }}}
  185. -- {{{ generic templates
  186. awmtk.proto_templates = {
  187. -- templates are built first using the given style, then applied to contents
  188. -- through returned function
  189. container = function(style)
  190. -- container is practically any "box" that contains buttons, textboxes,
  191. -- and anything you want to put into the container. do not confuse with
  192. -- popup - containers are designed to separate contents within a popup.
  193. return function(layout,options)
  194. return awmtk.merge({
  195. {
  196. layout,
  197. margins = style.container.margins,
  198. layout = wibox.container.margin
  199. },
  200. bgimage = style.container.bgimage_normal,
  201. bg = style.container.bg_normal,
  202. fg = style.container.fg_normal,
  203. shape = style.container.shape,
  204. shape_border_color = style.container.shape_border_color,
  205. shape_border_width = style.container.shape_border_width,
  206. widget = awmtk.wrap_hooks(wibox.container.background,options)
  207. },options or {})
  208. end
  209. end,
  210. button = function(style)
  211. -- self explanatory. notice that this does not bear any function -
  212. -- only the visual part of the button. by design, onpress and onrelease
  213. -- callbacks should be connected to button events for animations
  214. return function(layout,options)
  215. return awmtk.merge({
  216. {
  217. layout,
  218. margins = style.button.margins,
  219. layout = wibox.container.margin
  220. },
  221. bgimage = style.button.bgimage_normal,
  222. bg = style.button.bg_normal,
  223. fg = style.button.fg_normal,
  224. shape = style.button.shape,
  225. shape_border_color = style.button.shape_border_color,
  226. shape_border_width = style.button.shape_border_width,
  227. widget = awmtk.wrap_hooks(wibox.container.background,options)
  228. },options or {})
  229. end
  230. end,
  231. textbox = function(style)
  232. -- nothing fancy here, but you can set per-widget fonts using beautiful.
  233. return function(options)
  234. return awmtk.merge({
  235. font = style.textbox.font,
  236. widget = awmtk.wrap_hooks(wibox.widget.textbox,options)
  237. },options or {})
  238. end
  239. end,
  240. hseparator = function(style)
  241. -- wow, i guess?
  242. return function(options)
  243. return awmtk.merge({
  244. widget = awmtk.wrap_hooks(wibox.widget.separator,options),
  245. orientation = "horizontal",
  246. thickness = style.separator.thickness,
  247. color = style.separator.color,
  248. border_width = style.separator.border_width
  249. },options or {})
  250. end
  251. end,
  252. vseparator = function(style)
  253. -- i'm running out of comments
  254. return function(options)
  255. return awmtk.merge({
  256. widget = awmtk.wrap_hooks(wibox.widget.separator,options),
  257. orientation = "vertical",
  258. thickness = style.separator.thickness,
  259. color = style.separator.color,
  260. border_width = style.separator.border_width
  261. },options or {})
  262. end
  263. end,
  264. article = function(style)
  265. -- article is a template that combines 3 common pieces of a full item:
  266. -- icon, name and description. designed to be placed within a container
  267. -- or a button.
  268. return function(options)
  269. return awmtk.merge({
  270. (options.icon and {
  271. {
  272. {
  273. image = options.icon,
  274. id = options.icon_id,
  275. resize = options.resize,
  276. widget = wibox.widget.imagebox
  277. },
  278. strategy = "exact",
  279. height = options.icon_size or
  280. style.article.icon_size,
  281. width = options.icon_size or
  282. style.article.icon_size,
  283. widget = wibox.container.constraint
  284. },
  285. widget = wibox.container.place,
  286. valign = "center",
  287. halign = "center"
  288. }),
  289. {
  290. {
  291. markup = options.title or "",
  292. id = options.title_id,
  293. widget = wibox.widget.textbox,
  294. font = style.article.font,
  295. valign = style.article.title_valign or "center",
  296. align = style.article.title_align or "left"
  297. },
  298. (options.description and {
  299. markup = options.description or "",
  300. id = options.desc_id,
  301. widget = wibox.widget.textbox,
  302. font = style.article.small_font,
  303. valign = style.article.desc_valign or "center",
  304. align = style.article.desc_align or "left"
  305. }),
  306. spacing = style.article.spacing,
  307. layout = wibox.layout.flex.vertical
  308. },
  309. spacing = style.article.spacing,
  310. layout = awmtk.wrap_hooks(
  311. wibox.layout.fixed.horizontal,
  312. options
  313. )
  314. }, options or {})
  315. end
  316. end,
  317. center = function(style)
  318. return function(layout,options)
  319. options = options or {}
  320. return awmtk.merge({
  321. {
  322. layout,
  323. strategy = "exact",
  324. height = options.height or
  325. style.center.height,
  326. width = options.width or
  327. style.center.width,
  328. widget = wibox.container.constraint
  329. },
  330. widget = awmtk.wrap_hooks(
  331. wibox.container.place,
  332. options
  333. ),
  334. valign = "center",
  335. halign = "center"
  336. },options or {})
  337. end
  338. end,
  339. popup = function(style)
  340. -- popup is a distinct template designed to accomodate the "root" of
  341. -- a popup, allowing one to add titlebars to popups, for example.
  342. return function(widget,options)
  343. return awmtk.merge({
  344. widget = {
  345. widget,
  346. margins = style.popup.margins,
  347. layout = awmtk.wrap_hooks(wibox.container.margin,options)
  348. },
  349. bgimage = style.popup.bgimage_normal,
  350. shape = style.popup.shape,
  351. visible = false,
  352. ontop = true
  353. },options or {})
  354. end
  355. end,
  356. titlebar = function(style)
  357. -- titlebar is a separate class specifically for window and popup
  358. -- titlebars. the decision to make it a separate class was due to
  359. -- the fact that much customization is done through default theme table
  360. return function(layout,options)
  361. -- if there's one thing that fascinates me, it's how much weird
  362. -- bugs i manage to uncover by some sort of miraculous accident.
  363. -- this one fixes a race condition in margins+(left/right/bottom/top) configuration scenario
  364. local margins = style.titlebar.margins
  365. if (style.titlebar.left or
  366. style.titlebar.right or
  367. style.titlebar.bottom or
  368. style.titlebar.top) then
  369. margins = nil
  370. end
  371. return awmtk.merge({
  372. layout,
  373. margins = margins,
  374. layout = awmtk.wrap_hooks(wibox.container.margin,options),
  375. left = style.titlebar.left,
  376. right = style.titlebar.right,
  377. bottom = style.titlebar.bottom,
  378. top = style.titlebar.top
  379. },options or {})
  380. end
  381. end,
  382. wibar = function(style)
  383. -- just you regular old wibar, but as a style template.
  384. return function(layout,options)
  385. local margins = style.wibar.margins
  386. if (style.wibar.left or
  387. style.wibar.right or
  388. style.wibar.bottom or
  389. style.wibar.top) then
  390. margins = nil
  391. end
  392. return awmtk.merge({
  393. layout,
  394. margins = margins,
  395. layout = awmtk.wrap_hooks(wibox.container.margin,options),
  396. left = style.wibar.left,
  397. right = style.wibar.right,
  398. bottom = style.wibar.bottom,
  399. top = style.wibar.top
  400. },options or {})
  401. end
  402. end,
  403. slider = function(style)
  404. -- slider widget but wired to work with the awmtk2 namespace system
  405. return function(args)
  406. return awmtk.merge({
  407. handle_shape = style.slider.handle_shape or style.slider.shape,
  408. handle_color = style.slider.bg_normal,
  409. handle_margins = style.slider.handle_margins,
  410. handle_width = style.slider.handle_width,
  411. handle_border_color = style.slider.handle_border_color,
  412. handle_border_width = style.slider.handle_border_width,
  413. bar_shape = style.slider.bar_shape or style.slider.shape,
  414. bar_height = style.slider.bar_height,
  415. bar_color = style.slider.bg_focus,
  416. bar_margins = style.slider.bar_margins,
  417. bar_border_width = style.slider.bar_border_width,
  418. bar_border_color = style.slider.bar_border_color,
  419. forced_width = style.slider.width,
  420. forced_height = style.slider.height,
  421. widget = awmtk.wrap_hooks(wibox.widget.slider,args)
  422. },args or {})
  423. end
  424. end,
  425. checkbox = function(style)
  426. return function(args)
  427. return awmtk.merge({
  428. color = style.checkbox.color or style.checkbox.bg_normal,
  429. paddings = style.checkbox.paddings,
  430. shape = style.checkbox.shape,
  431. border_width = style.checkbox.border_width,
  432. border_color = style.checkbox.border_color or style.checkbox.bg_normal,
  433. bg = style.checkbox.bg or style.checkbox.bg_focus,
  434. check_color = style.checkbox.check_color or style.checkbox.bg_normal,
  435. check_shape = style.checkbox.check_shape,
  436. widget = awmtk.wrap_hooks(wibox.widget.checkbox,args)
  437. },args)
  438. end
  439. end
  440. }
  441. -- last but not least - we export a default template lib and default style.
  442. -- this is done in order to allow overriding default style behaviour from theme
  443. awmtk.default = awmtk.create_style("default",awmtk.proto_style,{})
  444. awmtk.templates = awmtk.create_template_lib("templates",awmtk.proto_templates,{})
  445. -- generic styles for widgets that need them
  446. awmtk.generic = {}
  447. awmtk.generic.menu = awmtk.create_style("generic_menu",awmtk.default,{})
  448. awmtk.generic.button_list = awmtk.create_style("generic_button_list",awmtk.default,{})
  449. awmtk.generic.iconified_widget = awmtk.create_style("generic_iconified_widget",awmtk.default,{})
  450. awmtk.generic.status_widget = awmtk.create_style("generic_status_widget",awmtk.default,{})
  451. awmtk.generic.oneline_widget = awmtk.create_style("generic_oneline_widget",awmtk.default,{})
  452. awmtk.generic.composite_widget = awmtk.create_style("generic_composite_widget",awmtk.default,{})
  453. awmtk.generic.popup = awmtk.create_style("generic_popup",awmtk.default,{})
  454. -- }}}
  455. return awmtk