From 1cbd839084f5cc643d0c3777b175bf64d7c2cd94 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 9 Aug 2020 23:33:29 -0700 Subject: [PATCH] tests: "Complete" the screen template. This isn't very nice code, far from it. But it renders fine. So, as mentionned like what, 1.5 years ago, the original code dump was half of the scope. This is the second half. It it has various sizes for various core objects. All of them are hardcoded and some off by a few pixels, but overall it works. --- tests/examples/screen/template.lua | 535 +++++++++++++++++++++++++---- 1 file changed, 468 insertions(+), 67 deletions(-) diff --git a/tests/examples/screen/template.lua b/tests/examples/screen/template.lua index b0f53f0d..7a68725f 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()