diff --git a/widget/task_preview.lua b/widget/task_preview.lua index 3b54867..0531181 100644 --- a/widget/task_preview.lua +++ b/widget/task_preview.lua @@ -1,30 +1,63 @@ --- --- Provides: --- bling::task_preview::visibility --- s (screen) --- v (boolean) --- c (client) --- -local awful = require("awful") -local wibox = require("wibox") -local helpers = require(tostring(...):match(".*bling") .. ".helpers") -local gears = require("gears") -local beautiful = require("beautiful") -local dpi = beautiful.xresources.apply_dpi local cairo = require("lgi").cairo +local awful = require("awful") +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 wibox = require("wibox") +local beautiful = require("beautiful") +local helpers = require(tostring(...):match(".*bling") .. ".helpers") +local dpi = beautiful.xresources.apply_dpi +local collectgarbage = collectgarbage +local ipairs = ipairs +local pcall = pcall +local type = type +local capi = { tag = tag } + +local task_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 task_preview:show(c, args) + args = args or {} + + args.coords = args.coords or self.coords + args.wibox = args.wibox + args.widget = args.widget + args.offset = args.offset or {} + + 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 --- TODO: rename structure to something better? -local function draw_widget( - c, - widget_template, - screen_radius, - widget_bg, - widget_border_color, - widget_border_width, - margin, - widget_width, - widget_height -) if not pcall(function() return type(c.content) end) then @@ -33,9 +66,9 @@ local function draw_widget( local content = nil if c.active then - content = gears.surface(c.content) + content = gsurface(c.content) elseif c.prev_content then - content = gears.surface(c.prev_content) + content = gsurface(c.prev_content) end local img = nil @@ -49,8 +82,8 @@ local function draw_widget( cr:paint() end - local widget = wibox.widget({ - (widget_template or { + local widget = wibox.widget{ + (self.widget_template or { { { { @@ -78,37 +111,37 @@ local function draw_widget( { id = "image_role", resize = true, - clip_shape = helpers.shape.rrect(screen_radius), + clip_shape = self.image_shape, widget = wibox.widget.imagebox, }, valign = "center", halign = "center", widget = wibox.container.place, }, - top = margin * 0.25, + top = self.margin * 0.25, widget = wibox.container.margin, }, fill_space = true, layout = wibox.layout.fixed.vertical, }, - margins = margin, + margins = self.margin, widget = wibox.container.margin, }, - bg = widget_bg, - shape_border_width = widget_border_width, - shape_border_color = widget_border_color, - shape = helpers.shape.rrect(screen_radius), + bg = self.bg, + shape_border_width = self.border_width, + shape_border_color = self.border_color, + shape = self.shape, widget = wibox.container.background, }), - width = widget_width, - height = widget_height, + width = self.forced_width, + height = self.forced_height, widget = wibox.container.constraint, - }) + } -- 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 gears.surface.xxx or something + w.image = img -- TODO: copy it with gsurface.xxx or something end for _, w in ipairs(widget:get_children_by_id("name_role")) do @@ -119,41 +152,62 @@ local function draw_widget( w.image = c.icon -- TODO: detect clienticon end - return widget + self._private.widget.widget = widget + self._private.widget.visible = true end -local enable = function(opts) - local opts = opts or {} +function task_preview:hide() + self._private.widget.visible = false + self._private.widget.widget = nil + collectgarbage("collect") +end - local widget_x = opts.x or dpi(20) - local widget_y = opts.y or dpi(20) - local widget_height = opts.height or dpi(200) - local widget_width = opts.width or dpi(200) - local placement_fn = opts.placement_fn or nil +function task_preview:toggle(c, args) + if self._private.widget.visible == true then + self:hide() + else + self:show(c, args) + end +end - local margin = beautiful.task_preview_widget_margin or dpi(0) - local screen_radius = beautiful.task_preview_widget_border_radius or dpi(0) - local widget_bg = beautiful.task_preview_widget_bg or "#000000" - local widget_border_color = beautiful.task_preview_widget_border_color - or "#ffffff" - local widget_border_width = beautiful.task_preview_widget_border_width - or dpi(3) +local function new(args) + args = args or {} - local task_preview_box = awful.popup({ - type = "dropdown_menu", + args.type = args.type or "dropdown_menu" + args.coords = args.coords or nil + args.placement = args.placement or nil + args.forced_width = args.forced_width or dpi(200) + args.forced_height = args.forced_height or dpi(200) + args.input_passthrough = args.input_passthrough or false + + args.margin = args.margin or beautiful.task_preview_widget_margin or dpi(0) + args.shape = args.shape or beautiful.task_preview_widget_shape or nil + args.bg = args.bg or beautiful.task_preview_widget_bg or "#000000" + args.border_width = args.border_width or beautiful.task_preview_widget_border_width or nil + args.border_color = args.border_color or beautiful.task_preview_widget_border_color or "#ffffff" + args.image_shape = args.image_shape or beautiful.task_preview_image_shape or nil + + local ret = gobject{} + ret._private = {} + + gtable.crush(ret, task_preview) + gtable.crush(ret, args) + + ret._private.widget = awful.popup + { + type = ret.type, visible = false, ontop = true, - placement = placement_fn, - widget = wibox.container.background, -- A dummy widget to make awful.popup not scream - 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, autostart = true, @@ -161,39 +215,45 @@ local enable = function(opts) 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::task_preview::visibility", function(s, v, c) - if v then - -- Update task preview contents - task_preview_box.widget = draw_widget( - c, - opts.structure, - screen_radius, - widget_bg, - widget_border_color, - widget_border_width, - margin, - widget_width, - widget_height - ) - else - task_preview_box.widget = nil - collectgarbage("collect") - end + return ret - if not placement_fn then - task_preview_box.x = s.geometry.x + widget_x - task_preview_box.y = s.geometry.y + widget_y - end + -- awesome.connect_signal("bling::task_preview::visibility", function(s, v, c) + -- if v then + -- -- Update task preview contents + -- task_preview_box.widget = draw_widget( + -- c, + -- opts.structure, + -- screen_radius, + -- widget_bg, + -- widget_border_color, + -- widget_border_width, + -- margin, + -- widget_width, + -- widget_height + -- ) + -- else + -- task_preview_box.widget = nil + -- collectgarbage("collect") + -- end - task_preview_box.visible = v - end) + -- if not placement_fn then + -- task_preview_box.x = s.geometry.x + widget_x + -- task_preview_box.y = s.geometry.y + widget_y + -- end + + -- task_preview_box.visible = v + -- end) end -return { enable = enable, draw_widget = draw_widget } +function task_preview.mt:__call(...) + return new(...) +end + +return setmetatable(task_preview, task_preview.mt) \ No newline at end of file