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.

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