radical/style/arrow.lua

141 lines
4.3 KiB
Lua

local setmetatable = setmetatable
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 ,
}
}
-- 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
-- Generate the arrow position
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 = 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
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
-- Generate a rounded cairo path with the arrow
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
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 draw(data,args)
local args = args or {}
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 {}))
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
return w,w2
end
return setmetatable(module, { __call = function(_, ...) return draw(...) end })
-- kate: space-indent on; indent-width 4; replace-tabs on;