195 lines
6.5 KiB
Lua
195 lines
6.5 KiB
Lua
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 module = {
|
|
margins = {
|
|
BOTTOM = 10,
|
|
TOP = 10,
|
|
LEFT = 0 ,
|
|
RIGHT = 0 ,
|
|
}
|
|
}
|
|
|
|
-- 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
|
|
|
|
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
|
|
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
|
|
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()]
|
|
end
|
|
data.margins[direction] = arrow_height + 2*data.border_width
|
|
end
|
|
data._internal.former_direction = direction
|
|
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
|
|
|
|
--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.margin._data then
|
|
data._internal.margin._data = data
|
|
end
|
|
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;
|