Rewrite the view layer
Radical is a model-view framework. There is a common API to set data, then view modules to display it. Those modules have been rewritten on top of new APIs. Not all features are yet re-integrated, most are. Future commits will address the various regressions and missing features. This time, the code should be maintainable. The whole separation of concerns analysis has been re-done. Allowing code de-duplication and generalization. The API remain the same, only `set_menu` has changed a little to allow merging the 2 sub-menu code paths.
This commit is contained in:
parent
1711d28dcd
commit
f239ee9a24
217
bar.lua
217
bar.lua
|
@ -1,202 +1,61 @@
|
|||
local setmetatable,unpack,table = setmetatable,unpack,table
|
||||
local base = require( "radical.base" )
|
||||
local color = require( "gears.color" )
|
||||
local wibox = require( "wibox" )
|
||||
local beautiful = require( "beautiful" )
|
||||
local cairo = require( "lgi" ).cairo
|
||||
local awful = require( "awful" )
|
||||
local util = require( "awful.util" )
|
||||
local fkey = require( "radical.widgets.fkey" )
|
||||
local button = require( "awful.button" )
|
||||
local checkbox = require( "radical.widgets.checkbox" )
|
||||
local base = require( "radical.base" )
|
||||
local color = require( "gears.color" )
|
||||
local wibox = require( "wibox" )
|
||||
local beautiful = require( "beautiful" )
|
||||
local item_style = require( "radical.item.style.arrow_single" )
|
||||
-- local vertical = require( "radical.layout.vertical" )
|
||||
local item_layout= require( "radical.item.layout.horizontal" )
|
||||
local margins2 = require("radical.margins" )
|
||||
local item_layout= require( "radical.item.layout.horizontal" )
|
||||
local common = require( "radical.common" )
|
||||
|
||||
local capi,module = { mouse = mouse , screen = screen, keygrabber = keygrabber },{}
|
||||
|
||||
local function get_direction(data)
|
||||
return "left" -- Nothing to do
|
||||
end
|
||||
|
||||
local function set_position(self)
|
||||
return --Nothing to do
|
||||
end
|
||||
|
||||
-- Draw the menu background
|
||||
local function bg_draw(self, context, cr, width, height)--w, cr, width, height)
|
||||
cr:save()
|
||||
cr:set_source(color(self._data.bg))
|
||||
cr:rectangle(0,0,width,height)
|
||||
cr:fill()
|
||||
cr:restore()
|
||||
end
|
||||
|
||||
local function proxy_draw(_,...)
|
||||
local l = _._internal and _._internal.layout or _
|
||||
return wibox.layout.fixed.draw(l,...)
|
||||
end
|
||||
|
||||
local function proxy_fit(_,...)
|
||||
local l = _._internal and _._internal.layout or _
|
||||
return wibox.layout.fixed.fit(l,...)
|
||||
end
|
||||
local module = {}
|
||||
|
||||
local function setup_drawable(data)
|
||||
local internal = data._internal
|
||||
local private_data = internal.private_data
|
||||
local internal = data._internal
|
||||
|
||||
internal.layout = internal.layout_func or wibox.layout.fixed.horizontal()
|
||||
internal.layout = internal.layout_func or wibox.layout.fixed.horizontal()
|
||||
internal.margin = wibox.layout.margin(internal.layout)
|
||||
internal.layout._data = data
|
||||
|
||||
--internal.layout.draw = bg_draw --TODO Dead code?
|
||||
|
||||
internal.layout._data = data
|
||||
|
||||
if internal.layout.set_spacing and data.spacing then
|
||||
internal.layout:set_spacing(data.spacing)
|
||||
end
|
||||
|
||||
--Getters
|
||||
data.get_x = function() return 0 end
|
||||
data.get_y = function() return 0 end
|
||||
data.get_width = function() return internal.layout:get_preferred_size() end
|
||||
data.get_height = function() return beautiful.default_height end
|
||||
data.get_visible = function() return true end
|
||||
data.get_direction = function() return "left" end
|
||||
|
||||
-- Setup the margins
|
||||
local m = wibox.layout.margin()
|
||||
m:set_widget(internal.layout)
|
||||
internal.margin = m
|
||||
local mrgns = margins2(m,data.default_margins or {})
|
||||
data._internal.mrgns = mrgns
|
||||
data.get_margins = function()
|
||||
return data._internal.mrgns or {}
|
||||
end
|
||||
|
||||
if data.style then
|
||||
data.style(data)
|
||||
end
|
||||
|
||||
-- This widget do not use wibox, so setup correct widget interface
|
||||
data.fit = internal.layout
|
||||
data.draw = internal.layout
|
||||
|
||||
-- Swap / Move / Remove
|
||||
data:connect_signal("item::swapped",function(_,item1,item2,index1,index2)
|
||||
internal.layout.widgets[index1],internal.layout.widgets[index2] = internal.layout.widgets[index2],internal.layout.widgets[index1]
|
||||
internal.layout:emit_signal("widget::updated")
|
||||
end)
|
||||
data:connect_signal("item::moved",function(_,item,new_idx,old_idx)
|
||||
table.insert(internal.layout.widgets,new_idx,table.remove(internal.layout.widgets,old_idx))
|
||||
internal.layout:emit_signal("widget::updated")
|
||||
end)
|
||||
data:connect_signal("item::removed",function(_,item,old_idx)
|
||||
table.remove(internal.layout.widgets,old_idx)
|
||||
if internal.layout._emit_updated then
|
||||
item.widget:disconnect_signal("widget::updated", internal.layout._emit_updated)
|
||||
if internal.layout.set_spacing and data.spacing then
|
||||
internal.layout:set_spacing(data.spacing)
|
||||
end
|
||||
internal.layout:emit_signal("widget::updated")
|
||||
end)
|
||||
data:connect_signal("item::appended",function(_,item)
|
||||
internal.layout:add(item.widget)
|
||||
internal.layout:emit_signal("widget::updated")
|
||||
end)
|
||||
data:connect_signal("widget::added",function(_,item,widget)
|
||||
internal.layout:add(widget)
|
||||
internal.layout:emit_signal("widget::updated")
|
||||
end)
|
||||
end
|
||||
|
||||
local function setup_buttons(data,item,args)
|
||||
local buttons = {}
|
||||
for i=1,10 do
|
||||
if args["button"..i] then
|
||||
buttons[i] = args["button"..i]
|
||||
--Getters
|
||||
data.get_visible = function() return true end
|
||||
data.get_margins = common.get_margins
|
||||
|
||||
if data.style then
|
||||
data.style(data)
|
||||
end
|
||||
end
|
||||
|
||||
-- Setup sub_menu
|
||||
if (item.sub_menu_m or item.sub_menu_f) and data.sub_menu_on >= base.event.BUTTON1 and data.sub_menu_on <= base.event.BUTTON3 then
|
||||
item.widget:set_menu(item.sub_menu_m or item.sub_menu_f,data.sub_menu_on)
|
||||
end
|
||||
|
||||
-- Scrool up
|
||||
if not buttons[4] then
|
||||
buttons[4] = function()
|
||||
data:scroll_up()
|
||||
end
|
||||
end
|
||||
|
||||
-- Scroll down
|
||||
if not buttons[5] then
|
||||
buttons[5] = function()
|
||||
data:scroll_down()
|
||||
end
|
||||
end
|
||||
|
||||
item:connect_signal("button::release",function(_m,_i,button_id,mods,geo)
|
||||
if #mods == 0 and buttons[button_id] then
|
||||
buttons[button_id](_m,_i,mods,geo)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function setup_item(data,item,args)
|
||||
-- Add widgets
|
||||
data._internal.layout:add(data.item_layout(item,data,args))
|
||||
if data.select_on == base.event.HOVER then
|
||||
item.widget:connect_signal("mouse::enter", function() item.selected = true end)
|
||||
item.widget:connect_signal("mouse::leave", function() item.selected = false end)
|
||||
else
|
||||
item.widget:connect_signal("mouse::enter", function() item.hover = true end)
|
||||
item.widget:connect_signal("mouse::leave", function() item.hover = false end)
|
||||
end
|
||||
|
||||
-- Setup buttons
|
||||
setup_buttons(data,item,args)
|
||||
|
||||
-- Tooltip
|
||||
item.widget:set_tooltip(item.tooltip)
|
||||
common.setup_item_move_events(data)
|
||||
end
|
||||
|
||||
local function new(args)
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.internal.get_direction = args.internal.get_direction or get_direction
|
||||
args.internal.set_position = args.internal.set_position or set_position
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.internal.setup_drawable = args.internal.setup_drawable or setup_drawable
|
||||
args.internal.setup_item = args.internal.setup_item or setup_item
|
||||
args.item_style = args.item_style or item_style
|
||||
args.item_layout = args.item_layout or item_layout
|
||||
args.sub_menu_on = args.sub_menu_on or base.event.BUTTON1
|
||||
local ret = base(args)
|
||||
ret:connect_signal("clear::menu",function(_,vis)
|
||||
ret._internal.layout:reset()
|
||||
end)
|
||||
ret:connect_signal("_hidden::changed",function(_,item)
|
||||
item.widget:emit_signal("widget::updated")
|
||||
end)
|
||||
args.internal.setup_item = args.internal.setup_item or common.setup_item
|
||||
args.item_style = args.item_style or item_style
|
||||
args.item_layout = args.item_layout or item_layout
|
||||
args.sub_menu_on = args.sub_menu_on or base.event.BUTTON1
|
||||
|
||||
local ret = base(args)
|
||||
|
||||
-- rawset(ret,"fit", proxy_fit)
|
||||
-- rawset(ret,"draw", proxy_draw)
|
||||
-- rawset(ret,"_fit_geometry_cache", ret._internal.layout._fit_geometry_cache)
|
||||
-- rawset(ret,"add_signal", function()end)
|
||||
-- ret._internal.layout:connect_signal("widget::updated",function()
|
||||
-- ret:emit_signal("widget::updated")
|
||||
-- end)
|
||||
return ret,ret._internal.margin
|
||||
end
|
||||
|
||||
function module.flex(args)
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.internal.layout_func = wibox.layout.flex.horizontal()
|
||||
local data = new(args)
|
||||
data._internal.text_fit = function(self,width,height) return width,height end
|
||||
return data,data._internal.margin
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.internal.layout_func = wibox.layout.flex.horizontal()
|
||||
|
||||
local data = new(args)
|
||||
|
||||
function data._internal.text_fit(self,width,height) return width,height end
|
||||
|
||||
return data,data._internal.margin
|
||||
end
|
||||
|
||||
return setmetatable(module, { __call = function(_, ...) return new(...) end })
|
||||
-- kate: space-indent on; indent-width 2; replace-tabs on;
|
||||
-- kate: space-indent on; indent-width 4; replace-tabs on;
|
||||
|
|
86
base.lua
86
base.lua
|
@ -10,6 +10,7 @@ local object = require( "radical.object" )
|
|||
local vertical = require( "radical.layout.vertical" )
|
||||
local theme = require( "radical.theme" )
|
||||
local item_mod = require( "radical.item" )
|
||||
local common = require( "radical.common" )
|
||||
|
||||
local capi = { mouse = mouse, screen = screen , keygrabber = keygrabber, root=root, }
|
||||
|
||||
|
@ -97,7 +98,7 @@ local function filter(data)
|
|||
end
|
||||
end
|
||||
data._total_item_height = visible_counter
|
||||
local w,h = data._internal.layout:fit()
|
||||
|
||||
-- Make sure to select an item
|
||||
if data._current_item and data._current_item._filter_out then
|
||||
local n = data.next_item
|
||||
|
@ -105,7 +106,7 @@ local function filter(data)
|
|||
n.selected = true
|
||||
end
|
||||
end
|
||||
data.height = h
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -163,12 +164,10 @@ local function activateKeyboard(data)
|
|||
end
|
||||
|
||||
if (key == 'Return') and data._current_item and data._current_item.button1 then
|
||||
if data.sub_menu_on == module.event.BUTTON1 then
|
||||
item_mod.execute_sub_menu(data,data._current_item)
|
||||
else
|
||||
data._current_item.button1(data,data._current_item)
|
||||
data.visible = false
|
||||
end
|
||||
if data.sub_menu_on ~= module.event.BUTTON1 then
|
||||
data.visible = false
|
||||
end
|
||||
elseif key == 'Escape' or (key == 'Tab' and data.filter_string == "") then
|
||||
data.visible = false
|
||||
capi.keygrabber.stop()
|
||||
|
@ -187,7 +186,6 @@ local function activateKeyboard(data)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
---------------------------------ITEM HANDLING----------------------------------
|
||||
local function add_item(data,args)
|
||||
local item = item_mod(data,args)
|
||||
|
@ -195,6 +193,32 @@ local function add_item(data,args)
|
|||
if args.selected == true then
|
||||
item.selected = true
|
||||
end
|
||||
|
||||
-- Show sub-menu
|
||||
if item._private_data.sub_menu_f or item._private_data.sub_menu_m then
|
||||
if data.sub_menu_on == module.event.SELECTED then
|
||||
item.widget:set_menu(
|
||||
function()
|
||||
local sub_menu = item._private_data.sub_menu_m
|
||||
|
||||
if not sub_menu and item._private_data.sub_menu_f then
|
||||
sub_menu = item._private_data.sub_menu_f(item.widget)
|
||||
end
|
||||
|
||||
if sub_menu and (item._private_data.sub_menu_f or sub_menu.rowcount > 0) then
|
||||
sub_menu.arrow_type = module.arrow_type.NONE
|
||||
sub_menu.parent_item = item
|
||||
item._tmp_menu = sub_menu
|
||||
data._tmp_menu = sub_menu
|
||||
end
|
||||
|
||||
return sub_menu
|
||||
end,
|
||||
"mouse::enter"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
item.index = data.rowcount
|
||||
data:emit_signal("item::added",item)
|
||||
return item
|
||||
|
@ -233,16 +257,10 @@ local function add_widget(data,widget,args)
|
|||
})
|
||||
item._private_data = private_data
|
||||
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
|
||||
data:emit_signal("widget::added",item,widget)
|
||||
if data.visible then
|
||||
local fit_w,fit_h = data._internal.layout:get_preferred_size()
|
||||
data.width = data._internal.width or fit_w
|
||||
data.height = fit_h
|
||||
end
|
||||
end
|
||||
|
||||
local function add_widgets(data,widgets)
|
||||
|
@ -360,8 +378,8 @@ local function new(args)
|
|||
overlay = args.overlay or nil,
|
||||
overlay_draw = args.overlay_draw or nil,
|
||||
opacity = args.opacity or beautiful.menu_opacity or 1,
|
||||
spacing = args.spacing or nil, --TODO add to README once merged upstream
|
||||
default_margins = args.default_margins or {},
|
||||
spacing = args.spacing or nil,
|
||||
default_margins = args.default_margins or beautiful.menu_default_margins or {},
|
||||
icon_per_state = args.icon_per_state or false,
|
||||
default_item_margins = args.default_item_margins or {},
|
||||
icon_transformation = args.icon_transformation or nil,
|
||||
|
@ -407,33 +425,21 @@ local function new(args)
|
|||
-- 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)
|
||||
end
|
||||
if data._internal.set_position then
|
||||
data._internal.set_position(data)
|
||||
end
|
||||
private_data.parent_geometry = value --TODO delete
|
||||
end
|
||||
|
||||
data.set_visible = function(_,value)
|
||||
private_data.visible = value
|
||||
if value then
|
||||
local fit_w,fit_h = data._internal.layout:get_preferred_size()
|
||||
data.width = internal.width or fit_w
|
||||
data.height = fit_h
|
||||
elseif data._tmp_menu and data._current_item then
|
||||
-- data._tmp_menu = nil
|
||||
if data._tmp_menu and data._current_item then
|
||||
data._current_item._tmp_menu = nil
|
||||
-- data._current_item.selected = false
|
||||
data.item_style(data._current_item,{})
|
||||
end
|
||||
if internal.has_changed and data.style then
|
||||
data.style(data,{arrow_x=20,margin=internal.margin})
|
||||
end
|
||||
-- if not internal.parent_geometry and data._internal.set_position then
|
||||
if internal.set_position then
|
||||
internal.set_position(data)
|
||||
-- end
|
||||
end
|
||||
if internal.set_visible then
|
||||
internal:set_visible(value)
|
||||
end
|
||||
|
@ -442,6 +448,11 @@ local function new(args)
|
|||
elseif data.parent_geometry and not data.parent_geometry.is_menu and data.enable_keyboard then
|
||||
capi.keygrabber.stop()
|
||||
end
|
||||
|
||||
-- Hide the sub menus when hiding
|
||||
if data._tmp_menu and not value then
|
||||
data._tmp_menu.visible = false
|
||||
end
|
||||
end
|
||||
|
||||
data.add_colors_group = function(data,section)
|
||||
|
@ -450,7 +461,8 @@ local function new(args)
|
|||
|
||||
data.set_layout = function(_,value)
|
||||
if value then
|
||||
value:setup_key_hooks(data)
|
||||
local f = value.setup_key_hooks or common.setup_key_hooks
|
||||
f(value, data)
|
||||
end
|
||||
private_data.layout = value
|
||||
end
|
||||
|
@ -637,8 +649,9 @@ local function new(args)
|
|||
|
||||
function data:hide()
|
||||
data.visible = false
|
||||
if data.parent_geometry and data.parent_geometry.is_menu then
|
||||
local parent = data.parent_geometry
|
||||
|
||||
if data.parent_geometry and data.parent_geometry.parent_menu then
|
||||
local parent = data.parent_geometry.parent_menu
|
||||
while parent do
|
||||
parent.visible = false
|
||||
parent = parent.parent_geometry and parent.parent_geometry.is_menu and parent.parent_geometry
|
||||
|
@ -647,7 +660,8 @@ local function new(args)
|
|||
end
|
||||
|
||||
if private_data.layout then
|
||||
private_data.layout:setup_key_hooks(data)
|
||||
local f = private_data.layout.setup_key_hooks or common.setup_key_hooks
|
||||
f(value, data)
|
||||
end
|
||||
|
||||
data._internal.setup_drawable(data)
|
||||
|
|
25
box.lua
25
box.lua
|
@ -1,22 +1,21 @@
|
|||
local setmetatable = setmetatable
|
||||
local context = require("radical.context")
|
||||
local base = require("radical.base")
|
||||
local capi = { mouse = mouse, screen = screen }
|
||||
|
||||
local function set_position(data)
|
||||
local s = data.screen or capi.mouse.screen
|
||||
s = s > capi.screen.count() and 1 or s
|
||||
local geom = capi.screen[s].geometry
|
||||
data.wibox.x = math.ceil(geom.x + (geom.width/2) - data.width/2)
|
||||
data.wibox.y = math.ceil(geom.y + (geom.height/2) - data.height/2)
|
||||
end
|
||||
local context = require( "radical.context" )
|
||||
local base = require( "radical.base" )
|
||||
local shape = require( "gears.shape" )
|
||||
local placement = require( "awful.placement" )
|
||||
|
||||
local function new(args)
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.arrow_type = base.arrow_type.NONE
|
||||
args.internal.set_position = args.internal.set_position or set_position
|
||||
return context(args)
|
||||
|
||||
local ret = context(args)
|
||||
-- placement.centered (ret.wibox)
|
||||
-- ret.wibox:set_valign("center")
|
||||
-- ret.wibox:set_halign("center")
|
||||
ret.wibox:set_shape (shape.rounded_rect, 10)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
return setmetatable({}, { __call = function(_, ...) return new(...) end })
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
-- Gather common code that can be shared between multiple sub-systems to widgets
|
||||
local item_mod = require( "radical.item" )
|
||||
local wibox = require( "wibox" )
|
||||
local horizontal_item_layout = require( "radical.item.layout.horizontal" )
|
||||
local margins2 = require( "radical.margins" )
|
||||
|
||||
local module = {}
|
||||
|
||||
local function left(data)
|
||||
if data._current_item._tmp_menu then
|
||||
data = data._current_item._tmp_menu
|
||||
data.items[1].selected = true
|
||||
return true,data
|
||||
end
|
||||
end
|
||||
|
||||
local function right(data)
|
||||
if data.parent_geometry and data.parent_geometry.is_menu then
|
||||
for k,v in ipairs(data.items) do
|
||||
if v._tmp_menu == data or v.sub_menu_m == data then
|
||||
v.selected = true
|
||||
end
|
||||
end
|
||||
data.visible = false
|
||||
data = data.parent_geometry
|
||||
return true,data
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function up(data)
|
||||
if not data.previous_item then return end
|
||||
data.previous_item.selected = true
|
||||
end
|
||||
|
||||
local function down(data)
|
||||
if not data.next_item then return end
|
||||
data.next_item.selected = true
|
||||
end
|
||||
|
||||
function module:setup_key_hooks(data)
|
||||
data:add_key_hook({}, "Up" , "press", up )
|
||||
data:add_key_hook({}, "Down" , "press", down )
|
||||
data:add_key_hook({}, "Left" , "press", right )
|
||||
data:add_key_hook({}, "Right" , "press", left )
|
||||
end
|
||||
|
||||
function module.get_margins(data)
|
||||
local internal = data._internal
|
||||
|
||||
if not internal._margins then
|
||||
local ret = {
|
||||
left = data.border_width+(data.default_margins.left
|
||||
or (data.style and data.style.margins.LEFT ) or 0),
|
||||
right = data.border_width+(data.default_margins.right
|
||||
or (data.style and data.style.margins.RIGHT ) or 0),
|
||||
top = data.border_width+(data.default_margins.top
|
||||
or (data.style and data.style.margins.TOP ) or 0),
|
||||
bottom = data.border_width+(data.default_margins.bottom
|
||||
or (data.style and data.style.margins.BOTTOM ) or 0),
|
||||
}
|
||||
|
||||
local m = margins2(internal.margin,ret)
|
||||
rawset(m,"_reset",m.reset)
|
||||
|
||||
function m.reset(margins)
|
||||
m.defaults = ret
|
||||
m:_reset()
|
||||
end
|
||||
|
||||
internal._margins = m
|
||||
end
|
||||
|
||||
return internal._margins
|
||||
end
|
||||
|
||||
function module.setup_buttons(data,item,args)
|
||||
local buttons = {}
|
||||
for i=1,10 do
|
||||
if args["button"..i] then
|
||||
buttons[i] = args["button"..i]
|
||||
end
|
||||
end
|
||||
|
||||
-- Setup sub_menu
|
||||
if (item.sub_menu_m or item.sub_menu_f) and data.sub_menu_on >= item_mod.event.BUTTON1 and data.sub_menu_on <= item_mod.event.BUTTON3 then
|
||||
item.widget:set_menu(item.sub_menu_m or item.sub_menu_f,"button::pressed",data.sub_menu_on)
|
||||
end
|
||||
|
||||
--Hide on right click
|
||||
if not buttons[3] then
|
||||
buttons[3] = function()
|
||||
data:hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- Scroll up
|
||||
if not buttons[4] then
|
||||
buttons[4] = function()
|
||||
data:scroll_up()
|
||||
end
|
||||
end
|
||||
|
||||
-- Scroll down
|
||||
if not buttons[5] then
|
||||
buttons[5] = function()
|
||||
data:scroll_down()
|
||||
end
|
||||
end
|
||||
|
||||
item:connect_signal("button::release",function(_m,_i,button_id,mods,geo)
|
||||
if #mods == 0 and buttons[button_id] then
|
||||
buttons[button_id](_m,_i,mods,geo)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function module.setup_item_move_events(data)
|
||||
local internal = data._internal
|
||||
local l = internal.content_layout or internal.layout
|
||||
|
||||
--SWAP / MOVE / REMOVE
|
||||
data:connect_signal("item::swapped",function(_,item1,item2,index1,index2)
|
||||
l:swap(index1, index2)
|
||||
end)
|
||||
|
||||
data:connect_signal("item::moved",function(_,item,new_idx,old_idx)
|
||||
local w = l:get_children()[old_idx]
|
||||
l:remove(old_idx)
|
||||
l:insert(new_idx, w)
|
||||
end)
|
||||
|
||||
data:connect_signal("item::removed",function(_,item,old_idx)
|
||||
l:remove(old_idx)
|
||||
end)
|
||||
|
||||
data:connect_signal("item::appended",function(_,item)
|
||||
l:add(item.widget)
|
||||
end)
|
||||
|
||||
data:connect_signal("item::added", function(_,item)
|
||||
l:add(item.widget)
|
||||
end)
|
||||
|
||||
data:connect_signal("widget::added",function(_,item,widget)
|
||||
wibox.layout.fixed.add(l,item.widget)
|
||||
l:emit_signal("widget::updated")
|
||||
end)
|
||||
|
||||
data:connect_signal("prefix_widget::added",function(_,widget,args)
|
||||
data._internal.pref_l:insert(1,widget)
|
||||
end)
|
||||
|
||||
data:connect_signal("suffix_widget::added",function(_,widget,args)
|
||||
data._internal.suf_l:add(widget)
|
||||
end)
|
||||
|
||||
data._internal.text_fit = function(self, context, width, height)
|
||||
return width,height
|
||||
end
|
||||
|
||||
data:connect_signal("clear::menu",function(_,vis)
|
||||
l:reset()
|
||||
end)
|
||||
|
||||
data:connect_signal("_hidden::changed",function(_,item)
|
||||
item.widget:emit_signal("widget::updated")
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
function module.setup_state_events(data, item)
|
||||
if data.select_on == item_mod.event.HOVER then
|
||||
item.widget:connect_signal("mouse::enter", function() item.selected = true end)
|
||||
item.widget:connect_signal("mouse::leave", function() item.selected = false end)
|
||||
else
|
||||
item.widget:connect_signal("mouse::enter", function() item.hover = true end)
|
||||
item.widget:connect_signal("mouse::leave", function() item.hover = false end)
|
||||
end
|
||||
end
|
||||
|
||||
function module.setup_item(data,item,args)
|
||||
--Create the background
|
||||
local item_layout = item.layout or data.item_layout or horizontal_item_layout
|
||||
item_layout(item,data,args)
|
||||
|
||||
-- Layout
|
||||
if data._internal.layout.setup_item then
|
||||
data._internal.layout.setup_item(data._internal.layout,data,item,args)
|
||||
end
|
||||
|
||||
-- Buttons
|
||||
module.setup_buttons(data,item,args)
|
||||
|
||||
-- Tooltip
|
||||
item.widget:set_tooltip(item.tooltip)
|
||||
|
||||
-- Set the correct item state
|
||||
module.setup_state_events(data, item)
|
||||
|
||||
-- Setup tooltip
|
||||
item.widget:set_tooltip(item.tooltip)
|
||||
|
||||
-- Apply item style
|
||||
local item_style = item.item_style or data.item_style
|
||||
item_style(item,{})
|
||||
|
||||
-- Enable scrollbar (if necessary)
|
||||
if data._internal.scroll_w and data.rowcount > data.max_items then
|
||||
data._internal.scroll_w.visible = true
|
||||
data._internal.scroll_w["up"]:emit_signal("widget::updated")
|
||||
data._internal.scroll_w["down"]:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
item.widget:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
return module
|
338
context.lua
338
context.lua
|
@ -1,307 +1,81 @@
|
|||
local base = require( "radical.base" )
|
||||
local print = print
|
||||
local unpack = unpack or table.unpack
|
||||
local debug = debug
|
||||
local rawset = rawset
|
||||
local type = type
|
||||
local unpack = unpack or table.unpack
|
||||
local rawset = rawset
|
||||
local setmetatable = setmetatable
|
||||
local color = require( "gears.color" )
|
||||
local wibox = require( "wibox" )
|
||||
local beautiful = require( "beautiful" )
|
||||
local cairo = require( "lgi" ).cairo
|
||||
local awful = require( "awful" )
|
||||
local util = require( "awful.util" )
|
||||
local layout = require( "radical.layout" )
|
||||
local checkbox = require( "radical.widgets.checkbox" )
|
||||
local arrow_style = require( "radical.style.arrow" )
|
||||
local item_mod = require("radical.item")
|
||||
local glib = require("lgi").GLib
|
||||
local margins2 = require("radical.margins")
|
||||
local base = require( "radical.base" )
|
||||
local wibox = require( "wibox" )
|
||||
local beautiful = require( "beautiful" )
|
||||
local layout = require( "radical.layout" )
|
||||
local arrow_style = require( "radical.style.arrow" )
|
||||
local smart_wibox = require( "radical.smart_wibox" )
|
||||
local common = require( "radical.common" )
|
||||
|
||||
local capi,module = { mouse = mouse , screen = screen, keygrabber = keygrabber },{}
|
||||
local capi, module = { keygrabber = keygrabber },{}
|
||||
|
||||
local function get_direction(data)
|
||||
local parent_geometry = data.parent_geometry --Local cache to avoid always calling the object hooks
|
||||
if not parent_geometry or not parent_geometry.drawable then return "bottom" end
|
||||
local function set_visible(i, value)
|
||||
local w, pg = i.w, i.private_data.parent_geometry
|
||||
|
||||
-- The border width is not included in the geometry
|
||||
local bw = data.wibox.border_width or 0
|
||||
|
||||
local drawable_geom = parent_geometry.drawable.drawable.geometry(parent_geometry.drawable.drawable)
|
||||
|
||||
drawable_geom.x, drawable_geom.height = drawable_geom.x - bw, drawable_geom.height + 2*bw
|
||||
|
||||
if parent_geometry.y+parent_geometry.height < drawable_geom.height then --Vertical wibox
|
||||
if drawable_geom.x >= capi.screen[capi.mouse.screen].geometry.width - (drawable_geom.x+drawable_geom.width) then
|
||||
return "right"
|
||||
else
|
||||
return "left"
|
||||
if value then
|
||||
w:move_by_parent(pg, true)
|
||||
end
|
||||
else --Horizontal wibox
|
||||
if drawable_geom.y >= capi.screen[capi.mouse.screen].geometry.height - (drawable_geom.y+drawable_geom.height) then
|
||||
return "bottom"
|
||||
else
|
||||
return "top"
|
||||
|
||||
w.visible = value
|
||||
|
||||
if not value and (not pg or not pg.is_menu) then
|
||||
capi.keygrabber.stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function set_geometry_real(data)
|
||||
local geo = data._internal._next_geometry
|
||||
if geo then
|
||||
for k,v in pairs(geo) do
|
||||
data.wibox[k] = math.ceil(v)
|
||||
end
|
||||
end
|
||||
data._internal._next_geometry = nil
|
||||
data._internal._need_geometry_reload = nil
|
||||
end
|
||||
|
||||
-- This will update the position before the next loop cycle
|
||||
-- This avoid tons of round trips with X for dynamic menus
|
||||
local function change_geometry_idle(data, x, y, w, h)
|
||||
data._internal._next_geometry = data._internal._next_geometry or {}
|
||||
local geo = data._internal._next_geometry
|
||||
|
||||
-- The border_width is not included in the geometry
|
||||
geo.x = x and (x - data.wibox.border_width) or geo.x
|
||||
geo.y = y and (y - data.wibox.border_width) or geo.y
|
||||
geo.width = w and (w - data.wibox.border_width) or geo.width
|
||||
geo.height = h and (h - data.wibox.border_width) or geo.height
|
||||
if not data._internal._need_geometry_reload then
|
||||
glib.idle_add(0, function() set_geometry_real(data) end)
|
||||
data._internal._need_geometry_reload = true
|
||||
end
|
||||
end
|
||||
|
||||
local function set_position(self)
|
||||
if not self.visible then return end
|
||||
local ret,parent = {x=self.x,y=self.y},self.parent_geometry
|
||||
local prefx,prefy = self._internal.private_data.x,self._internal.private_data.y
|
||||
local src_geo = capi.screen[capi.mouse.screen].geometry
|
||||
if parent and parent.is_menu then
|
||||
if parent.direction == "right" then
|
||||
ret={x=parent.x-self.width,y=parent.y+(self.parent_item.y)}
|
||||
else
|
||||
local margins = self.margins
|
||||
ret={x=parent.x+parent.width,y=parent.y+(self.parent_item.y)-(margins and self.margins.top or data.default_margins.top or self.style.margins.TOP)}
|
||||
|
||||
--Handle when the menu doesn't fit in the srceen horizontally
|
||||
if ret.x+self.width > src_geo.x + src_geo.width then
|
||||
ret.x = parent.x - self.width
|
||||
end
|
||||
|
||||
-- Handle when the menu doesn't fit on the screen vertically
|
||||
if ret.y+self.height > src_geo.y + src_geo.height then
|
||||
ret.y = ret.y - self.height + self.item_height + 2*(margins and self.margins.top or data.default_margins.top or self.style.margins.TOP)
|
||||
end
|
||||
end
|
||||
elseif parent then
|
||||
local drawable_geom = parent.drawable.drawable.geometry(parent.drawable.drawable)
|
||||
if (self.direction == "left") or (self.direction == "right") then
|
||||
ret = {
|
||||
x=drawable_geom.x+((self.direction == "right") and - self.width or drawable_geom.width),
|
||||
y=drawable_geom.y+parent.y--[[+((self.arrow_type ~= base.arrow_type.NONE) and parent.height/2-(self.arrow_x or 20)-6 or 0)]]
|
||||
}
|
||||
else
|
||||
ret = {
|
||||
x=drawable_geom.x+parent.x-((self.arrow_type ~= base.arrow_type.NONE) and (self.arrow_x or 20)+11-parent.width/2 or 0),
|
||||
y=(self.direction == "bottom") and drawable_geom.y-self.height or drawable_geom.y+drawable_geom.height}
|
||||
end
|
||||
elseif prefx ~= 0 or prefy ~= 0 then
|
||||
ret = capi.mouse.coords()
|
||||
if prefx then
|
||||
ret.x = prefx
|
||||
end
|
||||
if prefy then
|
||||
ret.y = prefy
|
||||
end
|
||||
else --Use mouse position to set position --TODO it is called too often
|
||||
ret = capi.mouse.coords()
|
||||
local draw = awful.mouse.wibox_under_pointer and awful.mouse.wibox_under_pointer() or awful.mouse.drawin_under_pointer and awful.mouse.drawin_under_pointer()
|
||||
if draw then
|
||||
local geometry = draw.geometry(draw)
|
||||
if self.direction == "top" or self.direction == "bottom" then
|
||||
ret.x = ret.x - (self.arrow_x or 20) - 13
|
||||
ret.y = geometry.y+geometry.height
|
||||
if ret.y+self.height > src_geo.height then
|
||||
self.direction = "bottom"
|
||||
ret.y = geometry.y-self.height
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--Handle when menu doesn't fit horizontally (if not handled earlier)
|
||||
if ret.x+self.width > src_geo.x + src_geo.width then
|
||||
ret.x = ret.x - (ret.x+self.width - (src_geo.x + src_geo.width))
|
||||
elseif ret.x < 0 then
|
||||
ret.x = 0
|
||||
end
|
||||
|
||||
change_geometry_idle(self,ret.x,ret.y - 2*(self.wibox.border_width or 0))
|
||||
end
|
||||
|
||||
local function setup_drawable(data)
|
||||
local internal = data._internal
|
||||
local private_data = internal.private_data
|
||||
local internal = data._internal
|
||||
|
||||
--Init
|
||||
internal.w = wibox({})
|
||||
internal.margin = wibox.layout.margin()
|
||||
if not data.layout then
|
||||
data.layout = layout.vertical
|
||||
end
|
||||
internal.layout = data.layout(data)
|
||||
internal.w.visible = false
|
||||
internal.w.ontop = true
|
||||
internal.margin:set_widget(internal.layout)
|
||||
internal.w:set_widget(internal.margin)
|
||||
internal.w:set_fg(data.fg)
|
||||
internal.w.opacity = data.opacity
|
||||
internal.w.border_color = data.border_color or beautiful.menu_outline_color or beautiful.menu_border_color or beautiful.fg_normal
|
||||
internal.w:set_bg(data.bg)
|
||||
-- Create the layout
|
||||
data.layout = data.layout or layout.vertical
|
||||
|
||||
--Getters
|
||||
data.get_wibox = function() return internal.w end
|
||||
data.get_x = function() return data._internal._next_geometry and data._internal._next_geometry.x or internal.w.x end
|
||||
data.get_y = function() return data._internal._next_geometry and data._internal._next_geometry.y or internal.w.y end
|
||||
data.get_width = function() return data._internal._next_geometry and data._internal._next_geometry.width or internal.w.width end
|
||||
data.get_height = function() return data._internal._next_geometry and data._internal._next_geometry.height or 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()
|
||||
if not internal._margins then
|
||||
local ret = {
|
||||
left = data.border_width+(data.default_margins.left or data.style.margins.LEFT or 0),
|
||||
right = data.border_width+(data.default_margins.right or data.style.margins.RIGHT or 0),
|
||||
top = data.border_width+(data.default_margins.top or data.style.margins.TOP or 0),
|
||||
bottom = data.border_width+(data.default_margins.bottom or data.style.margins.BOTTOM or 0),
|
||||
}
|
||||
local m = margins2(internal.margin,ret)
|
||||
rawset(m,"_reset",m.reset)
|
||||
m.reset = function(margins)
|
||||
m.defaults = ret
|
||||
m:_reset()
|
||||
end
|
||||
internal._margins = m
|
||||
end
|
||||
return internal._margins
|
||||
end
|
||||
internal.layout = data.layout(data)
|
||||
internal.margin = wibox.layout.margin(internal.layout)
|
||||
|
||||
--Setters
|
||||
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()
|
||||
data.height = fit_h
|
||||
data.width = fit_w
|
||||
end
|
||||
end
|
||||
data.set_x = function(_,value) change_geometry_idle(data,value) end
|
||||
data.set_y = function(_,value) change_geometry_idle(data,nil,value) end
|
||||
data.set_width = function(_,value)
|
||||
local need_update = internal.w.width == (value + 2*data.border_width)
|
||||
local margins = data.margins
|
||||
change_geometry_idle(data,nil,nil,value + data.margins.left + data.margins.right)
|
||||
if need_update then
|
||||
data.style(data)
|
||||
end
|
||||
end
|
||||
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
|
||||
change_geometry_idle(data,nil,nil,nil,new_height > 0 and new_height or 1)
|
||||
if need_update then
|
||||
data.style(data)
|
||||
internal.set_position(data)
|
||||
end
|
||||
end
|
||||
function internal:set_visible(value)
|
||||
internal.w.visible = value
|
||||
if not value and (not data.parent_geometry or not data.parent_geometry.is_menu) then
|
||||
capi.keygrabber.stop()
|
||||
end
|
||||
end
|
||||
-- Init
|
||||
internal.w = smart_wibox(internal.margin, {
|
||||
visible = false ,
|
||||
ontop = true ,
|
||||
opacity = data.opacity ,
|
||||
bg = data.bg ,
|
||||
fg = data.fg ,
|
||||
preferred_positions = {"right", "bottom"},
|
||||
border_color = data.border_color
|
||||
or beautiful.menu_outline_color
|
||||
or beautiful.menu_border_color
|
||||
or beautiful.fg_normal
|
||||
})
|
||||
|
||||
if data.visible then
|
||||
local fit_w,fit_h = data._internal.layout:fit()
|
||||
data.width = fit_w
|
||||
data.height = fit_h
|
||||
end
|
||||
end
|
||||
-- Accessors
|
||||
data.get_wibox = function() return internal.w end
|
||||
data.get_visible = function() return internal.private_data.visible end
|
||||
data.get_margins = common.get_margins
|
||||
internal.set_visible = set_visible
|
||||
|
||||
local function setup_buttons(data,item,args)
|
||||
local buttons = {}
|
||||
for i=1,10 do
|
||||
if args["button"..i] then
|
||||
buttons[i] = args["button"..i]
|
||||
end
|
||||
end
|
||||
|
||||
-- Click to open sub_menu
|
||||
if not buttons[1] and data.sub_menu_on == base.event.BUTTON1 then
|
||||
buttons[1] = function() item_mod.execute_sub_menu(data,item) end
|
||||
end
|
||||
|
||||
--Hide on right click
|
||||
if not buttons[3] then
|
||||
buttons[3] = function()
|
||||
data:hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- Scroll up
|
||||
if not buttons[4] then
|
||||
buttons[4] = function()
|
||||
data:scroll_up()
|
||||
end
|
||||
end
|
||||
|
||||
-- Scroll down
|
||||
if not buttons[5] then
|
||||
buttons[5] = function()
|
||||
data:scroll_down()
|
||||
end
|
||||
end
|
||||
|
||||
item:connect_signal("button::release",function(_m,_i,button_id,mods,geo)
|
||||
if #mods == 0 and buttons[button_id] then
|
||||
buttons[button_id](_m,_i,mods,geo)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function setup_item(data,item,args)
|
||||
-- Layout
|
||||
local f = (data._internal.layout.setup_item) or (layout.vertical.setup_item)
|
||||
f(data._internal.layout,data,item,args)
|
||||
|
||||
-- Buttons
|
||||
setup_buttons(data,item,args)
|
||||
|
||||
-- Tooltip
|
||||
item.widget:set_tooltip(item.tooltip)
|
||||
-- Support remove, swap, insert, append...
|
||||
common.setup_item_move_events(data)
|
||||
end
|
||||
|
||||
local function new(args)
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.internal.get_direction = args.internal.get_direction or get_direction
|
||||
args.internal.set_position = args.internal.set_position or set_position
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.internal.setup_drawable = args.internal.setup_drawable or setup_drawable
|
||||
args.internal.setup_item = args.internal.setup_item or setup_item
|
||||
args.style = args.style or beautiful.menu_default_style or arrow_style
|
||||
args.internal.setup_item = args.internal.setup_item or common.setup_item
|
||||
args.style = args.style or beautiful.menu_default_style or arrow_style
|
||||
local ret = base(args)
|
||||
ret:connect_signal("clear::menu",function(_,vis)
|
||||
ret._internal.layout:reset()
|
||||
end)
|
||||
ret:connect_signal("_hidden::changed",function(_,item)
|
||||
item.widget:emit_signal("widget::updated")
|
||||
|
||||
ret:connect_signal("parent_geometry::changed", function()
|
||||
args.internal.w:move_by_parent(ret.parent_geometry, true)
|
||||
end)
|
||||
|
||||
-- Init the style
|
||||
args.style(ret)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
return setmetatable(module, { __call = function(_, ...) return new(...) end })
|
||||
-- kate: space-indent on; indent-width 2; replace-tabs on;
|
||||
-- kate: space-indent on; indent-width 4; replace-tabs on;
|
||||
|
|
519
dock.lua
519
dock.lua
|
@ -1,421 +1,180 @@
|
|||
local setmetatable,unpack,table = setmetatable,unpack,table
|
||||
local setmetatable = setmetatable
|
||||
local math = math
|
||||
local base = require( "radical.base" )
|
||||
local color = require( "gears.color" )
|
||||
local wibox = require( "wibox" )
|
||||
local beautiful = require( "beautiful" )
|
||||
local cairo = require( "lgi" ).cairo
|
||||
local awful = require( "awful" )
|
||||
local util = require( "awful.util" )
|
||||
local fkey = require( "radical.widgets.fkey" )
|
||||
local button = require( "awful.button" )
|
||||
local checkbox = require( "radical.widgets.checkbox" )
|
||||
local vertical = require( "radical.layout.vertical" )
|
||||
local horizontal = require( "radical.layout.horizontal" )
|
||||
local item_layout= require( "radical.item.layout.icon" )
|
||||
local item_style = require( "radical.item.style.rounded" )
|
||||
local glib = require( "lgi" ).GLib
|
||||
local margins2 = require("radical.margins" )
|
||||
local base = require( "radical.base" )
|
||||
local color = require( "gears.color" )
|
||||
local wibox = require( "wibox" )
|
||||
local beautiful = require( "beautiful" )
|
||||
local vertical = require( "radical.layout.vertical" )
|
||||
local horizontal = require( "radical.layout.horizontal" )
|
||||
local item_layout = require( "radical.item.layout.icon" )
|
||||
local item_style = require( "radical.item.style.rounded" )
|
||||
local hot_corner = require( "radical.hot_corner" )
|
||||
local shape = require( "gears.shape" )
|
||||
local common = require( "radical.common" )
|
||||
local smart_wibox = require( "radical.smart_wibox" )
|
||||
local placement = require( "radical.placement" )
|
||||
|
||||
local default_radius = 10
|
||||
local rad = beautiful.dock_corner_radius or default_radius
|
||||
local default_shape = function(cr, width, height) shape.partially_rounded_rect(cr, width, height, false, true, true, false, rad) end
|
||||
|
||||
local capi,module = { mouse = mouse , screen = screen, keygrabber = keygrabber },{}
|
||||
local max_size = {height={},width={}}
|
||||
|
||||
local default_radius = 10
|
||||
|
||||
local dir_to_deg = {left=0,bottom=math.pi/2,right=math.pi,top=3*(math.pi/2)}
|
||||
|
||||
local function get_direction(data)
|
||||
local dir = data._internal.position or "left"
|
||||
return dir,dir_to_deg[dir]--"left" -- Nothing to do
|
||||
end
|
||||
|
||||
--No, screen 1 is not always at x=0, yet
|
||||
local function get_first_screen()
|
||||
for i=1,capi.screen.count() do
|
||||
if capi.screen[i].geometry.x == 0 then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
------------------------------------
|
||||
-- Drawing related code --
|
||||
------------------------------------
|
||||
|
||||
local function rotate2(img, geometry, angle,swap_size)
|
||||
geometry = swap_size and {width = geometry.height, height=geometry.width} or geometry
|
||||
local matrix,pattern,img2 = cairo.Matrix(),cairo.Pattern.create_for_surface(img),cairo.ImageSurface(cairo.Format.ARGB32, geometry.width, geometry.height)
|
||||
cairo.Matrix.init_rotate(matrix,angle)
|
||||
matrix:translate((angle == math.pi/2) and 0 or -geometry.width, (angle == 3*(math.pi/2)) and 0 or -geometry.height)
|
||||
pattern:set_matrix(matrix)
|
||||
local cr2 = cairo.Context(img2)
|
||||
cr2:set_source(pattern)
|
||||
cr2:paint()
|
||||
return img2
|
||||
end
|
||||
|
||||
-- Draw the round corners
|
||||
local function mask(rotate,width,height,radius,offset,anti,bg,fg)
|
||||
local invert = (rotate ~= 0) and (rotate ~= math.pi)
|
||||
local width,height = invert and height or width,invert and width or height
|
||||
local img = cairo.ImageSurface.create(cairo.Format.ARGB32, width, height)
|
||||
local cr = cairo.Context(img)
|
||||
cr:set_operator(cairo.Operator.SOURCE)
|
||||
cr:set_antialias(anti)
|
||||
cr:rectangle(0, 0, width, height)
|
||||
cr:set_source(bg)
|
||||
cr:fill()
|
||||
cr:set_source(fg)
|
||||
cr:arc(width-radius-1-offset,radius+offset*2,radius,0,2*math.pi)
|
||||
cr:arc(width-radius-1-offset,height-radius-2*offset,radius,0,2*math.pi)
|
||||
cr:rectangle(0, offset, width-radius-1, height-2*offset)
|
||||
cr:rectangle(width-radius-1-offset, radius+2*offset, radius, height-2*radius-2*offset)
|
||||
cr:fill()
|
||||
return rotate~=0 and rotate2(img,{width=width,height=height},rotate,true) or img
|
||||
end
|
||||
|
||||
-- Do not draw over the boder, ever
|
||||
local function dock_draw(self, context, cr, width, height)
|
||||
|
||||
local w = context.wibox
|
||||
|
||||
-- Generate the border surface
|
||||
if not self.mask or self.mask_hash ~= width*1000+height then
|
||||
local dir,rotation = get_direction(self.data)
|
||||
self.mask = mask(rotation,w.width,w.height,(beautiful.dock_corner_radius or default_radius) - 2,1,0,color(self.data.border_color or seld.data.fg),color("#FF000000"))
|
||||
self.mask_hash = width*1000+height
|
||||
end
|
||||
cr:save()
|
||||
|
||||
--Draw the border
|
||||
--self.__draw(self, context, cr, width, height)
|
||||
cr:set_source_surface(self.mask)
|
||||
cr:paint()
|
||||
cr:restore()
|
||||
end
|
||||
|
||||
|
||||
--Make sure the wibox is at the center of the screen
|
||||
local function align_wibox(w,direction,screen)
|
||||
local axis = (direction == "left" or direction == "right") and "height" or "width"
|
||||
local offset = axis == "height" and "y" or "x"
|
||||
local src_geom = capi.screen[screen].geometry
|
||||
local scr_size = src_geom[offset] + (src_geom[axis] - w[axis]) /2
|
||||
|
||||
w[offset] = math.ceil(scr_size)
|
||||
if direction == "left" then
|
||||
w.x = math.ceil(src_geom.x)
|
||||
elseif direction == "right" then
|
||||
w.x = math.ceil(src_geom.x + src_geom.width - w.width)
|
||||
elseif direction == "bottom" then
|
||||
w.y = math.ceil(src_geom.y+src_geom.height-w.height)
|
||||
else
|
||||
w.y = math.ceil(src_geom.y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------------------------
|
||||
-- Size and position related code --
|
||||
-----------------------------------------
|
||||
|
||||
-- Change the position, TODO
|
||||
local function set_position(self,value)
|
||||
self._internal.position = value
|
||||
end
|
||||
|
||||
local function get_position(self,value)
|
||||
return self._internal.position
|
||||
end
|
||||
|
||||
-- Compute the optimal maxmimum size
|
||||
local function get_max_size(data,screen)
|
||||
local dir = get_direction(data)
|
||||
local w_or_h = ((dir == "left" or dir == "right") and "height" or "width")
|
||||
local x_or_y = w_or_h == "height" and "y" or "x"
|
||||
local res = max_size[w_or_h][screen]
|
||||
if not res then
|
||||
local full,wa = capi.screen[screen].geometry[w_or_h],capi.screen[screen].workarea
|
||||
local top,bottom = wa[x_or_y],full-(wa.y+wa[w_or_h])
|
||||
local biggest = top > bottom and top or bottom
|
||||
--res = full - biggest*2 - 52 -- 26px margins
|
||||
res = wa[w_or_h] - 52 -- 26px margins
|
||||
max_size[w_or_h][screen] = res
|
||||
end
|
||||
return res
|
||||
local dir = "left"
|
||||
local w_or_h = ((dir == "left" or dir == "right") and "height" or "width")
|
||||
local x_or_y = w_or_h == "height" and "y" or "x"
|
||||
local res = max_size[w_or_h][screen]
|
||||
if not res then
|
||||
local full,wa = capi.screen[screen].geometry[w_or_h],capi.screen[screen].workarea
|
||||
local top,bottom = wa[x_or_y],full-(wa.y+wa[w_or_h])
|
||||
local biggest = top > bottom and top or bottom
|
||||
--res = full - biggest*2 - 52 -- 26px margins
|
||||
local margin = beautiful.dock_margin or 52
|
||||
res = wa[w_or_h] - margin
|
||||
max_size[w_or_h][screen] = res
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
-- local function get_size(data,screen)
|
||||
-- local max = get_max_size(data,screen)
|
||||
-- if data._internal.orientation == "vertical" and h > max then
|
||||
--
|
||||
-- elseif data._internal.orientation == "horizontal" and w > max then
|
||||
--
|
||||
-- end
|
||||
-- return w,h,max
|
||||
-- end
|
||||
|
||||
--TODO it still works, but rewrite this code anyway
|
||||
-- The dock always have to be shorter than the screen
|
||||
local function adapt_size(data,w,h,screen)
|
||||
local max = get_max_size(data,screen)
|
||||
local max = get_max_size(data,screen)
|
||||
|
||||
-- Get the current size, then compare and ajust
|
||||
local fit_w,fit_h = data._internal.layout:get_preferred_size({dpi=96,force_values=true},beautiful.default_height)
|
||||
|
||||
-- Get the number of items minus the number of widgets
|
||||
-- This can be used to approximate the number of pixel to remove
|
||||
local visible_item = data.visible_row_count - #data._internal.widgets + 1
|
||||
-- Get the current size, then compare and ajust
|
||||
local fit_w,fit_h = data._internal.layout:get_preferred_size({dpi=96,force_values=true},beautiful.default_height)
|
||||
|
||||
if data._internal.orientation == "vertical" and h > max then
|
||||
local wdg_height = data.widget_fit_height_sum
|
||||
--TODO this assume the widget size wont change
|
||||
-- data.item_height = math.ceil((data.item_height*max)/h) --OLD
|
||||
data.item_height = math.ceil((max-wdg_height)/visible_item)
|
||||
w = data.item_height
|
||||
h = max
|
||||
data.item_width = w
|
||||
data.menu_width = w
|
||||
w = w + data.margins.left+data.margins.right
|
||||
data.default_width = w
|
||||
data._internal.private_data.width = w
|
||||
elseif data._internal.orientation == "horizontal" and w > max then
|
||||
--TODO merge this with above
|
||||
local wdg_width = data.widget_fit_width_sum
|
||||
data.item_width = math.ceil((data.item_height*max)/w)
|
||||
data._internal.private_data = data.item_width
|
||||
w = max
|
||||
h = data.item_width
|
||||
data.item_height = h
|
||||
data.menu_height = h
|
||||
h = h + data.margins.bottom+data.margins.top
|
||||
-- data.default_width = h
|
||||
end
|
||||
if data.icon_size and data.icon_size > w then
|
||||
data.icon_size = w
|
||||
end
|
||||
data._internal._geom_vals = nil
|
||||
-- Get the number of items minus the number of widgets
|
||||
-- This can be used to approximate the number of pixel to remove
|
||||
local visible_item = data.visible_row_count - #data._internal.widgets + 1
|
||||
|
||||
return w == 0 and 1 or w, h == 0 and 1 or h
|
||||
local orientation = "vertical"
|
||||
|
||||
if orientation == "vertical" and h > max then
|
||||
local wdg_height = data.widget_fit_height_sum
|
||||
--TODO this assume the widget size wont change
|
||||
-- data.item_height = math.ceil((data.item_height*max)/h) --OLD
|
||||
data.item_height = math.ceil((max-wdg_height)/visible_item)
|
||||
w = data.item_height
|
||||
h = max
|
||||
data.item_width = w
|
||||
data.menu_width = w
|
||||
w = w + data.margins.left+data.margins.right
|
||||
data.default_width = w
|
||||
data._internal.private_data.width = w
|
||||
elseif orientation == "horizontal" and w > max then
|
||||
--TODO merge this with above
|
||||
local wdg_width = data.widget_fit_width_sum
|
||||
data.item_width = math.ceil((data.item_height*max)/w)
|
||||
data._internal.private_data = data.item_width
|
||||
w = max
|
||||
h = data.item_width
|
||||
data.item_height = h
|
||||
data.menu_height = h
|
||||
h = h + data.margins.bottom+data.margins.top
|
||||
-- data.default_width = h
|
||||
end
|
||||
if data.icon_size and data.icon_size > w then
|
||||
data.icon_size = w
|
||||
end
|
||||
|
||||
data._internal.margin:emit_signal("widget::layout_changed")
|
||||
data._internal.margin:emit_signal("widget::redraw_needed")
|
||||
|
||||
return w == 0 and 1 or w, h == 0 and 1 or h
|
||||
end
|
||||
|
||||
-- Create the auto hiding wibox
|
||||
-- Create the main wibox (lazy-loading)
|
||||
local function get_wibox(data, screen)
|
||||
if data._internal.w then return data._internal.w end
|
||||
data:emit_signal("dock::request")
|
||||
if data._internal.w then return data._internal.w end
|
||||
|
||||
local dir,rotation = get_direction(data)
|
||||
local geo_src = data._internal._geom_vals or data
|
||||
data._internal.margin = wibox.layout.margin(data._internal.layout)
|
||||
|
||||
-- Need to be created befoce calling align_wibox
|
||||
local m = wibox.layout.margin()
|
||||
m:set_widget(data._internal.layout)
|
||||
m:set_margins(0)
|
||||
data._internal.mrgns.widget = m
|
||||
local w = smart_wibox(data._internal.margin, {
|
||||
screen = screen ,
|
||||
ontop = true ,
|
||||
shape = beautiful.dock_shape or default_shape ,
|
||||
shape_border_width = 1 ,
|
||||
shape_border_color = color(data.border_color or data.fg ),
|
||||
bg = color(beautiful.bg_dock or beautiful.bg_normal),
|
||||
})
|
||||
|
||||
-- Make sure the down will fit on the screen
|
||||
geo_src.width,geo_src.height = adapt_size(data,geo_src.width,geo_src.height,screen)
|
||||
data._internal.w = w
|
||||
|
||||
local w = wibox{ screen = screen, width = geo_src.width, height = geo_src.height,ontop=true}
|
||||
align_wibox(w,dir,screen)
|
||||
data:emit_signal("visible::changed",true)
|
||||
|
||||
w:set_widget(m)
|
||||
data._internal.w = w
|
||||
w:connect_signal("property::height",function()
|
||||
adapt_size(data, w.width, w.height, 1)
|
||||
end)
|
||||
|
||||
-- Create the rounded corner mask
|
||||
w:set_bg(cairo.Pattern.create_for_surface(mask(rotation,w.width,w.height,(beautiful.dock_corner_radius or default_radius)-2,1,0,color(beautiful.fg_normal),color(beautiful.bg_dock or beautiful.bg_normal))))
|
||||
w.shape_bounding = mask(rotation,w.width,w.height,beautiful.dock_corner_radius or default_radius,0,1,color("#00000000"),color("#FFFFFFFF"))._native
|
||||
local function prop_change()
|
||||
w:set_bg(cairo.Pattern.create_for_surface(mask(rotation,w.width,w.height,(beautiful.dock_corner_radius or default_radius) -2,1,0,color(beautiful.fg_normal),color(beautiful.bg_dock or beautiful.bg_normal))))
|
||||
w.shape_bounding = mask(rotation,w.width,w.height,beautiful.dock_corner_radius or default_radius,0,1,color("#00000000"),color("#FFFFFFFF"))._native
|
||||
end
|
||||
w:connect_signal("property::height",prop_change)
|
||||
w:connect_signal("property::width" ,prop_change)
|
||||
|
||||
-- Hide the dock when the mouse leave
|
||||
w:connect_signal("mouse::leave",function()
|
||||
if not (data._tmp_menu and data._tmp_menu.visible) then
|
||||
data.visible = false
|
||||
end
|
||||
end)
|
||||
data:emit_signal("visible::changed",true)
|
||||
|
||||
-- Bring back the placeholder when hiding
|
||||
data:connect_signal("visible::changed",function(data,val)
|
||||
if not val then
|
||||
data._internal.placeholder.visible = true
|
||||
end
|
||||
end)
|
||||
|
||||
return w
|
||||
end
|
||||
|
||||
-- Create the "hidden" wibox that display the first one on command
|
||||
local function create_placeholder(data)
|
||||
local screen,dir = data.screen or get_first_screen() or 1,get_direction(data)
|
||||
local h_or_w = (dir == "left" or dir == "right") and "width" or "height"
|
||||
local hw_invert = h_or_w == "height" and "width" or "height"
|
||||
local placeholder = wibox{ screen = screen, [h_or_w] = 1,[hw_invert] = 1,bg="#00000000", ontop = true,visible=true }
|
||||
|
||||
placeholder:geometry({ [h_or_w] = 1, [hw_invert] = capi.screen[screen].geometry.height -100, x = dir == "right" and capi.screen[screen].geometry.width -1 or 0, y = capi.screen[screen].geometry.y + 50})
|
||||
|
||||
|
||||
-- Raise of create the main dock wibox
|
||||
placeholder:connect_signal("mouse::enter", function()
|
||||
get_wibox(data,screen).visible = true
|
||||
placeholder.visible = false
|
||||
end)
|
||||
|
||||
-- Move the placeholder when the wibox is resized
|
||||
data:connect_signal(((dir == "left" or dir == "right") and "height" or "width").."::changed",function()
|
||||
-- placeholder[hw_invert] = data[hw_invert]
|
||||
align_wibox(placeholder,dir,screen)
|
||||
end)
|
||||
data._internal.placeholder = placeholder
|
||||
|
||||
-- Adapt the size when new items are added
|
||||
data:connect_signal("layout_size",function(_,w,h)
|
||||
if not data._internal._has_changed then
|
||||
glib.idle_add(glib.PRIORITY_DEFAULT_IDLE, function()
|
||||
if not data._internal._geom_vals then return end
|
||||
local w,h,internal = data._internal._geom_vals.width,data._internal._geom_vals.height,data._internal
|
||||
|
||||
if h == 0 then
|
||||
h = 1
|
||||
end
|
||||
if w == 0 then
|
||||
w = 1
|
||||
end
|
||||
|
||||
-- Resize the placeholder
|
||||
internal.placeholder[hw_invert] = (hw_invert == "height") and h or w
|
||||
align_wibox(internal.placeholder,dir,screen)
|
||||
|
||||
-- Resize the dock wibox
|
||||
if internal.w then
|
||||
w,h=adapt_size(data,w,h,screen) --TODO place holder need to do
|
||||
internal.w.height = h
|
||||
internal.w.width = w
|
||||
align_wibox(internal.w,dir,screen)
|
||||
end
|
||||
|
||||
data._internal._has_changed = false
|
||||
end)
|
||||
end
|
||||
data._internal._geom_vals = {height=h,width=w}
|
||||
data._internal._has_changed = true
|
||||
end)
|
||||
placement.pin(w, placement.corner, "left", screen or 1)
|
||||
|
||||
return w
|
||||
end
|
||||
|
||||
local function setup_drawable(data)
|
||||
local internal = data._internal
|
||||
local private_data = internal.private_data
|
||||
local internal = data._internal
|
||||
|
||||
-- Create the layout
|
||||
internal.layout = data.layout(data)
|
||||
internal.layout.__draw = internal.layout.draw
|
||||
internal.layout.draw = dock_draw
|
||||
internal.layout.data = data
|
||||
-- Create the layout
|
||||
internal.layout = data.layout(data)
|
||||
|
||||
-- Getters
|
||||
data.get_x = function() return 0 end
|
||||
data.get_y = function() return 0 end
|
||||
data.get_width = function()
|
||||
return internal.w and internal.w.width or data._internal.layout:get_preferred_size({force_values=true})
|
||||
end
|
||||
data.get_height = function()
|
||||
if internal.orientation == "horizontal" then
|
||||
return beautiful.default_height
|
||||
else
|
||||
local w,h = internal.layout:get_preferred_size()
|
||||
return h
|
||||
-- Getters
|
||||
data.get_visible = function() return true end
|
||||
data.get_margins = common.get_margins
|
||||
|
||||
function data:set_visible(value)
|
||||
if internal.w then
|
||||
internal.w.visible = value or false
|
||||
end
|
||||
end
|
||||
end
|
||||
data.get_visible = function() return true end
|
||||
data.get_direction = get_direction
|
||||
|
||||
local mrgns = margins2(nil,{})
|
||||
data._internal.mrgns = mrgns
|
||||
data.get_margins = function()
|
||||
return data._internal.mrgns or {}
|
||||
end
|
||||
|
||||
function data:set_visible(value)
|
||||
if internal.w then
|
||||
internal.w.visible = value or false
|
||||
end
|
||||
end
|
||||
|
||||
-- This widget do not use wibox, so setup correct widget interface
|
||||
data.fit = internal.layout
|
||||
data.draw = internal.layout
|
||||
end
|
||||
|
||||
local function setup_item(data,item,args)
|
||||
-- Add widgets
|
||||
local f = (data._internal.layout.setup_item) or (vertical.setup_item)
|
||||
f(data._internal.layout,data,item,args)
|
||||
|
||||
-- Buttons
|
||||
local buttons = {}
|
||||
for i=1,10 do
|
||||
if args["button"..i] then
|
||||
buttons[i] = args["button"..i]
|
||||
end
|
||||
end
|
||||
|
||||
item:connect_signal("button::release",function(_m,_i,button_id,mods,geo)
|
||||
if #mods == 0 and buttons[button_id] then
|
||||
buttons[button_id](_m,_i,mods,geo)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Tooltip
|
||||
item.widget:set_tooltip(item.tooltip)
|
||||
common.setup_item_move_events(data)
|
||||
end
|
||||
|
||||
local function new(args)
|
||||
local args = args or {}
|
||||
local orientation = (not args.position or args.position == "left" or args.position == "right") and "vertical" or "horizontal"
|
||||
local length_inv = orientation == "vertical" and "width" or "height"
|
||||
local args = args or {}
|
||||
local orientation = (not args.position or args.position == "left" or args.position == "right") and "vertical" or "horizontal"
|
||||
local length_inv = orientation == "vertical" and "width" or "height"
|
||||
|
||||
-- The the Radical arguments
|
||||
args.internal = args.internal or {}
|
||||
args.internal.orientation = orientation
|
||||
args.internal.get_direction = args.internal.get_direction or get_direction
|
||||
args.internal.set_position = args.internal.set_position or set_position
|
||||
args.internal.setup_drawable = args.internal.setup_drawable or setup_drawable
|
||||
args.internal.setup_item = args.internal.setup_item or setup_item
|
||||
args.item_style = args.item_style or item_style
|
||||
args.bg = color("#00000000") --Use the dock bg instead
|
||||
args.item_height = 40
|
||||
args.item_width = 40
|
||||
args.sub_menu_on = args.sub_menu_on or base.event.BUTTON1
|
||||
args.internal = args.internal or {}
|
||||
args.internal.layout_func = orientation == "vertical" and vertical or horizontal
|
||||
args.layout = args.layout or args.internal.layout_func
|
||||
args.item_style = args.item_style or item.style
|
||||
-- args.item_layout = args.item_layout or item_layout
|
||||
args[length_inv] = args[length_inv] or 40
|
||||
-- The the Radical arguments
|
||||
args.internal = args.internal or {}
|
||||
args.internal.orientation = orientation
|
||||
args.internal.setup_drawable = args.internal.setup_drawable or setup_drawable
|
||||
args.internal.setup_item = args.internal.setup_item or common.setup_item
|
||||
args.item_style = args.item_style or item_style
|
||||
args.bg = color("#00000000") --Use the dock bg instead
|
||||
args.item_height = 40
|
||||
args.item_width = 40
|
||||
args.sub_menu_on = args.sub_menu_on or base.event.BUTTON1
|
||||
args.internal = args.internal or {}
|
||||
args.internal.layout_func = orientation == "vertical" and vertical or horizontal
|
||||
args.layout = args.layout or args.internal.layout_func
|
||||
args.item_style = args.item_style or item.style
|
||||
-- args.item_layout = args.item_layout or item_layout
|
||||
args[length_inv] = args[length_inv] or 40
|
||||
|
||||
-- Create the dock
|
||||
local ret = base(args)
|
||||
ret.set_position = set_position
|
||||
ret.get_position = get_position
|
||||
ret.position = args.position or "left"
|
||||
ret.screen = args.screen
|
||||
-- Create the dock
|
||||
local ret = base(args)
|
||||
ret.position = args.position or "left"
|
||||
ret.screen = args.screen or 1
|
||||
|
||||
-- Add a 1px placeholder to trigger it
|
||||
create_placeholder(ret)
|
||||
-- Add a 1px placeholder to trigger it
|
||||
if not beautiful.dock_always_show then
|
||||
hot_corner.register_wibox(ret.position, function()
|
||||
return get_wibox(ret, 1)
|
||||
end, ret.screen, 1)
|
||||
else
|
||||
timer.delayed_call(function()
|
||||
local w = get_wibox(ret, 1)
|
||||
w.visible = true
|
||||
end)
|
||||
end
|
||||
|
||||
return ret
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
return setmetatable(module, { __call = function(_, ...) return new(...) end })
|
||||
-- kate: space-indent on; indent-width 2; replace-tabs on;
|
||||
-- kate: space-indent on; indent-width 4; replace-tabs on;
|
||||
|
|
46
embed.lua
46
embed.lua
|
@ -14,6 +14,7 @@ local button = require( "awful.button" )
|
|||
local layout = require( "radical.layout" )
|
||||
local checkbox = require( "radical.widgets.checkbox" )
|
||||
local classic_style = require( "radical.style.classic" )
|
||||
local common = require( "radical.common" )
|
||||
|
||||
local capi,module = { mouse = mouse , screen = screen , keygrabber = keygrabber },{}
|
||||
|
||||
|
@ -26,14 +27,11 @@ local function setup_drawable(data)
|
|||
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
|
||||
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
|
||||
internal.layout = data.layout(data)
|
||||
data.width,data.height = data._internal.layout:fit()
|
||||
data.margins={left=0,right=0,bottom=0,top=0}
|
||||
internal.layout:connect_signal("mouse::enter",function(_,geo)
|
||||
if data._embeded_parent._current_item then
|
||||
|
@ -47,49 +45,11 @@ local function setup_drawable(data)
|
|||
end)
|
||||
end
|
||||
|
||||
local function setup_item(data,item,args)
|
||||
|
||||
-- Create the layout
|
||||
local f = (data._internal.layout.setup_item) or (layout.vertical.setup_item)
|
||||
f(data._internal.layout,data,item,args)
|
||||
local buttons = {}
|
||||
for i=1,10 do
|
||||
if args["button"..i] then
|
||||
buttons[i] = args["button"..i]
|
||||
end
|
||||
end
|
||||
if not buttons[3] then --Hide on right click
|
||||
buttons[3] = function()
|
||||
data.visible = false
|
||||
if data.parent_geometry and data.parent_geometry.is_menu then
|
||||
data.parent_geometry.visible = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if not buttons[4] then
|
||||
buttons[4] = function()
|
||||
data:scroll_up()
|
||||
end
|
||||
end
|
||||
if not buttons[5] then
|
||||
buttons[5] = function()
|
||||
data:scroll_down()
|
||||
end
|
||||
end
|
||||
|
||||
item:connect_signal("button::release",function(_m,_i,button_id,mods,geo)
|
||||
if #mods == 0 and buttons[button_id] then
|
||||
buttons[button_id](_m,_i,mods,geo)
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
local function new(args)
|
||||
local args = args or {}
|
||||
args.internal = args.internal or {}
|
||||
args.internal.setup_drawable = args.internal.setup_drawable or setup_drawable
|
||||
args.internal.setup_item = args.internal.setup_item or setup_item
|
||||
args.internal.setup_item = args.internal.setup_item or common.setup_item
|
||||
args.style = args.style or classic_style
|
||||
local ret = base(args)
|
||||
ret:connect_signal("clear::menu",function(_,vis)
|
||||
|
|
|
@ -98,10 +98,6 @@ function module.screenshot(clients,geo)
|
|||
|
||||
end
|
||||
|
||||
if geo then
|
||||
prev_menu.parent_geometry = geo
|
||||
end
|
||||
|
||||
prev_menu.visible = true
|
||||
return prev_menu
|
||||
end
|
||||
|
|
51
init.lua
51
init.lua
|
@ -11,11 +11,32 @@ local function set_tooltip(self, text, args)
|
|||
self._tooltip = tooltip(self,text, args)
|
||||
end
|
||||
|
||||
local function set_menu(self,menu,button)
|
||||
--- Set a menu for widget "self".
|
||||
-- This function is available for all widgets.
|
||||
-- Any signals can be used as trigger, the common ones are:
|
||||
--
|
||||
-- * "button::press" (default) Left mouse button (normal click_
|
||||
-- * "mouse::enter" When the mouse enter the widget
|
||||
--
|
||||
-- @param self A widget (implicit parameter)
|
||||
-- @param menu A radical menu or a function returning one (for lazy-loading)
|
||||
-- @tparam[opt="button1::pressed"] string event The event trigger for showing
|
||||
-- the menu.
|
||||
-- @tparam[opt=1] button_id The mouse button 1 (1= left, 3=right)
|
||||
local function set_menu(self,menu, event, button_id)
|
||||
if not menu then return end
|
||||
local b = button or 1
|
||||
local current,bt = self:buttons(),aw_button({},b,function(geo)
|
||||
local event = event or "button::pressed"
|
||||
local button_id = button_id or 1
|
||||
|
||||
|
||||
local function trigger(_, geo)
|
||||
local geo = geo or _
|
||||
local m = menu
|
||||
|
||||
if self._data and self._data.is_menu then
|
||||
geo.parent_menu = self._data
|
||||
end
|
||||
|
||||
if type(menu) == "function" then
|
||||
if self._tmp_menu and self._tmp_menu.visible then
|
||||
self._tmp_menu.visible = false
|
||||
|
@ -26,21 +47,19 @@ local function set_menu(self,menu,button)
|
|||
end
|
||||
if not m then return end
|
||||
|
||||
local dgeo = geo.drawable.drawable:geometry()
|
||||
-- The geometry is a mix of the drawable and widget one
|
||||
local geo2 = {
|
||||
x = dgeo.x + geo.x,
|
||||
y = dgeo.y + geo.y,
|
||||
width = geo.width ,
|
||||
height = geo.height ,
|
||||
drawable = geo.drawable ,
|
||||
}
|
||||
m.parent_geometry = geo
|
||||
m._internal.w:move_by_parent(geo)
|
||||
|
||||
m.parent_geometry = geo2
|
||||
m.visible = not m.visible
|
||||
end)
|
||||
for k, v in pairs(bt) do
|
||||
current[type(k) == "number" and (#current+1) or k] = v
|
||||
end
|
||||
|
||||
if event == "button::pressed" then
|
||||
local current,bt = self:buttons(),aw_button({},b,trigger)
|
||||
for k, v in pairs(bt) do
|
||||
current[type(k) == "number" and (#current+1) or k] = v
|
||||
end
|
||||
else
|
||||
self:connect_signal(event, trigger)
|
||||
end
|
||||
self._menu = menu
|
||||
return bt
|
||||
|
|
|
@ -66,20 +66,6 @@ local function load_async(tab,key)
|
|||
return rawget(module,key)
|
||||
end
|
||||
|
||||
function module.execute_sub_menu(data,item)
|
||||
if (item._private_data.sub_menu_f or item._private_data.sub_menu_m) then
|
||||
local sub_menu = item._private_data.sub_menu_m or item._private_data.sub_menu_f(data,item)
|
||||
if sub_menu and (item._private_data.sub_menu_f or sub_menu.rowcount > 0) then
|
||||
sub_menu.arrow_type = module.arrow_type.NONE
|
||||
sub_menu.parent_item = item
|
||||
sub_menu.parent_geometry = data
|
||||
sub_menu.visible = true
|
||||
item._tmp_menu = sub_menu
|
||||
data._tmp_menu = sub_menu
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function hide_sub_menu(item,data)
|
||||
if item._tmp_menu then
|
||||
item._tmp_menu.visible = false
|
||||
|
@ -212,15 +198,18 @@ local function new_item(data,args)
|
|||
item.state[module.item_flags.SELECTED] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Select the new one
|
||||
if data.sub_menu_on == module.event.SELECTED and (current_item ~= item or not item._tmp_menu)then
|
||||
module.execute_sub_menu(data,item)
|
||||
end
|
||||
item.state[module.item_flags.SELECTED] = true
|
||||
data._current_item = item
|
||||
end
|
||||
|
||||
item.set_icon = function (_,value)
|
||||
local icon_w = item._internal.icon_w
|
||||
|
||||
if not icon_w then return end
|
||||
|
||||
icon_w:set_image(value)
|
||||
end
|
||||
|
||||
-- Listen to signals
|
||||
item:connect_signal("state::changed",function()
|
||||
item:style()
|
||||
|
|
|
@ -1,7 +1,33 @@
|
|||
local holo = require("radical.item.style.holo")
|
||||
local rounded = require("radical.item.style.rounded" )
|
||||
local theme = require( "radical.theme" )
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
return {
|
||||
-- Create a generic item_style
|
||||
local function generic(_, args)
|
||||
local args = args or {}
|
||||
|
||||
args.margins = args.margins or {
|
||||
TOP = 0,
|
||||
BOTTOM = 0,
|
||||
RIGHT = 0,
|
||||
LEFT = 0
|
||||
}
|
||||
|
||||
local module = {
|
||||
margins = args.margins
|
||||
}
|
||||
|
||||
local function draw(item)
|
||||
item.widget:set_shape(args.shape, unpack(args.shape_args or {}))
|
||||
item.widget:set_shape_border_width(item.border_width)
|
||||
theme.update_colors(item)
|
||||
end
|
||||
|
||||
return setmetatable(module, { __call = function(_, ...) return draw(...) end })
|
||||
end
|
||||
|
||||
return setmetatable({
|
||||
basic = require("radical.item.style.basic" ),
|
||||
classic = require("radical.item.style.classic" ),
|
||||
subtle = require("radical.item.style.subtle" ),
|
||||
|
@ -15,4 +41,4 @@ return {
|
|||
arrow_3d = require("radical.item.style.arrow_3d" ),
|
||||
slice_prefix = require("radical.item.style.slice_prefix" ),
|
||||
line_3d = require("radical.item.style.line_3d" ),
|
||||
}
|
||||
}, {__call=generic})
|
||||
|
|
|
@ -5,101 +5,13 @@ local util = require( "awful.util" )
|
|||
local button = require( "awful.button" )
|
||||
local checkbox = require( "radical.widgets.checkbox" )
|
||||
local wibox = require( "wibox" )
|
||||
local common = require( "radical.common" )
|
||||
local item_layout = require("radical.item.layout.icon")
|
||||
local base = nil
|
||||
|
||||
local module = {}
|
||||
|
||||
local function left(data)
|
||||
if data._current_item._tmp_menu then
|
||||
data = data._current_item._tmp_menu
|
||||
data.items.selected = true
|
||||
return true,data
|
||||
end
|
||||
end
|
||||
|
||||
local function right(data)
|
||||
if data.parent_geometry.is_menu then
|
||||
for k,v in ipairs(data.items) do
|
||||
if v._tmp_menu == data or v.sub_menu_m == data then
|
||||
v.selected = true
|
||||
end
|
||||
end
|
||||
data.visible = false
|
||||
data = data.parent_geometry
|
||||
return true,data
|
||||
end
|
||||
end
|
||||
|
||||
local function up(data)
|
||||
data.previous_item.selected = true
|
||||
end
|
||||
|
||||
local function down(data)
|
||||
data.next_item.selected = true
|
||||
end
|
||||
|
||||
function module:setup_key_hooks(data)
|
||||
data:add_key_hook({}, "Up" , "press", up )
|
||||
data:add_key_hook({}, "&" , "press", up )
|
||||
data:add_key_hook({}, "Down" , "press", down )
|
||||
data:add_key_hook({}, "KP_Enter", "press", down )
|
||||
data:add_key_hook({}, "Left" , "press", left )
|
||||
data:add_key_hook({}, "\"" , "press", left )
|
||||
data:add_key_hook({}, "Right" , "press", right )
|
||||
data:add_key_hook({}, "#" , "press", right )
|
||||
end
|
||||
|
||||
local function setup_event(data,item,args)
|
||||
--Event handling
|
||||
if data.select_on == base.event.HOVER then
|
||||
item.widget:connect_signal("mouse::enter", function() item.selected = true end)
|
||||
item.widget:connect_signal("mouse::leave", function() item.selected = false end)
|
||||
end
|
||||
data._internal.layout:add(item)
|
||||
local buttons = {}
|
||||
for i=1,10 do
|
||||
if args["button"..i] then
|
||||
buttons[#buttons+1] = button({},i,args["button"..i])
|
||||
end
|
||||
end
|
||||
if not buttons[3] then --Hide on right click
|
||||
buttons[#buttons+1] = button({},3,function()
|
||||
data.visible = false
|
||||
if data.parent_geometry and data.parent_geometry.is_menu then
|
||||
data.parent_geometry.visible = false
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--Be sure to always hide sub menus, even when data.visible is set manually
|
||||
data:connect_signal("visible::changed",function(_,vis)
|
||||
if data._tmp_menu and data.visible == false then
|
||||
data._tmp_menu.visible = false
|
||||
end
|
||||
end)
|
||||
data:connect_signal("parent_geometry::changed",function(_,vis)
|
||||
local fit_w,fit_h = data._internal.layout:fit()
|
||||
data.height = fit_h
|
||||
if data.style then
|
||||
data.style(data)
|
||||
end
|
||||
end)
|
||||
item.widget:buttons( util.table.join(unpack(buttons)))
|
||||
end
|
||||
|
||||
function module:setup_item(data,item,args)
|
||||
local bg = item_layout(item,data,args)
|
||||
|
||||
-- Set size
|
||||
local fit_w,fit_h = data._internal.layout:fit()
|
||||
data.width = fit_w
|
||||
data.height = fit_h
|
||||
if data.style then
|
||||
data.style(data)
|
||||
end
|
||||
local text_w = item._internal.text_w
|
||||
local icon_w = item._internal.icon_w
|
||||
|
||||
-- Setup text
|
||||
item.set_text = function (_,value)
|
||||
|
@ -118,50 +30,29 @@ function module:setup_item(data,item,args)
|
|||
end
|
||||
end
|
||||
end
|
||||
item.set_icon = function (_,value)
|
||||
icon_w:set_image(value)
|
||||
end
|
||||
|
||||
item:set_text(item._private_data.text)
|
||||
|
||||
-- Setup tooltip
|
||||
bg:set_tooltip(item.tooltip)
|
||||
|
||||
-- Set widget
|
||||
item.widget = bg
|
||||
data.item_style(item,{})
|
||||
setup_event(data,item,args)
|
||||
end
|
||||
|
||||
--Get preferred item geometry
|
||||
local function item_fit(data,item,self, content, width, height)
|
||||
if not data.visible then return 1,1 end
|
||||
local w, h = item._private_data._fit(self,content,width,height) --TODO port to new context API
|
||||
return data.item_width or 70, item._private_data.height or h
|
||||
return data.item_width or 70, item._private_data.height or h --TODO broken
|
||||
end
|
||||
|
||||
local function new(data)
|
||||
if not base then
|
||||
base = require( "radical.base" )
|
||||
end
|
||||
|
||||
-- Define the item layout
|
||||
local real_l = wibox.widget.base.make_widget_declarative {
|
||||
spacing = data.spacing ,
|
||||
item_fit = item_fit ,
|
||||
setup_key_hooks = module.setup_key_hooks ,
|
||||
setup_key_hooks = common.setup_key_hooks ,
|
||||
setup_item = module.setup_item ,
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}
|
||||
|
||||
data:connect_signal("widget::added",function(_,item,widget)
|
||||
wibox.layout.fixed.add(real_l, item.widget)
|
||||
real_l:emit_signal("widget::updated")
|
||||
end)
|
||||
|
||||
function real_l:add(item)
|
||||
return wibox.layout.fixed.add(self, item.widget)
|
||||
end
|
||||
|
||||
-- Hack fit
|
||||
local new_fit
|
||||
new_fit = function(self,context,w,h,force_values) --TODO use the context instead of extra argument
|
||||
|
|
|
@ -4,194 +4,93 @@ local scroll = require( "radical.widgets.scroll" )
|
|||
local filter = require( "radical.widgets.filter" )
|
||||
local wibox = require( "wibox" )
|
||||
local cairo = require( "lgi" ).cairo
|
||||
local base = nil
|
||||
local common = require( "radical.common" )
|
||||
local horizontal_item_layout= require( "radical.item.layout.horizontal" )
|
||||
|
||||
local module = {}
|
||||
|
||||
local function left(data)
|
||||
if data._current_item._tmp_menu then
|
||||
data = data._current_item._tmp_menu
|
||||
data.items[1].selected = true
|
||||
return true,data
|
||||
end
|
||||
end
|
||||
|
||||
local function right(data)
|
||||
if data.parent_geometry and data.parent_geometry.is_menu then
|
||||
for k,v in ipairs(data.items) do
|
||||
if v._tmp_menu == data or v.sub_menu_m == data then
|
||||
v.selected = true
|
||||
end
|
||||
end
|
||||
data.visible = false
|
||||
data = data.parent_geometry
|
||||
return true,data
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function up(data)
|
||||
data.previous_item.selected = true
|
||||
end
|
||||
|
||||
local function down(data)
|
||||
data.next_item.selected = true
|
||||
end
|
||||
|
||||
function module:setup_key_hooks(data)
|
||||
data:add_key_hook({}, "Up" , "press", up )
|
||||
data:add_key_hook({}, "&" , "press", up ) -- Xephyr bug
|
||||
data:add_key_hook({}, "Down" , "press", down )
|
||||
data:add_key_hook({}, "KP_Enter", "press", down ) -- Xephyr bug
|
||||
data:add_key_hook({}, "Left" , "press", right )
|
||||
data:add_key_hook({}, "\"" , "press", right ) -- Xephyr bug
|
||||
data:add_key_hook({}, "Right" , "press", left )
|
||||
data:add_key_hook({}, "#" , "press", left ) -- Xephyr bug
|
||||
end
|
||||
|
||||
--Get preferred item geometry
|
||||
local function item_fit(data,item,self,context, width,height)
|
||||
local w, h = 0,0--item._internal.cache_w or 1,item._internal.cache_h or 1
|
||||
if data.visible then
|
||||
w, h = item._private_data._fit({},self,context,width,height)
|
||||
item._internal.pix_cache = {} --Clear the pimap cache
|
||||
end
|
||||
local w, h = 0,0--item._internal.cache_w or 1,item._internal.cache_h or 1
|
||||
if data.visible then
|
||||
w, h = item._private_data._fit({},self,context,width,height)
|
||||
end
|
||||
|
||||
return w, item.height or h
|
||||
return w, item.height or h
|
||||
end
|
||||
|
||||
function module:setup_text(item,data,text_w)
|
||||
local text_w = item._internal.text_w
|
||||
function module.setup_text(item,data,text_w)
|
||||
local text_w = item._internal.text_w
|
||||
if not text_w then return end
|
||||
|
||||
text_w.draw = function(self,context, cr, width, height)
|
||||
if item.underlay then
|
||||
horizontal_item_layout.paint_underlay(data,item,cr,width,height)
|
||||
text_w.draw = function(self,context, cr, width, height)
|
||||
if item.underlay then
|
||||
horizontal_item_layout.paint_underlay(data,item,cr,width,height)
|
||||
end
|
||||
wibox.widget.textbox.draw(self, context, cr, width, height)
|
||||
end
|
||||
wibox.widget.textbox.draw(self, context, cr, width, height)
|
||||
end
|
||||
text_w.fit = function(self,context,width,height) return width,height end
|
||||
text_w.fit = function(self,context,width,height) return width,height end
|
||||
|
||||
item.set_text = function (_,value)
|
||||
if data.disable_markup then
|
||||
text_w:set_text(value)
|
||||
else
|
||||
text_w:set_markup(value)
|
||||
item.set_text = function (_,value)
|
||||
if data.disable_markup then
|
||||
text_w:set_text(value)
|
||||
else
|
||||
text_w:set_markup(value)
|
||||
end
|
||||
item._private_data.text = value
|
||||
end
|
||||
item._private_data.text = value
|
||||
end
|
||||
item:set_text(item._private_data.text)
|
||||
return text_w
|
||||
|
||||
item:set_text(item._private_data.text)
|
||||
|
||||
return text_w
|
||||
end
|
||||
|
||||
function module:setup_item(data,item,args)
|
||||
if not base then
|
||||
base = require( "radical.base" )
|
||||
end
|
||||
--Create the background
|
||||
local item_layout = item.layout or data.item_layout or horizontal_item_layout
|
||||
item.widget = item_layout(item,data,args)
|
||||
|
||||
--Event handling
|
||||
if data.select_on == base.event.HOVER then
|
||||
item.widget:connect_signal("mouse::enter", function(_,geo)
|
||||
item.y = geo.y
|
||||
item.selected = true
|
||||
end)
|
||||
item.widget:connect_signal("mouse::leave", function()
|
||||
item.selected = false
|
||||
end)
|
||||
else
|
||||
item.widget:connect_signal("mouse::enter", function(_,geo)
|
||||
item.y = geo.y
|
||||
item.hover = true
|
||||
end)
|
||||
item.widget:connect_signal("mouse::leave", function()
|
||||
item.hover = false
|
||||
end)
|
||||
end
|
||||
data._internal.layout:add(item)
|
||||
|
||||
--Be sure to always hide sub menus, even when data.visible is set manually
|
||||
data:connect_signal("visible::changed",function(_,vis)
|
||||
if data._tmp_menu and data.visible == false then
|
||||
data._tmp_menu.visible = false
|
||||
item._private_data._fit = wibox.widget.background.fit
|
||||
if item._internal.margin_w then
|
||||
item._internal.margin_w.fit = function(...)
|
||||
if (item.visible == false or item._filter_out == true or item._hidden == true) then
|
||||
return 0,0
|
||||
end
|
||||
return item_fit(data,item,...)
|
||||
end
|
||||
end
|
||||
end)
|
||||
data:connect_signal("parent_geometry::changed",function(_,vis)
|
||||
local fit_w,fit_h = data._internal.layout:fit()
|
||||
data.height = fit_h
|
||||
data.style(data)
|
||||
end)
|
||||
|
||||
item._private_data._fit = wibox.widget.background.fit
|
||||
if item._internal.margin_w then
|
||||
item._internal.margin_w.fit = function(...)
|
||||
if (item.visible == false or item._filter_out == true or item._hidden == true) then
|
||||
return 0,0
|
||||
end
|
||||
return data._internal.layout.item_fit(data,item,...)
|
||||
-- Text need to take as much space as possible, override default
|
||||
module.setup_text(item,data)
|
||||
|
||||
-- Compute the minimum width
|
||||
if data.auto_resize and item._internal.margin_w then
|
||||
local fit_w = wibox.layout.margin.fit(item._internal.margin_w,{dpi=96},9999,9999)
|
||||
local is_largest = item == data._internal.largest_item_w
|
||||
if fit_w < 1000 and (not data._internal.largest_item_w_v or data._internal.largest_item_w_v < fit_w) then
|
||||
data._internal.largest_item_w = item
|
||||
data._internal.largest_item_w_v = fit_w
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Text need to take as much space as possible, override default
|
||||
module:setup_text(item,data)
|
||||
|
||||
-- Necessary for :set_position()
|
||||
local fit_w,fit_h = data._internal.layout:fit()
|
||||
|
||||
data.width = fit_w
|
||||
data.height = fit_h
|
||||
|
||||
-- Enable scrollbar if necessary
|
||||
if data._internal.scroll_w and data.rowcount > data.max_items then
|
||||
data._internal.scroll_w.visible = true
|
||||
data._internal.scroll_w["up"]:emit_signal("widget::updated")
|
||||
data._internal.scroll_w["down"]:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
-- Setup tooltip
|
||||
item.widget:set_tooltip(item.tooltip)
|
||||
|
||||
-- Apply item style
|
||||
local item_style = item.item_style or data.item_style
|
||||
item_style(item,{})
|
||||
|
||||
-- Compute the minimum width
|
||||
if data.auto_resize and item._internal.margin_w then
|
||||
local fit_w = wibox.layout.margin.fit(item._internal.margin_w,{},9999,9999)
|
||||
local is_largest = item == data._internal.largest_item_w
|
||||
if fit_w < 1000 and (not data._internal.largest_item_w_v or data._internal.largest_item_w_v < fit_w) then
|
||||
data._internal.largest_item_w = item
|
||||
data._internal.largest_item_w_v = fit_w
|
||||
end
|
||||
end
|
||||
|
||||
item.widget:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
local function compute_geo(data,width,height,force_values)
|
||||
local w = data.default_width
|
||||
if data.auto_resize and data._internal.largest_item_w then
|
||||
w = data._internal.largest_item_w_v > data.default_width and data._internal.largest_item_w_v or data.default_width
|
||||
end
|
||||
local w = data.default_width
|
||||
if data.auto_resize and data._internal.largest_item_w then
|
||||
w = data._internal.largest_item_w_v > data.default_width and data._internal.largest_item_w_v or data.default_width
|
||||
end
|
||||
|
||||
local visblerow = data.visible_row_count
|
||||
local visblerow = data.visible_row_count
|
||||
|
||||
local sw,sh = data._internal.suf_l:get_preferred_size()
|
||||
local pw,ph = data._internal.pref_l:get_preferred_size()
|
||||
if not data._internal.has_widget then
|
||||
return w,(total and total > 0 and total or visblerow*data.item_height) + ph + sh
|
||||
else
|
||||
local sumh = data.widget_fit_height_sum
|
||||
local h = (visblerow-#data._internal.widgets)*data.item_height + sumh
|
||||
return w,h
|
||||
end
|
||||
local sw,sh = data._internal.suf_l:get_preferred_size()
|
||||
local pw,ph = data._internal.pref_l:get_preferred_size()
|
||||
if not data._internal.has_widget then
|
||||
return w,(total and total > 0 and total or visblerow*data.item_height) + ph + sh
|
||||
else
|
||||
local sumh = data.widget_fit_height_sum
|
||||
local h = (visblerow-#data._internal.widgets)*data.item_height + sumh
|
||||
return w,h
|
||||
end
|
||||
end
|
||||
|
||||
local function new(data)
|
||||
base = base or require( "radical.base" )
|
||||
|
||||
local function real_fit(self,context,o_w,o_h,force_values)
|
||||
if not data.visible then return 1,1 end
|
||||
|
@ -200,10 +99,6 @@ local function new(data)
|
|||
return w,h
|
||||
end
|
||||
|
||||
local function real_add(self, item)
|
||||
return wibox.layout.fixed.add(data._internal.content_layout, item.widget)
|
||||
end
|
||||
|
||||
-- Create the scroll widgets
|
||||
if data.max_items then
|
||||
data._internal.scroll_w = scroll(data)
|
||||
|
@ -250,7 +145,7 @@ local function new(data)
|
|||
|
||||
-- Methods
|
||||
item_fit = item_fit ,
|
||||
setup_key_hooks = module.setup_key_hooks,
|
||||
setup_key_hooks = common.setup_key_hooks,
|
||||
setup_item = module.setup_item ,
|
||||
}
|
||||
|
||||
|
@ -262,46 +157,9 @@ local function new(data)
|
|||
|
||||
-- Set the overloaded methods
|
||||
real_l.fit = real_fit
|
||||
real_l.add = real_add
|
||||
|
||||
local l = data._internal.content_layout
|
||||
|
||||
--SWAP / MOVE / REMOVE
|
||||
data:connect_signal("item::swapped",function(_,item1,item2,index1,index2)
|
||||
l:swap(index1, index2)
|
||||
end)
|
||||
|
||||
data:connect_signal("item::moved",function(_,item,new_idx,old_idx)
|
||||
local w = l:get_children()[old_idx]
|
||||
l:remove(old_idx)
|
||||
l:insert(new_idx, w)
|
||||
end)
|
||||
|
||||
data:connect_signal("item::removed",function(_,item,old_idx)
|
||||
l:remove(old_idx)
|
||||
end)
|
||||
|
||||
data:connect_signal("item::appended",function(_,item)
|
||||
l:add(item.widget)
|
||||
end)
|
||||
|
||||
data:connect_signal("widget::added",function(_,item,widget)
|
||||
wibox.layout.fixed.add(l,item.widget)
|
||||
l:emit_signal("widget::updated")
|
||||
end)
|
||||
|
||||
data:connect_signal("prefix_widget::added",function(_,widget,args)
|
||||
data._internal.pref_l:insert(1,widget)
|
||||
end)
|
||||
|
||||
data:connect_signal("suffix_widget::added",function(_,widget,args)
|
||||
data._internal.suf_l:add(widget)
|
||||
end)
|
||||
|
||||
data._internal.text_fit = function(self, context, width, height)
|
||||
return width,height
|
||||
end
|
||||
|
||||
return real_l
|
||||
end
|
||||
|
||||
|
|
278
style/arrow.lua
278
style/arrow.lua
|
@ -1,194 +1,140 @@
|
|||
local setmetatable = setmetatable
|
||||
local beautiful = require( "beautiful" )
|
||||
local color = require( "gears.color" )
|
||||
local surface = require( "gears.surface" )
|
||||
local cairo = require( "lgi" ).cairo
|
||||
local base = require( "radical.base" )
|
||||
local glib = require("lgi").GLib
|
||||
local shape = require( "gears.shape" )
|
||||
local unpack = unpack or table.unpack
|
||||
local beautiful = require( "beautiful" )
|
||||
local color = require( "gears.color" )
|
||||
local cairo = require( "lgi" ).cairo
|
||||
local base = require( "radical.base" )
|
||||
local shape = require( "gears.shape" )
|
||||
|
||||
local module = {
|
||||
margins = {
|
||||
BOTTOM = 10,
|
||||
TOP = 10,
|
||||
LEFT = 0 ,
|
||||
RIGHT = 0 ,
|
||||
}
|
||||
margins = {
|
||||
BOTTOM = 10,
|
||||
TOP = 10,
|
||||
LEFT = 0 ,
|
||||
RIGHT = 0 ,
|
||||
}
|
||||
}
|
||||
|
||||
-- Matrix rotation per direction
|
||||
local angles = {
|
||||
top = math.pi , -- 180
|
||||
bottom = 0 , -- 0
|
||||
left = math.pi/2 , -- 90
|
||||
right = 3*math.pi/2 , -- 270
|
||||
}
|
||||
|
||||
-- If width and height need to be swapped
|
||||
local swaps = {
|
||||
top = false,
|
||||
bottom= false,
|
||||
right = true ,
|
||||
left = true ,
|
||||
}
|
||||
|
||||
local invert = {
|
||||
top = "bottom",
|
||||
bottom = "top" ,
|
||||
right = "left" , --FIXME this is wrong
|
||||
left = "right" , --FIXME this is wrong
|
||||
}
|
||||
|
||||
-- Constants
|
||||
local radius = 10
|
||||
local arrow_height = 13
|
||||
|
||||
-- Matrix rotation per direction
|
||||
local angles = {
|
||||
top = 0 , -- 0
|
||||
bottom = math.pi , -- 180
|
||||
left = 3*math.pi/2 , -- 270
|
||||
right = math.pi/2 , -- 90
|
||||
}
|
||||
|
||||
-- If width and height need to be swapped
|
||||
local swaps = {
|
||||
top = false,
|
||||
bottom= false,
|
||||
right = true ,
|
||||
left = true ,
|
||||
}
|
||||
|
||||
-- Generate the arrow position
|
||||
local function gen_arrow_x(data,direction)
|
||||
local at = data.arrow_type
|
||||
local par_center_x = data.parent_geometry and (data.parent_geometry.x + data.parent_geometry.width/2) or -1
|
||||
local menu_beg_x = data.x
|
||||
local function gen_arrow_x(data,direction, width, height)
|
||||
local at = data.arrow_type
|
||||
local par_center_x = data.wibox.width/2
|
||||
|
||||
if at == base.arrow_type.PRETTY or not at then
|
||||
if direction == "left" then
|
||||
data._arrow_x = data._internal.w.height -20 - (data.arrow_x_orig or 20)
|
||||
elseif direction == "right" then
|
||||
--TODO
|
||||
elseif direction == "bottom" then
|
||||
data._arrow_x = data.width -20 - (data.arrow_x_orig or 20)
|
||||
if par_center_x >= menu_beg_x then
|
||||
data._arrow_x = data.width - (par_center_x - menu_beg_x) - arrow_height
|
||||
end
|
||||
elseif direction == "top" then
|
||||
--TODO
|
||||
if at == base.arrow_type.PRETTY or not at then
|
||||
if direction == "left" then
|
||||
data._arrow_x = data._internal.w.height -20 - (data.arrow_x_orig or 20)
|
||||
elseif direction == "right" then
|
||||
--TODO
|
||||
elseif direction == "bottom" then
|
||||
data._arrow_x = width -20 - (data.arrow_x_orig or 20)
|
||||
if par_center_x >= 0 then
|
||||
data._arrow_x = width - (par_center_x - 0) - arrow_height
|
||||
end
|
||||
elseif direction == "top" then
|
||||
--TODO
|
||||
end
|
||||
elseif at == base.arrow_type.CENTERED then
|
||||
if direction == "left" or direction == "right" then
|
||||
data._arrow_x = height/2 - arrow_height
|
||||
else
|
||||
data._arrow_x = width/2 - arrow_height
|
||||
end
|
||||
end
|
||||
elseif at == base.arrow_type.CENTERED then
|
||||
if direction == "left" or direction == "right" then
|
||||
data._arrow_x = data.height/2 - arrow_height
|
||||
else
|
||||
data._arrow_x = data.width/2 - arrow_height
|
||||
end
|
||||
|
||||
local function update_margins(data, pos)
|
||||
-- Set the margins correctly
|
||||
if data._internal.margin then
|
||||
data.margins.left = module.margins.LEFT
|
||||
data.margins.right = module.margins.RIGHT
|
||||
data.margins.top = module.margins.TOP
|
||||
data.margins.bottom = module.margins.BOTTOM
|
||||
|
||||
-- Add enough room for the arrow
|
||||
if pos and data.arrow_type ~= base.arrow_type.NONE then
|
||||
data.margins[invert[pos]] = data.margins[invert[pos]] + arrow_height
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate a rounded cairo path with the arrow
|
||||
local function draw_roundedrect_path(cr, width, height, radius, data, padding, angle, swap_size)
|
||||
local no_arrow = data.arrow_type == base.arrow_type.NONE
|
||||
local padding = padding or 0
|
||||
local arrow_offset = no_arrow and 0 or padding/2
|
||||
local width, height = width - 2*padding - (swap_size and arrow_offset or 0), height - 2*padding - (swap_size and 0 or arrow_offset)
|
||||
|
||||
if swap_size then
|
||||
width, height = height - arrow_offset, width
|
||||
end
|
||||
|
||||
-- Use rounded rext for sub-menu and
|
||||
local s = shape.transform(no_arrow and shape.rounded_rect or shape.infobubble)
|
||||
|
||||
-- Apply transformations
|
||||
s = s : rotate_at(width / 2, height / 2, angle)
|
||||
if padding > 0 then
|
||||
s = s : translate(padding + (swap_size and arrow_offset or 0), padding + (angle == 0 and arrow_offset or 0))
|
||||
end
|
||||
|
||||
-- Avoid a race condition
|
||||
if (not data._arrow_x) and (not no_arrow) then
|
||||
gen_arrow_x(data, data.direction)
|
||||
end
|
||||
|
||||
-- the (swap_size and 2 or 1) indicate a bug elsewhere
|
||||
s(cr, width, height, radius, arrow_height - arrow_offset, (data._arrow_x or 20) - arrow_offset*(swap_size and 2 or 1))
|
||||
|
||||
end
|
||||
|
||||
local function _set_direction(data,direction)
|
||||
local hash = data.height*1000 + data.width
|
||||
|
||||
-- Try not to waste time for nothing
|
||||
if data._internal._last_direction == direction..(hash) then return end
|
||||
|
||||
-- Avoid recomputing the arrow_x value
|
||||
if not data._arrow_x or data._internal.last_size ~= hash then
|
||||
gen_arrow_x(data,direction)
|
||||
data._internal.last_size = hash
|
||||
end
|
||||
|
||||
local angle, swap = angles[direction],swaps[direction]
|
||||
|
||||
data._internal._need_direction_reload = false
|
||||
data._internal._last_direction = direction..(hash)
|
||||
|
||||
surface.apply_shape_bounding(data.wibox, draw_roundedrect_path, radius, data, 0, angle, swap)
|
||||
-- surface.apply_shape_clip (data.wibox, draw_roundedrect_path, radius, data, data.border_width, angle, swap)
|
||||
end
|
||||
|
||||
-- Try to avoid useless repaint, this function is heavy
|
||||
local function set_direction(data,direction)
|
||||
data._internal._need_direction = direction
|
||||
if not data._internal._need_direction_reload and data._internal._last_direction ~= direction..(data.height*1000+data.width) then
|
||||
glib.idle_add(glib.PRIORITY_HIGH_IDLE, function() _set_direction(data,data._internal._need_direction) end)
|
||||
data._internal._need_direction_reload = true
|
||||
end
|
||||
|
||||
-- Margins need to set manually, a reset will override user changes
|
||||
if data.arrow_type ~= base.arrow_type.NONE and (not (data.parent_geometry and data.parent_geometry.is_menu)) and data._internal.former_direction ~= direction then
|
||||
if data._internal.former_direction then
|
||||
data.margins[data._internal.former_direction] = data.border_width + module.margins[data._internal.former_direction:upper()]
|
||||
local function draw_roundedrect_path(cr, width, height, radius, data, position)
|
||||
if data.arrow_type == base.arrow_type.NONE then
|
||||
return shape.rounded_rect(cr, width, height, radius)
|
||||
end
|
||||
data.margins[direction] = arrow_height + 2*data.border_width
|
||||
end
|
||||
data._internal.former_direction = direction
|
||||
|
||||
local angle, swap = angles[position], swaps[position]
|
||||
|
||||
-- Invert width and height to avoid distortion
|
||||
if swap then
|
||||
width, height = height, width
|
||||
end
|
||||
|
||||
-- Use rounded rext for sub-menu and
|
||||
local s = shape.transform(shape.infobubble)
|
||||
|
||||
-- Apply transformations
|
||||
s = s : rotate_at(width / 2, height / 2, angle)
|
||||
|
||||
-- Decide where the arrow will be
|
||||
gen_arrow_x(data, data.direction, width, height)
|
||||
|
||||
-- Forward to the real shape
|
||||
local ax = swap and width - (data._arrow_x or 20) or (data._arrow_x or 20)
|
||||
s(cr, width, height, radius, arrow_height, ax)
|
||||
end
|
||||
|
||||
local function get_arrow_x(data)
|
||||
local height,width = data.height,data.width
|
||||
local hash = height*1000+width
|
||||
if not data._arrow_x or data._internal.last_size ~= hash then
|
||||
gen_arrow_x(data,direction)
|
||||
data._internal.last_size = hash
|
||||
end
|
||||
return data._arrow_x
|
||||
end
|
||||
|
||||
-- As the menus have a rounded border, rectangle elements will draw over the
|
||||
-- corner border. To fix this, this method re-draw the border on top of the
|
||||
-- content
|
||||
local function after_draw_children(self, context, cr, width, height)
|
||||
local data = self._data
|
||||
|
||||
local dir = data.direction
|
||||
local angle, swap = angles[dir], swaps[dir]
|
||||
|
||||
cr:translate(data.border_width/2,data.border_width/2)
|
||||
|
||||
-- Generate the path
|
||||
draw_roundedrect_path(cr, width, height, beautiful.menu_corner_radius or radius, data, data.border_width/2, angle, swap)
|
||||
cr:set_source(color(beautiful.menu_outline_color or beautiful.menu_border_color or beautiful.fg_normal))
|
||||
cr:set_line_width(data.border_width)
|
||||
cr:stroke()
|
||||
end
|
||||
|
||||
|
||||
local function draw(data,args)
|
||||
local args = args or {}
|
||||
local direction = data.direction or "top"
|
||||
if not data.get_arrow_x then
|
||||
rawset(data,"arrow_x_orig",data.arrow_x)
|
||||
rawset(data,"arrow_x_orig",nil)
|
||||
data.get_arrow_x = get_arrow_x
|
||||
-- Prevent sharp corners from being over the border
|
||||
if data._internal.margin then
|
||||
data._internal.margin.__draw = data._internal.margin.draw
|
||||
local args = args or {}
|
||||
|
||||
--TODO eventually restart work on upstreaming this, for now it pull too
|
||||
-- much trouble along with it
|
||||
data._internal.margin._data = data
|
||||
data._internal.margin.after_draw_children = after_draw_children
|
||||
if not data._internal.arrow_setup then
|
||||
data._internal.w:set_shape_border_width(data.border_width or 1)
|
||||
data._internal.w:set_shape_border_color(color(beautiful.menu_outline_color or beautiful.menu_border_color or beautiful.fg_normal))
|
||||
data._internal.w:set_shape(data.shape or shape.infobubble, unpack(data.shape_args or {}))
|
||||
|
||||
if not data._internal.margin._data then
|
||||
data._internal.margin._data = data
|
||||
end
|
||||
data._internal.w:connect_signal("property::position", function(_, pos)
|
||||
data._internal.w:set_shape(function(cr, w, h) draw_roundedrect_path(cr, w, h, radius, data, pos) end)
|
||||
update_margins(data, pos)
|
||||
end)
|
||||
|
||||
local pos = data._internal.w.position
|
||||
if pos then
|
||||
data._internal.w:set_shape(function(cr, w, h) draw_roundedrect_path(cr, w, h, radius, data, data._internal.w.position) end)
|
||||
end
|
||||
update_margins(data, pos)
|
||||
|
||||
data._internal.arrow_setup = true
|
||||
end
|
||||
end
|
||||
|
||||
set_direction(data,direction)
|
||||
|
||||
--TODO call this less often
|
||||
return w,w2
|
||||
end
|
||||
|
||||
return setmetatable(module, { __call = function(_, ...) return draw(...) end })
|
||||
-- kate: space-indent on; indent-width 2; replace-tabs on;
|
||||
-- kate: space-indent on; indent-width 4; replace-tabs on;
|
||||
|
|
Loading…
Reference in New Issue