From 5255cca8034d5293d15d5f30042a711f2d421508 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Wed, 5 Mar 2014 00:12:48 -0500 Subject: [PATCH] Change object syntax to user get_ and set_ prefix instead of get_map/set_map mappers (issue #15) --- README.md | 98 ++++++++++++++++++++++++++++++++++++++ bar.lua | 16 +++---- base.lua | 47 +++++++++--------- context.lua | 28 +++++------ embed.lua | 12 ++--- item/init.lua | 10 ++-- item/layout/horizontal.lua | 16 +++---- item/layout/icon.lua | 4 +- layout/horizontal.lua | 6 +-- layout/vertical.lua | 4 +- object.lua | 19 ++++---- radial.lua | 18 +++---- 12 files changed, 187 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index f047f6f..8886797 100644 --- a/README.md +++ b/README.md @@ -308,3 +308,101 @@ allow masks such as desaturation, tinting, invert or some matrix to be applied on the pixmap before it is being drawn. This function take the path/surface as only parameter and return the transformed surface. +## Extending Radical + +Radical is not designed to be used "as is". Every menus are different. While +common ones can be created without extending Radical capabilities, more advanced +one most likely will. Good news, this is what Radical have been designed for. +The previous generations proved to me that any lack or native extensibility +will cause the code to bloat when it come to adding a feature. Radical horizontal +design allow to add more modules and properties without having to touch the "core" +files. + +### Object model + +The Radical object model is similar to the Awesome one. Each objects have a set +of signals developers can listen to to have changes notification. The big +difference is that Radical object model automatically generate the properties +themselves. If one desire to add a new one, it is possible to listen to `item::added` +to apply it on the item or apply it directly on the menu itself depending if the +property is for the menu or for an item. Here is an example how it work: + +```lua + local menu = radical.context{} + + -- Create the setter + menu.set_foo = function(m,value) + print("Setting value to:",value) + m._foo_real = value + end + + -- Create the getter + menu.get_foo = function(m) + print("Getter called, returning",m._foo_real) + end + + -- The property is now created, this will call the setter: + menu.foo = "my foo value" + + -- This will call the getter: + print(menu.foo) + + -- The signals will be automatically generated + data:connect_signal("foo::changed",function(m,value) + print("foo changed:",value) + end) + + -- New signals don't need to be registered and can be called right away + data:connect_signal("my_new_signal::action_name",function(m,value1,value2,value3) + print("Callback",m,value1,value2,value3) + end) + + -- Manually emiting a signal + menu:emit_signal("my_new_signal::action_name",value1,value2,value3) + +``` + +### State model + +Radical support multiple states per item at once. The "current state" is the one +with the smallest ID. A state ID is an integer from -inf to inf. More important +states, like `urgent` versus `checked` can be implemented by using an +appropriate ordering. The default set of states is subject to changes, so it +is wiser to use a completely different range if someone want to replace the +currents one. Each states can be assigned a background and foreground color +using the `radical.theme.register_color(id, radical_name, beautiful_name, true )` +method. Toggling a state can be done using the `item.state[]` meta table: + +```lua + local my_state_name = 9999 -- <== The ID + local menu = radical.context{} + local item = menu:add_item{text="text"} + + -- Activate a state + item.state[my_state_name] = true + + -- Desactivate a state + item.state[my_state_name] = nil + +``` + +Radical will take care of choosing the current state and redraw the item with +the right background and foreground colors. + +### Layout + +TODO + +### Style + +TODO + +### Item layout + +TODO + +### Item style + +TODO + + diff --git a/bar.lua b/bar.lua index 1e556cf..88a8238 100644 --- a/bar.lua +++ b/bar.lua @@ -35,7 +35,7 @@ end local function setup_drawable(data) local internal = data._internal - local get_map,set_map,private_data = internal.get_map,internal.set_map,internal.private_data + local private_data = internal.private_data --Init internal.margin = wibox.layout.margin() @@ -46,13 +46,13 @@ local function setup_drawable(data) internal.margin:set_widget(internal.layout) --Getters - get_map.x = function() return 0 end - get_map.y = function() return 0 end - get_map.width = function() return internal.margin.fix(internal.margin,9999,99) end - get_map.height = function() return beautiful.default_height end - get_map.visible = function() return true end - get_map.direction = function() return "left" end - get_map.margins = function() return {left=0,right=0,top=0,bottom=0} end + data.get_x = function() return 0 end + data.get_y = function() return 0 end + data.get_width = function() return internal.margin.fix(internal.margin,9999,99) end + data.get_height = function() return beautiful.default_height end + data.get_visible = function() return true end + data.get_direction = function() return "left" end + data.get_margins = function() return {left=0,right=0,top=0,bottom=0} end -- This widget do not use wibox, so setup correct widget interface data.fit = internal.margin.fit diff --git a/base.lua b/base.lua index 318b8bd..4e9a50e 100644 --- a/base.lua +++ b/base.lua @@ -166,6 +166,7 @@ local function add_item(data,args) item.selected = true end item.index = data.rowcount + data:emit_signal("item::added",item) return item end @@ -179,7 +180,7 @@ local function add_widget(data,widget,args) return args.width or w,args.height or h end - local item,set_map,get_map,private_data = object({ + local item,private_data = object({ private_data = { widget = widget, selected = false, @@ -188,15 +189,13 @@ local function add_widget(data,widget,args) visible = true, selected = true, }, - get_map = { - y = function() return (args.y and args.y >= 0) and args.y or data.height - (data.margins.top or data.border_width) - data.item_height end, --Hack around missing :fit call for last item - }, autogen_getmap = true, autogen_setmap = true, autogen_signals = true, }) item._private_data = private_data - item._internal = {get_map=get_map,set_map=set_map} + item._internal = {} + item.get_y = function() return (args.y and args.y >= 0) and args.y or data.height - (data.margins.top or data.border_width) - data.item_height end --Hack around missing :fit call for last item data._internal.widgets[#data._internal.widgets+1] = item data._internal.items[#data._internal.items+1] = {item} @@ -228,7 +227,7 @@ local function new(args) if not internal.widgets then internal.widgets = {} end -- All the magic in the universe - local data,set_map,get_map,private_data = object({ + local data,private_data = object({ private_data = { -- Default settings bg = args.bg or beautiful.menu_bg_normal or beautiful.bg_normal or "#000000", @@ -280,16 +279,6 @@ local function new(args) opacity = args.opacity or beautiful.menu_opacity or 1, icon_transformation = args.icon_transformation or nil, }, - get_map = { - is_menu = function() return true end, - margin = function() return {left=0,bottom=0,right=0,left=0} end, - items = function() return internal.items end, - rowcount = function() return #internal.items end, - columncount = function() return (#internal.items > 0) and #(internal.items[1]) or 0 end, - }, - set_map = { - auto_resize = function(val) private_data[""] = val end, - }, force_private = { parent = true, visible = true, @@ -302,10 +291,20 @@ local function new(args) autogen_setmap = true, autogen_signals = true, }) - internal.get_map,internal.set_map,internal.private_data = get_map,set_map,private_data + internal.private_data = private_data data.add_item,data.add_widget,data.add_embeded_menu,data._internal,data.add_key_binding = add_item,add_widget,add_embeded_menu,internal,add_key_binding theme.setup_colors(data,args) - set_map.parent_geometry = function(value) + + -- Getters + data.get_is_menu = function(_) return true end + data.get_margin = function(_) return {left=0,bottom=0,right=0,left=0} end + data.get_items = function(_) return internal.items end + data.get_rowcount = function(_) return #internal.items end + data.get_columncount = function(_) return (#internal.items > 0) and #(internal.items[1]) or 0 end + + -- Setters + data.set_auto_resize = function(_,val) private_data[""] = val end + data.set_parent_geometry = function(_,value) private_data.parent_geometry = value if data._internal.get_direction then data.direction = data._internal.get_direction(data) @@ -315,7 +314,7 @@ local function new(args) end end - set_map.visible = function(value) + data.set_visible = function(_,value) private_data.visible = value if value then local fit_w,fit_h = data._internal.layout:fit(9999,9999) @@ -342,8 +341,8 @@ local function new(args) capi.keygrabber.stop() end end - - set_map.layout = function(value) + + data.set_layout = function(_,value) if value then value:setup_key_hooks(data) end @@ -356,7 +355,7 @@ local function new(args) -- end -- end - get_map.current_index = function() + data.get_current_index = function(_) if data._current_item then for k,v in ipairs(internal.items) do --rows for k2,v2 in ipairs(v) do --columns @@ -368,14 +367,14 @@ local function new(args) end end - get_map.previous_item = function() + data.get_previous_item = function(_) local candidate,idx = internal.items[(data.current_index or 0)-1],(data.current_index or 0)-1 while candidate and (candidate[1]._hidden or candidate[1]._filter_out) and idx > 0 do candidate,idx = internal.items[idx - 1],idx-1 end return (candidate or internal.items[data.rowcount])[1] end - get_map.next_item = function() + data.get_next_item = function(_) local candidate,idx = internal.items[(data.current_index or 0)+1],(data.current_index or 0)+1 while candidate and (candidate[1]._hidden or candidate[1]._filter_out) and idx <= data.rowcount do candidate,idx = internal.items[idx + 1],idx+1 diff --git a/context.lua b/context.lua index 2f1b130..4f23774 100644 --- a/context.lua +++ b/context.lua @@ -100,7 +100,7 @@ end local function setup_drawable(data) local internal = data._internal - local get_map,set_map,private_data = internal.get_map,internal.set_map,internal.private_data + local private_data = internal.private_data --Init internal.w = wibox({}) @@ -117,14 +117,14 @@ local function setup_drawable(data) internal.w.opacity = data.opacity --Getters - get_map.wibox = function() return internal.w end - get_map.x = function() return internal.w.x end - get_map.y = function() return internal.w.y end - get_map.width = function() return internal.w.width end - get_map.height = function() return internal.w.height end - get_map.visible = function() return private_data.visible end - get_map.direction = function() return private_data.direction end - get_map.margins = function() + data.get_wibox = function() return internal.w end + data.get_x = function() return internal.w.x end + data.get_y = function() return internal.w.y end + data.get_width = function() return internal.w.width end + data.get_height = function() return internal.w.height end + data.get_visible = function() return private_data.visible end + data.get_direction = function() return private_data.direction end + data.get_margins = function() local ret = {left=data.border_width,right=data.border_width,top=data.style.margins.TOP,bottom=data.style.margins.BOTTOM} if data.arrow_type ~= base.arrow_type.NONE then ret[data.direction] = ret[data.direction]+13 @@ -133,7 +133,7 @@ local function setup_drawable(data) end --Setters - set_map.direction = function(value) + data.set_direction = function(_,value) if private_data.direction ~= value and (value == "top" or value == "bottom" or value == "left" or value == "right") then private_data.direction = value local fit_w,fit_h = internal.layout:fit() @@ -141,9 +141,9 @@ local function setup_drawable(data) data.width = fit_w end end - set_map.x = function(value) internal.w.x = value end - set_map.y = function(value) internal.w.y = value end - set_map.width = function(value) + data.set_x = function(_,value) internal.w.x = value end + data.set_y = function(_,value) internal.w.y = value end + data.set_width = function(_,value) local need_update = internal.w.width == (value + 2*data.border_width) local margins = data.margins internal.w.width = value + data.margins.left + data.margins.right @@ -151,7 +151,7 @@ local function setup_drawable(data) data.style(data) end end - set_map.height = function(value) + data.set_height = function(_,value) local margins = data.margins local need_update = (internal.w.height ~= (value + margins.top + margins.bottom)) local new_height = (value + margins.top + margins.bottom) or 1 diff --git a/embed.lua b/embed.lua index 8778534..8ee6965 100644 --- a/embed.lua +++ b/embed.lua @@ -19,16 +19,16 @@ local capi,module = { mouse = mouse , screen = screen , keygrabber = keygrabber local function setup_drawable(data) local internal = data._internal - local get_map,set_map,private_data = internal.get_map,internal.set_map,internal.private_data + local private_data = internal.private_data -- An embeded menu can only be visible if the parent is - get_map.visible = function() return data._embeded_parent and data._embeded_parent.visible or false end --Let the parent handle that - set_map.visible = function(v) if data._embeded_parent then data._embeded_parent.visible = v end end + data.get_visible = function() return data._embeded_parent and data._embeded_parent.visible or false end --Let the parent handle that + data.set_visible = function(_,v) if data._embeded_parent then data._embeded_parent.visible = v end end -- Enumate geometry --BUG this is fake, but better than nothing - get_map.width = function() return data._embeded_parent and data._embeded_parent.width end - get_map.y = function() return data._embeded_parent and data._embeded_parent.y end - get_map.x = function() return data._embeded_parent and data._embeded_parent.x end + data.get_width = function() return data._embeded_parent and data._embeded_parent.width end + data.get_y = function() return data._embeded_parent and data._embeded_parent.y end + data.get_x = function() return data._embeded_parent and data._embeded_parent.x end if not data.layout then data.layout = layout.vertical end diff --git a/item/init.lua b/item/init.lua index 34a1314..e51c34f 100644 --- a/item/init.lua +++ b/item/init.lua @@ -75,7 +75,7 @@ end local function new_item(data,args) local args = args or {} - local item,set_map,get_map,private_data = object({ + local item,private_data = object({ private_data = { text = args.text or "" , height = args.height or data.item_height or beautiful.menu_height or 30 , @@ -103,16 +103,14 @@ local function new_item(data,args) selected = true, index = true, }, - get_map = { - y = function() return (args.y and args.y >= 0) and args.y or data.height - (data.margins.top or data.border_width) - data.item_height end, --Hack around missing :fit call for last item - }, autogen_getmap = true, autogen_setmap = true, autogen_signals = true, }) item._private_data = private_data - item._internal = {get_map=get_map,set_map=set_map} + item._internal = {} theme.setup_item_colors(data,item,args) + item.get_y = function() return (args.y and args.y >= 0) and args.y or data.height - (data.margins.top or data.border_width) - data.item_height end --Hack around missing :fit call for last item item.get_bg = function() return data.bg end @@ -138,7 +136,7 @@ local function new_item(data,args) data._internal.items[#data._internal.items][1] = item -- Setters - set_map.selected = function(value) + item.set_selected = function(_,value) private_data.selected = value if value == false then data.item_style(item,{}) diff --git a/item/layout/horizontal.lua b/item/layout/horizontal.lua index 62e19d2..bc12621 100644 --- a/item/layout/horizontal.lua +++ b/item/layout/horizontal.lua @@ -12,7 +12,7 @@ local module = {} -- Add [F1], [F2] ... to items function module:setup_fkey(item,data) - item._internal.set_map.f_key = function(value) + item.set_f_key = function(_,value) item._internal.has_changed = true item._internal.f_key = value data:remove_key_hook("F"..value) @@ -21,7 +21,7 @@ function module:setup_fkey(item,data) data.visible = false end) end - item._internal.get_map.f_key = function() return item._internal.f_key end + item.get_f_key = function() return item._internal.f_key end end -- Like an overlay, but under @@ -55,7 +55,7 @@ function module:setup_icon(item,data) icon:set_image(item.icon) end - item._internal.set_map.icon = function (value) + item.set_icon = function (_,value) icon:set_image(value) end return icon @@ -64,7 +64,7 @@ end -- Show the checkbox function module:setup_checked(item,data) if item.checkable then - item._internal.get_map.checked = function() + item.get_checked = function() if type(item._private_data.checked) == "function" then return item._private_data.checked() else @@ -73,7 +73,7 @@ function module:setup_checked(item,data) end local ck = wibox.widget.imagebox() ck:set_image(item.checked and checkbox.checked() or checkbox.unchecked()) - item._internal.set_map.checked = function (value) + item.set_checked = function (_,value) item._private_data.checked = value ck:set_image(item.checked and checkbox.checked() or checkbox.unchecked()) item._internal.has_changed = true @@ -84,7 +84,7 @@ end -- Setup hover function module:setup_hover(item,data) - item._internal.set_map.hover = function(value) + item.set_hover = function(_,value) local item_style = item.item_style or data.item_style item.state[5] = value and true or nil item_style(item,{}) @@ -197,7 +197,7 @@ local function create_item(item,data,args) wibox.widget.textbox.draw(self,w, cr, width, height) end tb:set_text(item.text) - item._internal.set_map.text = function (value) + item.set_text = function (_,value) if data.disable_markup then tb:set_text(value) else @@ -245,7 +245,7 @@ local function create_item(item,data,args) item.widget:set_tooltip(item.tooltip) -- Overlay - item._internal.set_map.overlay = function(value) + item.set_overlay = function(_,value) item._private_data.overlay = value item.widget:emit_signal("widget::updated") end diff --git a/item/layout/icon.lua b/item/layout/icon.lua index 07c26fa..3778307 100644 --- a/item/layout/icon.lua +++ b/item/layout/icon.lua @@ -74,7 +74,7 @@ local function create_item(item,data,args) end end if item.checkable then - item._internal.get_map.checked = function() + item.get_checked = function() if type(item._private_data.checked) == "function" then return item._private_data.checked() else @@ -84,7 +84,7 @@ local function create_item(item,data,args) local ck = wibox.widget.imagebox() ck:set_image(item.checked and checkbox.checked() or checkbox.unchecked()) lr:add(ck) - item._internal.set_map.checked = function (value) + item.set_checked = function (_,value) item._private_data.checked = value ck:set_image(item.checked and checkbox.checked() or checkbox.unchecked()) end diff --git a/layout/horizontal.lua b/layout/horizontal.lua index 5704dea..3148449 100644 --- a/layout/horizontal.lua +++ b/layout/horizontal.lua @@ -98,7 +98,7 @@ function module:setup_item(data,item,args) local icon_w = item._internal.icon_w -- Setup text - item._internal.set_map.text = function (value) + item.set_text = function (_,value) if data.disable_markup then text_w:set_text(value) else @@ -114,10 +114,10 @@ function module:setup_item(data,item,args) end end end - item._internal.set_map.icon = function (value) + item.set_icon = function (_,value) icon_w:set_image(value) end - item._internal.set_map.text(item._private_data.text) + item:set_text(item._private_data.text) -- Setup tooltip bg:set_tooltip(item.tooltip) diff --git a/layout/vertical.lua b/layout/vertical.lua index 42b9df9..353e704 100644 --- a/layout/vertical.lua +++ b/layout/vertical.lua @@ -92,7 +92,7 @@ function module:setup_text(item,data,text_w) end text_w.fit = function(self,width,height) return width,height end - item._internal.set_map.text = function (value) + item.set_text = function (_,value) if data.disable_markup then text_w:set_text(value) else @@ -114,7 +114,7 @@ function module:setup_text(item,data,text_w) -- end end end - item._internal.set_map.text(item._private_data.text) + item:set_text(item._private_data.text) return text_w end diff --git a/object.lua b/object.lua index b5b24d2..6bb686c 100644 --- a/object.lua +++ b/object.lua @@ -6,14 +6,14 @@ local pairs = pairs local function setup_object(args) local data,args,private_data,signals = {},args or {},private_data or {},{} - local get_map,set_map,private_data = args.get_map or {},args.set_map or {},args.private_data or {} + local private_data = args.private_data or {} function data:connect_signal(name,func) signals[name] = signals[name] or {} table.insert(signals[name],func) end - function data:remove_signal(name,func) + function data:disconnect_signal(name,func) for k,v in pairs(signals[name] or {}) do if v == func then signals[name][k] = nil @@ -33,8 +33,8 @@ local function setup_object(args) end local function return_data(tab, key) - if get_map[key] ~= nil then - return get_map[key]() + if rawget(tab,"get_"..key) then + return rawget(tab,"get_"..key)(tab) elseif args.autogen_getmap == true and private_data[key] ~= nil then return private_data[key] elseif args.other_get_callback then @@ -51,15 +51,16 @@ local function setup_object(args) end local function catch_changes(tab, key,value) - if set_map[key] == false then + local setter = rawget(tab,"set_"..key) + if setter == false then --print("This is not a setter",debug.traceback()) --In some case, it may be called "normally", having this print is only good for debug - elseif (data[key] ~= value or (args.always_handle ~= nil and args.always_handle[key] == true)) and set_map[key] ~= nil then - set_map[key](value) + elseif (data[key] ~= value or (args.always_handle ~= nil and args.always_handle[key] == true)) and setter then + setter(tab,value) auto_signal(key) elseif (args.force_private or {})[key] == true or (args.autogen_setmap and (private_data[key] ~= nil)) then private_data[key] = value auto_signal(key) - elseif set_map[key] == nil then + elseif setter == nil then rawset(data,key,value) end if args.auto_signal_changed == true then @@ -68,6 +69,6 @@ local function setup_object(args) end setmetatable(data, { __index = return_data, __newindex = catch_changes, __len = function() return #data + #private_data end, }) - return data,set_map,get_map,private_data + return data,private_data end return setmetatable({}, { __call = function(_, ...) return setup_object(...) end }) \ No newline at end of file diff --git a/radial.lua b/radial.lua index a6bd82b..01f41f5 100644 --- a/radial.lua +++ b/radial.lua @@ -276,7 +276,7 @@ end local function setup_drawable(data) local internal = data._internal - local get_map,set_map,private_data = internal.get_map,internal.set_map,internal.private_data + local private_data = internal.private_data --Init -- internal.w = wibox({}) @@ -288,14 +288,14 @@ local function setup_drawable(data) internal.margin:set_widget(internal.layout) --Getters - get_map.wibox = function() return nil end -- Will this break? - get_map.x = function() return 0 end - get_map.y = function() return 0 end - get_map.width = function() return 500 end - get_map.height = function() return 40 end - get_map.visible = function() return private_data.visible end - get_map.direction = function() return private_data.direction end - get_map.margins = function() + data.get_wibox = function() return nil end -- Will this break? + data.get_x = function() return 0 end + data.get_y = function() return 0 end + data.get_width = function() return 500 end + data.get_height = function() return 40 end + data.get_visible = function() return private_data.visible end + data.get_direction = function() return private_data.direction end + data.get_margins = function() local ret = {left=data.border_width,right=data.border_width,top=data.style.margins.TOP,bottom=data.style.margins.BOTTOM} if data.arrow_type ~= base.arrow_type.NONE then ret[data.direction] = ret[data.direction]+13