diff --git a/tests/examples/screen/template.lua b/tests/examples/screen/template.lua index b0f53f0dc..7a68725fe 100644 --- a/tests/examples/screen/template.lua +++ b/tests/examples/screen/template.lua @@ -76,12 +76,13 @@ local function stripe_pat(col, angle, line_width, spacing) end local colors = { - geometry = "#000000", - workarea = "#0000ff", - tiling_area = "#ff0000", - padding_area= "#ff0000", - wibar = "#000000", + geometry = "#000000", + workarea = "#0000ff", + tiling_area = "#ff0000", + padding_area = "#ff0000", + wibar = "#000000", tiling_client = "#ff0000", + gaps = "#9900ff", } local function draw_area(_, rect, name, offset, highlight) @@ -142,6 +143,7 @@ local function write_on_area_middle(rect, text, offset) cr:show_layout(playout) end +-- For clients/wibars with struts only. local function draw_struct(_, struct, name, offset, label) draw_solid_area(_, struct, name, offset) if type(label) == 'string' then @@ -149,6 +151,15 @@ local function draw_struct(_, struct, name, offset, label) end end +-- For floating or tiled clients. +local function draw_client(_, c, name, offset, label, alpha) + draw_solid_area(_, c, name, offset, alpha) + if type(label) == 'string' then + write_on_area_middle(c, label, offset) + end +end + + local function compute_ruler(_, rect, name) table.insert(hrulers, { label = name, x = rect.x, width = rect.width @@ -176,7 +187,37 @@ end local dx = 5 -local function show_ruler_label(offset, padding, ruler, playout) +local playout, playout_height = nil, nil + +local function get_playout() + if playout then return playout end + + local pctx = PangoCairo.font_map_get_default():create_context() + playout = Pango.Layout.new(pctx) + local pdesc = Pango.FontDescription() + pdesc:set_absolute_size(11 * Pango.SCALE) + playout:set_font_description(pdesc) + + return playout +end + +local function get_text_height() + if playout_height then return playout_height end + + local l = get_playout() + local attr, parsed = Pango.parse_markup("GeometryWorkareaPaddingMargins", -1, 0) + l.attributes, l.text = attr, parsed + + local _, logical = playout:get_pixel_extents() + + playout_height = logical.height + + return playout_height +end + +local function show_ruler_label(offset, padding, ruler, playout2) + if not ruler.label then return end + local lbl if ruler.x then @@ -187,13 +228,85 @@ local function show_ruler_label(offset, padding, ruler, playout) ruler.y.." height = "..ruler.height.."" end + local attr, parsed = Pango.parse_markup(lbl, -1, 0) + playout2.attributes, playout2.text = attr, parsed + local _, logical = playout2:get_pixel_extents() + cr:move_to((offset.x - logical.width) /2, offset.y+padding) + cr:show_layout(playout2) +end + +local function show_aligned_label(dx2, offset, padding, ruler) + local lbl + + if ruler.x then + lbl = ruler.width + else + lbl = ruler.height + end + local attr, parsed = Pango.parse_markup(lbl, -1, 0) playout.attributes, playout.text = attr, parsed local _, logical = playout:get_pixel_extents() - cr:move_to((offset.x - logical.width) /2, offset.y+padding) + + if ruler.x then + local off = (ruler.width*factor - logical.width)/2 + cr:move_to(ruler.x*factor+off, offset.y+padding) + else + local off = (ruler.height*factor - logical.width)/2 + cr:move_to(-ruler.y*factor-dx2*factor-off, padding) + end + cr:show_layout(playout) end +local function draw_vruler(s, dx2, sx, ruler, layer) + local pad = 5+(layer-1)*dx2 + cr:set_source(color(ruler.color or (colors[ruler.label].."66"))) + cr:move_to(sx+layer*dx2, ruler.y*factor) + cr:line_to(sx+layer*dx2, ruler.y*factor+ruler.height*factor) + cr:stroke() + + cr:move_to(sx+layer*dx2-2.5,ruler.y*factor) + cr:line_to(sx+layer*dx2+2.5, ruler.y*factor) + cr:stroke() + + cr:move_to(sx+layer*dx2-2.5,ruler.y*factor+ruler.height*factor) + cr:line_to(sx+layer*dx2+2.5, ruler.y*factor+ruler.height*factor) + cr:stroke() + + cr:save() + cr:move_to(sx+layer*dx2-2.5,ruler.y*factor) + cr:rotate(-math.pi/2) + if ruler and ruler.label then + show_ruler_label({x=-s.geometry.height*factor, y=sx}, pad, ruler, get_playout()) + elseif ruler and ruler.align then + show_aligned_label(dx2, {x=s.geometry.width*factor, y=0}, pad, ruler) + end + cr:restore() +end + +local function draw_hruler(s, dx2, sy, ruler, layer) + local pad = 10+(layer-1)*(dx2 or 0) + cr:set_source(color(ruler.color or (colors[ruler.label].."66"))) + cr:move_to(ruler.x*factor, sy+pad) + cr:line_to(ruler.x*factor+ruler.width*factor, sy+pad) + cr:stroke() + + cr:move_to(ruler.x*factor, sy+pad-2.5) + cr:line_to(ruler.x*factor, sy+pad+2.5) + cr:stroke() + + cr:move_to(ruler.x*factor+ruler.width*factor, sy+pad-2.5) + cr:line_to(ruler.x*factor+ruler.width*factor, sy+pad+2.5) + cr:stroke() + + if ruler and ruler.label then + show_ruler_label({x=s.geometry.width*factor, y=sy}, pad, ruler, get_playout()) + elseif ruler and ruler.align then + show_aligned_label(dx2, {x=s.geometry.width*factor, y=sy}, pad, ruler) + end +end + local function draw_rulers(s) -- The table has a maximum of 4 entries, the sort algorithm is irrelevant. while not bubble_sort(hrulers, "x", "width" ) do end @@ -205,67 +318,290 @@ local function draw_rulers(s) local sx = (s.geometry.x+s.geometry.width )*factor local sy = (s.geometry.y+s.geometry.height)*factor - - local pctx = PangoCairo.font_map_get_default():create_context() - local playout = Pango.Layout.new(pctx) - local pdesc = Pango.FontDescription() - pdesc:set_absolute_size(11 * Pango.SCALE) - playout:set_font_description(pdesc) - local attr, parsed = Pango.parse_markup("GeometryWorkareaPaddingMargins", -1, 0) - playout.attributes, playout.text = attr, parsed - local _, logical = playout:get_pixel_extents() - dx = logical.height + 10 + dx = get_text_height() + 10 for k, ruler in ipairs(vrulers) do - local pad = 5+(k-1)*dx - cr:set_source(color(colors[ruler.label].."66")) - cr:move_to(sx+k*dx, ruler.y*factor) - cr:line_to(sx+k*dx, ruler.y*factor+ruler.height*factor) - cr:stroke() - - cr:move_to(sx+k*dx-2.5,ruler.y*factor) - cr:line_to(sx+k*dx+2.5, ruler.y*factor) - cr:stroke() - - cr:move_to(sx+k*dx-2.5,ruler.y*factor+ruler.height*factor) - cr:line_to(sx+k*dx+2.5, ruler.y*factor+ruler.height*factor) - cr:stroke() - - cr:save() - cr:move_to(sx+k*dx-2.5,ruler.y*factor) - cr:rotate(-math.pi/2) - show_ruler_label({x=-s.geometry.height*factor, y=sx}, pad, ruler, playout) - cr:restore() + draw_vruler(s, dx, sx, ruler, k) end - for k, ruler in ipairs(hrulers) do - local pad = 10+(k-1)*dx - cr:set_source(color(colors[ruler.label].."66")) - cr:move_to(ruler.x*factor, sy+pad) - cr:line_to(ruler.x*factor+ruler.width*factor, sy+pad) - cr:stroke() - - cr:move_to(ruler.x*factor, sy+pad-2.5) - cr:line_to(ruler.x*factor, sy+pad+2.5) - cr:stroke() - - cr:move_to(ruler.x*factor+ruler.width*factor, sy+pad-2.5) - cr:line_to(ruler.x*factor+ruler.width*factor, sy+pad+2.5) - cr:stroke() - - show_ruler_label({x=s.geometry.width*factor, y=sy}, pad, ruler, playout) + draw_hruler(s, dx, sy, ruler, k) end end +local tr_x, tr_y = 0, 0 + +-- Not a very efficient way to do this, but at least it is simple. +local function deduplicate_gaps(gaps) + for _, gap1 in ipairs(gaps) do + for k, gap2 in ipairs(gaps) do + if gap1[2] == gap2[1] then + gap1[2] = gap2[2] + table.remove(gaps, k) + return true + elseif gap2[1] >= gap1[1] and gap2[2] <= gap1[2] and gap1 ~= gap2 then + table.remove(gaps, k) + return true + elseif gap1[1] == gap2[1] and gap2[2] == gap2[2] and gap1 ~= gap2 then + table.remove(gaps, k) + return true + end + end + end + + + return false +end + +local function get_gaps(s) + local ret = {vertical={gaps={}, content={}}, horizontal={gaps={}, content={}}} + + local gap = s.selected_tag.gap + + if gap == 0 then return ret end + + if s.selected_tag.gap_single_client == false and #s.tiled_clients == 1 then + return ret + end + + -- First, get all gaps. + for _, c in ipairs(s.tiled_clients) do + local bw = c.border_width + table.insert(ret.horizontal.gaps, {c.x-gap , c.x }) + table.insert(ret.vertical.gaps , {c.y-gap , c.y }) + table.insert(ret.horizontal.gaps, {c.x+c.width +2*bw, c.x+c.width+gap +2*bw}) + table.insert(ret.vertical.gaps , {c.y+c.height+2*bw, c.y+c.height+gap+2*bw}) + end + + -- Merge continuous gaps. + while deduplicate_gaps(ret.vertical.gaps ) do end + while deduplicate_gaps(ret.horizontal.gaps) do end + + return ret +end + +local function evaluate_translation(draw_gaps, draw_struts, draw_mwfact, draw_client_snap) + for s in screen do + if (draw_gaps and s.selected_tag and s.selected_tag.gap > 0) then + local gaps = get_gaps(s) + + -- Only add the space if there is something to display. + if #gaps.horizontal.gaps > 0 then + tr_y = math.max(tr_y, 3 * get_text_height()) + end + + if #gaps.vertical.gaps > 0 then + tr_x = math.max(tr_x, 2 * get_text_height()) + end + end + + if draw_client_snap or draw_struts then + tr_y = math.max(tr_y, 3 * get_text_height()) + tr_x = math.max(tr_x, 2 * get_text_height()) + end + + if draw_mwfact then + tr_y = math.max(tr_y, 3 * get_text_height()) + end + end +end + +local function translate() + cr:translate(tr_x, tr_y * 0.66) +end + +local function draw_gaps(s) + cr:translate(-tr_x, -tr_y) + + local gaps = get_gaps(s) + + local offset = s.tiling_area + + for _, hgap in ipairs(gaps.horizontal.gaps) do + draw_hruler( + s, + offset.x, + get_text_height(), + { + x = offset.x+hgap[1]+tr_x, + width = math.ceil(hgap[2]-hgap[1]), + label = nil, + color = colors.gaps.."66", + align = true + }, + 1 + ) + end + + for _, vgap in ipairs(gaps.vertical.gaps) do + draw_vruler( + s, + get_text_height()*1.5, + 0, + { + y = offset.y+vgap[1]+tr_y, + height = math.ceil(vgap[2]-vgap[1]), + label = nil, + color = colors.gaps.."66", + align = true + }, + 1 + ) + end + + cr:translate(tr_x, tr_y) +end + +local function has_struts(s) + for k, v in pairs(s.workarea) do + if s.geometry[k] ~= v then + return true + end + end + + return false +end + +local function draw_struts(s) + local left = s.workarea.x - s.geometry.x + local right = (s.geometry.x + s.geometry.width) - (s.workarea.x + s.workarea.width) + local top = s.workarea.y - s.geometry.y + local bottom = (s.geometry.y + s.geometry.height) - (s.workarea.y + s.workarea.height) + + cr:translate(-tr_x, -tr_y) + + if left > 0 then + draw_hruler( + s, + 0, + get_text_height(), + {x = s.geometry.x+tr_x*2, width = left, color = colors.gaps.."66", align = true}, + 1 + ) + end + + if top > 0 then + draw_vruler( + s, + get_text_height()*1.5, + 0, + {y=s.geometry.y+tr_y*(1/factor), height = top, color = colors.gaps.."66", align = true}, + 1 + ) + end + + if right > 0 then + draw_hruler( + s, + 0, + get_text_height(), + {x = s.geometry.x, width = left, color = colors.gaps.."66", align = true}, + 1 + ) + end + + if bottom > 0 then + draw_vruler( + s, + get_text_height()*1.5, + 0, + { + y = s.geometry.y+tr_y*(1/factor)+s.geometry.height - bottom, + height = bottom, + color = colors.gaps.."66", + align = true + }, + 1 + ) + end + + cr:translate(tr_x, tr_y) +end + +local function draw_mwfact(s) + cr:translate(-tr_x, -tr_y) + + local mwfact = s.selected_tag.master_width_factor + + local offset = s.tiling_area.x + local width = s.tiling_area.width + + local w1, w2 = math.ceil(width*mwfact), math.ceil(width*(1-mwfact)) + + draw_hruler(s, offset, get_text_height(), {x=offset,width=w1,color = colors.gaps.."66", align=true}, 1) + draw_hruler(s, offset, get_text_height(), {x=offset+w1,width=w2,color = colors.gaps.."66", align=true}, 1) + + cr:translate(tr_x, tr_y) +end + +local function draw_client_snap(s) + cr:translate(-tr_x, -tr_y) + + local snap_areas = { + vertical ={}, + horizontal={} + } + + local d = require("awful.mouse.snap").default_distance + + for _, c in ipairs(s.clients) do + if c.floating then + table.insert(snap_areas.horizontal, {c.x-d, c.x}) + table.insert(snap_areas.horizontal, {c.x+c.width, c.x+c.width+d}) + table.insert(snap_areas.vertical , {c.y-d, c.y}) + table.insert(snap_areas.vertical , {c.y+c.height, c.y+c.height+d}) + end + end + + while deduplicate_gaps(snap_areas.horizontal) do end + while deduplicate_gaps(snap_areas.vertical ) do end + + --FIXME + --[[for _, hgap in ipairs(snap_areas.horizontal) do + draw_hruler( + s, + 0, + get_text_height(), + {x=tr_x+s.workarea.x+hgap[1],width=hgap[2]-hgap[1],label="gaps"}, + 1 + ) + end + + for _, vgap in ipairs(snap_areas.vertical) do + draw_vruler( + s, + get_text_height()*1.5, + 0, + {y=tr_y+vgap[1],height=vgap[2]-vgap[1],label="gaps"}, + 1 + ) + end]]-- + + cr:translate(tr_x, tr_y) +end + +-- local function draw_screen_snap(s) +-- local d = require("awful.mouse.snap").aerosnap_distance +-- +-- +-- local proxy = { +-- x = c.x - sd, +-- y = c.y - sd, +-- width = c.width + 2*sd, +-- height = c.height + 2*sd, +-- } +-- +-- draw_client(s, proxy, 'gaps', (k-1)*10, nil, "11") +-- end + local function draw_info(s) cr:set_source_rgb(0, 0, 0) - local pctx = PangoCairo.font_map_get_default():create_context() - local playout = Pango.Layout.new(pctx) - local pdesc = Pango.FontDescription() + local pctx = PangoCairo.font_map_get_default():create_context() + local playout2 = Pango.Layout.new(pctx) + local pdesc = Pango.FontDescription() pdesc:set_absolute_size(11 * Pango.SCALE) - playout:set_font_description(pdesc) + playout2:set_font_description(pdesc) local rows = { "primary", "index", "geometry", "dpi", "dpi range", "outputs" @@ -298,13 +634,13 @@ local function draw_info(s) -- Get the extents of the longest label. for k, label in ipairs(rows) do local attr, parsed = Pango.parse_markup(label..":", -1, 0) - playout.attributes, playout.text = attr, parsed - local _, logical = playout:get_pixel_extents() + playout2.attributes, playout2.text = attr, parsed + local _, logical = playout2:get_pixel_extents() col1_width = math.max(col1_width, logical.width+10) attr, parsed = Pango.parse_markup(values[k], -1, 0) - playout.attributes, playout.text = attr, parsed - _, logical = playout:get_pixel_extents() + playout2.attributes, playout2.text = attr, parsed + _, logical = playout2:get_pixel_extents() col2_width = math.max(col2_width, logical.width+10) height = math.max(height, logical.height) @@ -316,15 +652,15 @@ local function draw_info(s) -- Draw everything. for k, label in ipairs(rows) do local attr, parsed = Pango.parse_markup(label..":", -1, 0) - playout.attributes, playout.text = attr, parsed + playout2.attributes, playout2.text = attr, parsed cr:move_to(dx2, dy) - cr:show_layout(playout) + cr:show_layout(playout2) attr, parsed = Pango.parse_markup(values[k], -1, 0) - playout.attributes, playout.text = attr, parsed - local _, logical = playout:get_pixel_extents() + playout2.attributes, playout2.text = attr, parsed + local _, logical = playout2:get_pixel_extents() cr:move_to( dx2+col1_width+5, dy) - cr:show_layout(playout) + cr:show_layout(playout2) dy = dy + 5 + logical.height end @@ -344,11 +680,21 @@ for _=1, screen.count() do compute_ruler(s, s.geometry, "geometry") end +-- If there is some rulers on the left/top, add a global translation. +evaluate_translation( + args.draw_gaps, + args.draw_struts, + args.draw_mwfact, + args.draw_client_snap +) + -- Get the final size of the image. local sew, seh = screen._get_extents() sew, seh = sew/args.factor + (screen.count()-1)*10+2, seh/args.factor+2 -sew,seh=sew+100,seh+100 +sew, seh = sew + tr_x, seh + 0.66*tr_y + +sew, seh = sew + 5*get_text_height(), seh + 5*get_text_height() img = cairo.SvgSurface.create(image_path..".svg", sew, seh) cr = cairo.Context(img) @@ -356,10 +702,14 @@ cr = cairo.Context(img) cr:set_line_width(1.5) cr:set_dash({10,4},1) +-- Instead of adding origin offset everywhere, translate the viewport. +translate() + -- Draw the various areas. for k=1, screen.count() do local s = screen[1] + -- The outer geometry. draw_area(s, s.geometry, "geometry", (k-1)*10, args.highlight_geometry) @@ -381,17 +731,68 @@ for k=1, screen.count() do draw_struct(s, args.draw_wibar, 'wibar', (k-1)*10, 'Wibar') end + local skip_gaps = s.selected_tag + and s.selected_tag.gap_single_client == false + and #s.tiled_clients == 1 + + local sd = require("awful.mouse.snap").default_distance + -- Draw clients. if args.draw_clients then for label,c in pairs(args.draw_clients) do - draw_struct(s, c, 'tiling_client', (k-1)*10, label) + local gap = c:tags()[1].gap + if args.draw_gaps and gap > 0 and (not c.floating) and not skip_gaps then + local proxy = { + x = c.x - gap, + y = c.y - gap, + width = c.width + 2*gap, + height = c.height + 2*gap, + } + + draw_client(s, proxy, 'gaps', (k-1)*10, nil, "11") + elseif args.draw_client_snap and c.floating then + local proxy = { + x = c.x - sd, + y = c.y - sd, + width = c.width + 2*sd, + height = c.height + 2*sd, + } + + draw_client(s, proxy, 'gaps', (k-1)*10, nil, "11") + end + + draw_client(s, c, 'tiling_client', (k-1)*10, label) end end + if args.draw_struts and has_struts(s) then + draw_struts(s) + end + -- Draw the informations. if args.display_screen_info ~= false then draw_info(s) end + + -- Draw the useless gaps. + if args.draw_gaps and not skip_gaps then + draw_gaps(s) + end + + -- Draw the useless gaps. + if args.draw_mwfact then + draw_mwfact(s) + end + + -- Draw the snapping areas of floating clients. + if args.draw_client_snap then + draw_client_snap(s) + end + + -- Draw the screen edge areas. + --if args.draw_screen_snap then + -- draw_screen_snap(s) + --end end img:finish()