diff --git a/widget/tag_preview.lua b/widget/tag_preview.lua index 2a181b4..a571869 100644 --- a/widget/tag_preview.lua +++ b/widget/tag_preview.lua @@ -1,87 +1,127 @@ --- --- Provides: --- bling::tag_preview::update -- first line is the signal --- t (tag) -- indented lines are function parameters --- bling::tag_preview::visibility --- s (screen) --- v (boolean) --- +local cairo = require("lgi").cairo local awful = require("awful") local wibox = require("wibox") local helpers = require(tostring(...):match(".*bling") .. ".helpers") -local gears = require("gears") +local gobject = require("gears.object") +local gtable = require("gears.table") +local gtimer = require("gears.timer") +local gmatrix = require("gears.matrix") +local gsurface = require("gears.surface") local beautiful = require("beautiful") local dpi = beautiful.xresources.apply_dpi -local cairo = require("lgi").cairo +local collectgarbage = collectgarbage +local ipairs = ipairs +local pcall = pcall +local capi = {client = client, tag = tag} + +local tag_preview = {mt = {}} + +local function _get_widget_geometry(_hierarchy, widget) + local width, height = _hierarchy:get_size() + if _hierarchy:get_widget() == widget then + -- Get the extents of this widget in the device space + local x, y, w, h = gmatrix.transform_rectangle( + _hierarchy:get_matrix_to_device(), 0, 0, width, + height) + return {x = x, y = y, width = w, height = h, hierarchy = _hierarchy} + end + + for _, child in ipairs(_hierarchy:get_children()) do + local ret = _get_widget_geometry(child, widget) + if ret then return ret end + end +end + +local function get_widget_geometry(wibox, widget) + return _get_widget_geometry(wibox._drawable._widget_hierarchy, widget) +end + +function tag_preview:update(t) + local args = self + + if not args.coords and args.wibox and args.widget then + args.coords = get_widget_geometry(args.wibox, args.widget) + if args.offset.x ~= nil then + args.coords.x = args.coords.x + args.offset.x + end + if args.offset.y ~= nil then + args.coords.y = args.coords.y + args.offset.y + end + + self._private.widget.x = args.coords.x + self._private.widget.y = args.coords.y + end + + local geo = t.screen:get_bounding_geometry({ + honor_padding = args.padding, + honor_workarea = args.work_area + }) + + self._private.widget.maximum_width = + args.scale * geo.width + args.margin * 2 + self._private.widget.maximum_height = + args.scale * geo.height + args.margin * 2 -local function draw_widget( - t, - tag_preview_image, - scale, - screen_radius, - client_radius, - client_opacity, - client_bg, - client_border_color, - client_border_width, - widget_bg, - widget_border_color, - widget_border_width, - geo, - margin, - background_image -) local client_list = wibox.layout.manual() client_list.forced_height = geo.height - client_list.forced_width = geo.width - local tag_screen = t.screen - for i, c in ipairs(t:clients()) do + client_list.forced_width = geo.widget + + for _, c in ipairs(t:clients()) do if not c.hidden and not c.minimized then - - - local img_box = wibox.widget ({ + local img_box = wibox.widget { resize = true, - forced_height = 100 * scale, - forced_width = 100 * scale, - widget = wibox.widget.imagebox, - }) + forced_height = 100 * args.scale, + forced_width = 100 * args.scale, + widget = wibox.widget.imagebox + } - -- If fails to set image, fallback to a awesome icon - if not pcall(function() img_box.image = gears.surface.load(c.icon) end) then - img_box.image = beautiful.theme_assets.awesome_icon (24, "#222222", "#fafafa") - end + -- If fails to set image, fallback to a awesome icon - if tag_preview_image then + if args.client_icon then + if not pcall(function() + img_box.image = gsurface.load(c.icon) + end) then + img_box.image = beautiful.theme_assets.awesome_icon(24, + "#222222", + "#fafafa") + end + end + + if args.tag_preview_image then if c.prev_content or t.selected then - local content + local content = nil if t.selected then - content = gears.surface(c.content) + content = gsurface(c.content) else - content = gears.surface(c.prev_content) + content = gsurface(c.prev_content) end local cr = cairo.Context(content) local x, y, w, h = cr:clip_extents() - local img = cairo.ImageSurface.create( - cairo.Format.ARGB32, - w - x, - h - y - ) + local img = cairo.ImageSurface.create(cairo.Format.ARGB32, + w - x, h - y) cr = cairo.Context(img) cr:set_source_surface(content, 0, 0) cr.operator = cairo.Operator.SOURCE cr:paint() img_box = wibox.widget({ - image = gears.surface.load(img), + image = gsurface.load(img), resize = true, - opacity = client_opacity, - forced_height = math.floor(c.height * scale), - forced_width = math.floor(c.width * scale), - widget = wibox.widget.imagebox, + opacity = args.client_opacity, + forced_height = c.height * args.scale, + forced_width = c.width * args.scale, + widget = wibox.widget.imagebox }) + end end + local c_bg = args.client_bg + + if c == capi.client.focus then + c_bg = beautiful.xcolor4 + end + local client_box = wibox.widget({ { nil, @@ -90,157 +130,143 @@ local function draw_widget( img_box, nil, expand = "outside", - layout = wibox.layout.align.horizontal, + layout = wibox.layout.align.horizontal }, nil, expand = "outside", - widget = wibox.layout.align.vertical, + widget = wibox.layout.align.vertical }, - forced_height = math.floor(c.height * scale), - forced_width = math.floor(c.width * scale), - bg = client_bg, - shape_border_color = client_border_color, - shape_border_width = client_border_width, - shape = helpers.shape.rrect(client_radius), - widget = wibox.container.background, + forced_height = math.floor(c.height * args.scale), + forced_width = math.floor(c.width * args.scale), + bg = c_bg, + shape_border_color = args.client_border_color, + shape_border_width = args.client_border_width, + shape = helpers.shape.rrect(args.client_border_radius), + widget = wibox.container.background }) client_box.point = { - x = math.floor((c.x - geo.x) * scale), - y = math.floor((c.y - geo.y) * scale), + x = math.floor((c.x - geo.x) * args.scale), + y = math.floor((c.y - geo.y) * args.scale) } client_list:add(client_box) + end end - return wibox.widget { + local w = wibox.widget { { - background_image, + args.background_image, { { - { - { - client_list, - forced_height = geo.height, - forced_width = geo.width, - widget = wibox.container.place, - }, - layout = wibox.layout.align.horizontal, - }, - layout = wibox.layout.align.vertical, + client_list, + forced_height = geo.height, + forced_width = geo.width, + valign = "center", + halign = "center", + widget = wibox.container.place }, - margins = margin, - widget = wibox.container.margin, + margins = args.tag_margin, + widget = wibox.container.margin }, layout = wibox.layout.stack }, - bg = widget_bg, - shape_border_width = widget_border_width, - shape_border_color = widget_border_color, - shape = helpers.shape.rrect(screen_radius), - widget = wibox.container.background, + bg = args.tag_bg, + shape_border_color = args.tag_border_color, + shape_border_width = args.tag_border_width, + shape = helpers.shape.rrect(args.tag_border_radius), + widget = wibox.container.background } + + self._private.widget.widget = w end -local enable = function(opts) - local opts = opts or {} +local function new(args) + args = args or {} - local tag_preview_image = opts.show_client_content or false - local widget_x = opts.x or dpi(20) - local widget_y = opts.y or dpi(20) - local scale = opts.scale or 0.2 - local work_area = opts.honor_workarea or false - local padding = opts.honor_padding or false - local placement_fn = opts.placement_fn or nil - local background_image = opts.background_widget or nil + args.type = args.type or "dropdown_menu" + args.coords = args.coords or nil + args.placement = args.placement or nil + args.wibox = args.wibox + args.widget = args.widget + args.offset = args.offset or {} + args.padding = args.padding + args.work_area = args.work_area + args.scale = args.scale or 0.2 + args.margin = args.margin or dpi(0) + args.client_icon = args.client_icon + args.client_opacity = args.client_opacity or 0 + args.client_bg = args.client_bg or "#000000" + args.client_border_color = args.client_border_color or "#ffffff" + args.client_border_width = args.client_border_width or dpi(1) + args.client_border_radius = args.client_border_radius or dpi(0) + args.tag_margin = args.tag_margin or dpi(0) + args.tag_bg = args.tag_bg or "#000000" + args.tag_border_color = args.tag_border_color or "#ffffff" + args.tag_border_width = args.tag_border_width or dpi(0) + args.tag_border_radius = args.tag_border_radius or dpi(0) + args.background_image = args.background_image or nil + args.tag_preview_image = args.tag_preview_image - local margin = beautiful.tag_preview_widget_margin or dpi(0) - local screen_radius = beautiful.tag_preview_widget_border_radius or dpi(0) - local client_radius = beautiful.tag_preview_client_border_radius or dpi(0) - local client_opacity = beautiful.tag_preview_client_opacity or 0.5 - local client_bg = beautiful.tag_preview_client_bg or "#000000" - local client_border_color = beautiful.tag_preview_client_border_color - or "#ffffff" - local client_border_width = beautiful.tag_preview_client_border_width - or dpi(3) - local widget_bg = beautiful.tag_preview_widget_bg or "#000000" - local widget_border_color = beautiful.tag_preview_widget_border_color - or "#ffffff" - local widget_border_width = beautiful.tag_preview_widget_border_width - or dpi(3) + local ret = gobject {} + ret._private = {} - local tag_preview_box = awful.popup({ - type = "dropdown_menu", + gtable.crush(ret, tag_preview) + gtable.crush(ret, args) + + ret._private.widget = awful.popup({ + type = ret.type, visible = false, ontop = true, - placement = placement_fn, - widget = wibox.container.background, - input_passthrough = true, + placement = ret.placement, + input_passthrough = ret.input_passthrough, bg = "#00000000", + widget = wibox.container.background -- A dummy widget to make awful.popup not scream }) - tag.connect_signal("property::selected", function(t) + capi.tag.connect_signal("property::selected", function(t) -- Awesome switches up tags on startup really fast it seems, probably depends on what rules you have set -- which can cause the c.content to not show the correct image - gears.timer - { + gtimer { timeout = 0.1, - call_now = false, + call_now = false, autostart = true, single_shot = true, callback = function() if t.selected == true then for _, c in ipairs(t:clients()) do - c.prev_content = gears.surface.duplicate_surface(c.content) + c.prev_content = gsurface.duplicate_surface(c.content) end end end } end) - awesome.connect_signal("bling::tag_preview::update", function(t) - local geo = t.screen:get_bounding_geometry({ - honor_padding = padding, - honor_workarea = work_area, - }) - - tag_preview_box.maximum_width = scale * geo.width + margin * 2 - tag_preview_box.maximum_height = scale * geo.height + margin * 2 - - - tag_preview_box.widget = draw_widget( - t, - tag_preview_image, - scale, - screen_radius, - client_radius, - client_opacity, - client_bg, - client_border_color, - client_border_width, - widget_bg, - widget_border_color, - widget_border_width, - geo, - margin, - background_image - ) - end) - - awesome.connect_signal("bling::tag_preview::visibility", function(s, v) - if not placement_fn then - tag_preview_box.x = s.geometry.x + widget_x - tag_preview_box.y = s.geometry.y + widget_y - end - - if v == false then - tag_preview_box.widget = nil - collectgarbage("collect") - end - - tag_preview_box.visible = v - end) + return ret end -return {enable = enable, draw_widget = draw_widget} +function tag_preview:get_widget() return self._private.widget.widget end + +function tag_preview:show(t) + self:update(t) + self._private.widget.visible = true +end + +function tag_preview:hide() + self._private.widget.visible = false + self._private.widget.widget = nil + collectgarbage("collect") +end + +function tag_preview:toggle(t) + if self._private.widget.visible == true then + self:hide() + else + self:show(t) + end +end + +function tag_preview.mt:__call(...) return new(...) end + +return setmetatable(tag_preview, tag_preview.mt) diff --git a/widget/task_preview.lua b/widget/task_preview.lua index 84c1e6f..566c988 100644 --- a/widget/task_preview.lua +++ b/widget/task_preview.lua @@ -58,25 +58,10 @@ function task_preview:show(c, args) self._private.widget.y = args.coords.y end - if not pcall(function() return type(c.content) end) then return end - - local content = nil - if c.active then - content = gsurface(c.content) - elseif c.prev_content then - content = gsurface(c.prev_content) - end - - local img = nil - if content ~= nil then - local cr = cairo.Context(content) - local x, y, w, h = cr:clip_extents() - img = cairo.ImageSurface.create(cairo.Format.ARGB32, w - x, h - y) - cr = cairo.Context(img) - cr:set_source_surface(content, 0, 0) - cr.operator = cairo.Operator.SOURCE - cr:paint() - end + local shoot = awful.screenshot {client = c} + shoot:refresh() + local ib = shoot.content_widget + ib.resize = true local widget = wibox.widget { (self.widget_template or { @@ -104,12 +89,7 @@ function task_preview:show(c, args) }, { { - { - id = "image_role", - resize = true, - clip_shape = self.image_shape, - widget = wibox.widget.imagebox - }, + id = "image_container_role", valign = "center", halign = "center", widget = wibox.container.place @@ -136,8 +116,8 @@ function task_preview:show(c, args) -- TODO: have something like a create callback here? - for _, w in ipairs(widget:get_children_by_id("image_role")) do - w.image = img -- TODO: copy it with gsurface.xxx or something + for _, w in ipairs(widget:get_children_by_id("image_container_role")) do + w.widget = ib -- TODO: copy it with gsurface.xxx or something end for _, w in ipairs(widget:get_children_by_id("name_role")) do