Use betters maths for the Radial menu

The old version is terrible... How much did I drink when I made
that... Well, it was when I was unexperienced with cairo, but...
Anyway, the new maths will eventually allow to use hinting
and Pango with good results. There is also less magic constants.
This commit is contained in:
Emmanuel Lepage Vallee 2014-12-18 16:13:10 -05:00
parent 9f121b1d42
commit 8fe10a1a9d
2 changed files with 112 additions and 30 deletions

View File

@ -288,7 +288,9 @@ capi.tag.connect_signal("property::index2",function(t,i)
local item = cache[t]
if item then
instances[s]:move(item,i)
item.tw:set_markup(" <b>"..(i).."</b> ")
if item.tw then
item.tw:set_markup(" <b>"..(i).."</b> ")
end
end
end
end)

View File

@ -21,6 +21,75 @@ local capi = { mouse = mouse, mousegrabber = mousegrabber }
local module={}
-- Generate a cached pixmap starting at pi/2-(2*pi / seg_count)
-- This mask is then rotated in place where desired
-- the reason for this is to keep the code simple and allow
-- a menu rotate "animation" with the scroll wheel
local function gen_text_mask(data, layer, segs, text)
-- Step 1: compute the minimal size
--TODO limit to the radius
local h = (default.base_radius + layer*default.radius) * (segs < 2 and 2 or 1)
-- I let the reader make the proof of this: good luck with that
local dr = (math.pi*2)/segs
local w = segs < 2 and data.width or 2*math.abs(math.sin((dr)/2)*(data.width/2))
-- local w = data.width
-- Step 2: Create the surface
local img = cairo.ImageSurface(cairo.Format.ARGB32, w, h)
local cr = cairo.Context(img)
local border_width= 4
local start_angle = -(math.pi/2) - dr/2
local end_angle = -(math.pi/2) + dr/2
local cur_angle = start_angle
local cur_rad = h-border_width
cr:set_source_rgba(1,1,1,1)
cr:select_font_face("monospace")
local matrix = cairo.Matrix()
cairo.Matrix.init_translate(matrix,w/2,h)
for i=1,text:len() do
local c = text:sub(i,i)
-- Step 4: Create the transformation matrix
-- TODO use composite
local matrix12 = cairo.Matrix()
local ex = cr:text_extents(c)
cairo.Matrix.init_rotate(matrix12, cur_angle+math.pi/2)
matrix12:translate(-ex.width/2,ex.height/2)
local trext = cairo.Matrix()
cairo.Matrix.init_translate(trext,
cur_rad*math.cos(cur_angle),
cur_rad*math.sin(cur_angle)
)
local res = cairo.Matrix()
res:multiply(trext,matrix)
local res2 = cairo.Matrix()
res2:multiply(matrix12,res)
cr:set_matrix(res2)
-- Step 4: Paint
cr:text_path(c)
cr:fill()
-- Step 5: update the angle
cur_angle = cur_angle + 0.05 --TODO use trigonometry to copute this
if cur_angle > end_angle then
cur_angle = start_angle
cur_rad = cur_rad - border_width - ex.height
end
end
return img
end
function module.radial_client_select(args)
--Settings
local args = args or {}
@ -88,17 +157,18 @@ function module.radial_client_select(args)
local function gen_arc(layer)
data.layers[layer].position = (data.layers[layer].position or 0) + 1
local sef_count = #data.layers[layer].content
local position = data.layers[layer].position
local dat_layer = data.layers[layer]
local outer_radius = layer*default.radius + default.base_radius
local inner_radius = outer_radius - default.radius
local start_angle = ((2*math.pi)/(4*layer))*(position-1)
local end_angle = ((2*math.pi)/(4*layer))*(position)
local start_angle = ((2*math.pi)/sef_count)*(position-1)
local end_angle = ((2*math.pi)/sef_count)*(position)
dat_layer.cr:set_operator(cairo.Operator.SOURCE)
if data.layers[layer].selected == position then
dat_layer.cr:set_source ( color(beautiful.fg_focus) )
else
dat_layer.cr:set_source_rgb((5+(21-5)/(4*layer)*position)/256,(10+(119-10)/(4*layer)*position)/256,(27+(209-27)/(4*layer)*position)/256)
dat_layer.cr:set_source_rgb((5+(21-5)/sef_count*position)/256,(10+(119-10)/sef_count*position)/256,(27+(209-27)/sef_count*position)/256)
end
dat_layer.cr:move_to ( data.width/2, data.height/2 )
dat_layer.cr:arc ( data.width/2,data.height/2,outer_radius,start_angle,end_angle )
@ -118,30 +188,33 @@ function module.radial_client_select(args)
local step = (2*math.pi)/(((math.pi*2*(default.base_radius + default.radius*layer - 3 - level*12))/4)*0.65) --relation between arc and char width
local testAngle = start_angle + 0.05
cr2:select_font_face("monospace")
for i=1,text:len() do
cr2:set_operator(cairo.Operator.CLEAR)
cr2:paint()
cr2:set_operator(cairo.Operator.SOURCE)
cr2:set_source_rgb(1,1,1)
cr2:move_to(0,10)
cr2:text_path(text:sub(i,i))
cr2:fill()
local matrix12 = cairo.Matrix()
cairo.Matrix.init_rotate(matrix12, -testAngle )
matrix12:translate(-data.width/2+(default.base_radius + default.radius*layer - 3 - level*12)*(math.sin( - testAngle)),-data.height/2+(default.base_radius + default.radius*layer - 3 - level*12)*(math.cos( -testAngle)))
local pattern = cairo.Pattern.create_for_surface(img2,20,20)
pattern:set_matrix(matrix12)
cr:set_source(pattern)
cr:paint()
testAngle=testAngle+step
if testAngle+step > end_angle - 0.05 then
testAngle = start_angle+0.05
level = level +1
if level > 2 then
break
end
end
end
local img = gen_text_mask(data,layer,#data.layers[layer].content,text)
cr:set_source_surface(img,0,60)
cr:paint()
-- for i=1,text:len() do
-- cr2:set_operator(cairo.Operator.CLEAR)
-- cr2:paint()
-- cr2:set_operator(cairo.Operator.SOURCE)
-- cr2:set_source_rgb(1,1,1)
-- cr2:move_to(0,10)
-- cr2:text_path(text:sub(i,i))
-- cr2:fill()
-- local matrix12 = cairo.Matrix()
-- cairo.Matrix.init_rotate(matrix12, -testAngle )
-- matrix12:translate(-data.width/2+(default.base_radius + default.radius*layer - 3 - level*12)*(math.sin( - testAngle)),-data.height/2+(default.base_radius + default.radius*layer - 3 - level*12)*(math.cos( -testAngle)))
-- local pattern = cairo.Pattern.create_for_surface(img2,20,20)
-- pattern:set_matrix(matrix12)
-- cr:set_source(pattern)
-- cr:paint()
-- testAngle=testAngle+step
-- if testAngle+step > end_angle - 0.05 then
-- testAngle = start_angle+0.05
-- level = level +1
-- if level > 2 then
-- break
-- end
-- end
-- end
end
local function repaint_layer(idx,content)
@ -159,7 +232,10 @@ function module.radial_client_select(args)
else
real_rad = -real_rad
end
local new_selected = (idx*4 or 1) - math.floor(((real_rad*(idx*4 or 1))/2*math.pi)/10)
local count = #(lay.content or {})
local new_selected = count - math.floor( (real_rad)/(2*math.pi) * count )
if content or (lay.content and new_selected ~= lay.selected) then
lay.content = content or lay.content
lay.cr:set_operator(cairo.Operator.CLEAR)
@ -192,7 +268,10 @@ function module.radial_client_select(args)
local dr = (2*math.pi)/#data.layers[i].content
-- print("BLA BLA",k,i)
local r1 = dr*(k-1)
draw_text(cr,"1234567890123456789012345678901234567890123456789012345678901234567890",r1,r1+dr,i)
-- print(i,k,r1)
if i == 2 and k == 1 then
draw_text(cr,"1234567890123456789012345678901234567890123456789012345678901234567890",r1,r1+dr,i)
end
end
end
cr:set_source_surface(create_inner_circle(),0,0)
@ -221,6 +300,7 @@ function module.radial_client_select(args)
{name="test",icon="",func = function(menu,...) end },
{name="test",icon="",func = function(menu,...) end },
{name="test",icon="",func = function(menu,...) end },
{name="test",icon="",func = function(menu,...) end },
})
data:set_layer(2,{