2016-02-21 08:31:31 +01:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
--- A rather hacky way to create free-floating widgets.
|
|
|
|
--
|
|
|
|
-- Hopefully this will be more maintainable then the old Radical hard-coded
|
|
|
|
-- positioning code.
|
|
|
|
--
|
|
|
|
-- @author Emmanuel Lepage Vallee
|
|
|
|
-- @copyright 2016 Emmanuel Lepage Vallee
|
|
|
|
-- @release @AWESOME_VERSION@
|
|
|
|
-- @module radical.smart_wibox
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
local capi = {mouse = mouse, screen = screen}
|
|
|
|
local wibox = require( "wibox" )
|
|
|
|
local util = require( "awful.util" )
|
|
|
|
local surface = require( "gears.surface" )
|
|
|
|
local glib = require( "lgi" ).GLib
|
|
|
|
local beautiful = require( "beautiful" )
|
|
|
|
local color = require( "gears.color" )
|
|
|
|
local screen = require( "awful.screen" )
|
|
|
|
local mouse = require( "awful.mouse" )
|
|
|
|
local placement = require( "radical.placement")
|
|
|
|
local unpack = unpack or table.unpack
|
|
|
|
|
|
|
|
local module = {}
|
|
|
|
|
|
|
|
local main_widget = {}
|
|
|
|
|
2016-03-15 08:55:20 +01:00
|
|
|
--TODO position = relative to parent
|
|
|
|
--TODO direction = up or down (the alternate stuff)
|
|
|
|
|
2016-02-21 08:31:31 +01:00
|
|
|
-- Get the optimal direction for the wibox
|
|
|
|
-- This (try to) avoid going offscreen
|
|
|
|
local function set_position(self)
|
|
|
|
local points = rawget(self, "possible_positions") or {}
|
|
|
|
local preferred_positions = rawget(self, "_preferred_directions") or {}
|
|
|
|
|
|
|
|
local pos_name = placement.move_relative(self, points, preferred_positions)
|
|
|
|
|
|
|
|
if pos_name ~= rawget(self, "position") then
|
2016-03-15 08:55:20 +01:00
|
|
|
self:emit_signal("property::direction", pos_name)
|
2016-02-21 08:31:31 +01:00
|
|
|
rawset(self, "position", pos_name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Fit this widget into the given area
|
|
|
|
function main_widget:fit(context, width, height)
|
|
|
|
if not self.widget then
|
|
|
|
return 0, 0
|
|
|
|
end
|
|
|
|
|
|
|
|
return wibox.widget.base.fit_widget(self, context, self.widget, width, height)
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Layout this widget
|
|
|
|
function main_widget:layout(context, width, height)
|
|
|
|
if self.widget then
|
|
|
|
local w, h = wibox.widget.base.fit_widget(self, context, self.widget, 9999, 9999)
|
|
|
|
glib.idle_add(glib.PRIORITY_HIGH_IDLE, function()
|
|
|
|
self._wb.width = math.ceil(w or 1)
|
|
|
|
self._wb.height = math.ceil(h or 1)
|
|
|
|
set_position(self._wb)
|
|
|
|
end)
|
|
|
|
return { wibox.widget.base.place_widget_at(self.widget, 0, 0, width, height) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Set the widget that is drawn on top of the background
|
|
|
|
function main_widget:set_widget(widget)
|
|
|
|
if widget then
|
|
|
|
wibox.widget.base.check_widget(widget)
|
|
|
|
end
|
|
|
|
self.widget = widget
|
|
|
|
self:emit_signal("widget::layout_changed")
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Get the number of children element
|
|
|
|
-- @treturn table The children
|
|
|
|
function main_widget:get_children()
|
|
|
|
return {self.widget}
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Replace the layout children
|
|
|
|
-- This layout only accept one children, all others will be ignored
|
|
|
|
-- @tparam table children A table composed of valid widgets
|
|
|
|
function main_widget:set_children(children)
|
|
|
|
self.widget = children and children[1]
|
|
|
|
self:emit_signal("widget::layout_changed")
|
|
|
|
end
|
|
|
|
|
|
|
|
function main_widget:before_draw_children(context, cr, width, height)
|
|
|
|
-- Update the wibox shape bounding. This module use custom painter instead
|
|
|
|
-- of a shape clip to get antialiasing.
|
|
|
|
if self._wb._shape and (width ~= self.prev_width or height ~= self.prev_height) then
|
|
|
|
surface.apply_shape_bounding(self._wb, self._wb._shape, unpack(self._wb._shape_args))
|
|
|
|
self.prev_width = width
|
|
|
|
self.prev_height = height
|
|
|
|
end
|
|
|
|
|
|
|
|
-- There is nothing else to do. The wibox background painter will do
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Draw the border after the content to emulate the shape_clip
|
|
|
|
function main_widget:after_draw_children(context, cr, width, height)
|
|
|
|
local border_width = self._wb._shape_border_width
|
|
|
|
|
|
|
|
if not border_width then return end
|
|
|
|
|
|
|
|
cr:translate(border_width/2, border_width/2)
|
|
|
|
cr:set_line_width(border_width)
|
|
|
|
|
|
|
|
cr:set_source(self._wb._shape_border_color)
|
|
|
|
self._wb._shape(cr, width-border_width, height-border_width, unpack(self._wb._shape_args or {}))
|
|
|
|
cr:stroke()
|
|
|
|
end
|
|
|
|
|
|
|
|
local wb_func = {}
|
|
|
|
|
|
|
|
--- Set the wibox shape.
|
|
|
|
-- All other paramaters will be passed to the `s` function
|
|
|
|
-- @param s A `gears.shape` compatible function
|
|
|
|
function wb_func:set_shape(s, ...)
|
|
|
|
|
|
|
|
rawset(self, "_shape" , s )
|
|
|
|
rawset(self, "_shape_args" , {...} )
|
|
|
|
|
|
|
|
self.widget:emit_signal("widget::layout_changed")
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Set the wibox shape border color.
|
|
|
|
-- Note that this is independant from the wibox border_color.
|
|
|
|
-- The default are `beautiful.menu_border_color` or `beautiful.border_color`.
|
|
|
|
-- The there is no border, then this function will do nothing.
|
|
|
|
-- @param The border color or nil
|
|
|
|
function wb_func:set_shape_border_color(col)
|
|
|
|
rawset(self,"_shape_border_color", col and color(col) or color(beautiful.menu_border_color or beautiful.border_color))
|
|
|
|
self.widget:emit_signal("widget::layout_changed")
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Set the shape border (clip) width.
|
|
|
|
-- The shape will be used to draw the border. Any content within the border
|
|
|
|
-- will be hidden.
|
|
|
|
-- @tparam number width The border width
|
|
|
|
function wb_func:set_shape_border_width(width)
|
|
|
|
rawset(self,"_shape_border_width", width)
|
|
|
|
self.widget:emit_signal("widget::layout_changed")
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Set the preferred wibox directions relative to its parent.
|
|
|
|
-- Valid directions are:
|
|
|
|
-- * left
|
|
|
|
-- * right
|
|
|
|
-- * top
|
|
|
|
-- * bottom
|
|
|
|
-- @tparam string ... One of more directions (in the preferred order)
|
|
|
|
function wb_func:set_preferred_positions(...)
|
|
|
|
local dirs = {}
|
|
|
|
for k, v in ipairs{...} do
|
|
|
|
dirs[v] = k
|
|
|
|
end
|
|
|
|
rawset(self, "_preferred_directions", dirs)
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Move the wibox to a position relative to `geo`.
|
|
|
|
-- This will try to avoid overlapping the source wibox and auto-detect the right
|
|
|
|
-- direction to avoid going off-screen.
|
|
|
|
-- @param[opt=mouse.coords()] geo A geometry table. It is given as parameter
|
|
|
|
-- from buttons callbacks and signals such as `mouse::enter`.
|
2016-03-14 22:52:28 +01:00
|
|
|
-- @param mode Use the mouse position instead of the widget center as
|
2016-02-21 08:31:31 +01:00
|
|
|
-- reference point.
|
2016-03-14 22:52:28 +01:00
|
|
|
function wb_func:move_by_parent(geo, mode)
|
2016-03-02 10:19:44 +01:00
|
|
|
if rawget(self, "is_relative") == false then return end
|
|
|
|
|
2016-03-15 08:55:20 +01:00
|
|
|
--TODO add border_width?
|
|
|
|
local dps = placement.get_relative_points(geo, mode, {
|
|
|
|
xoffset = rawget(self, "xoffset") or 0,
|
|
|
|
yoffset = rawget(self, "yoffset") or 0,
|
|
|
|
})
|
2016-02-21 08:31:31 +01:00
|
|
|
|
|
|
|
rawset(self, "possible_positions", dps)
|
|
|
|
|
|
|
|
set_position(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
function wb_func:move_by_mouse()
|
|
|
|
--TODO
|
|
|
|
end
|
|
|
|
|
2016-03-15 08:55:20 +01:00
|
|
|
function wb_func:set_xoffset(offset)
|
|
|
|
local old = rawget(self, "xoffset") or 0
|
|
|
|
if old == offset then return end
|
|
|
|
|
|
|
|
rawset(self, "xoffset", offset)
|
|
|
|
|
|
|
|
-- Update the points
|
|
|
|
for k,v in pairs(rawget(self, "possible_positions") or {}) do
|
|
|
|
v.x = v.x - old + offset
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Update the position
|
|
|
|
set_position(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
function wb_func:set_yoffset(offset)
|
|
|
|
local old = rawget(self, "yoffset") or 0
|
|
|
|
if old == offset then return end
|
|
|
|
|
|
|
|
rawset(self, "yoffset", offset)
|
|
|
|
|
|
|
|
-- Update the points
|
|
|
|
for k,v in pairs(rawget(self, "possible_positions") or {}) do
|
|
|
|
v.y = v.y - old + offset
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Update the position
|
|
|
|
set_position(self)
|
2016-02-21 08:31:31 +01:00
|
|
|
end
|
|
|
|
|
2016-03-15 08:55:20 +01:00
|
|
|
function wb_func:set_margin(margin)
|
|
|
|
rawset(self, "left_margin" , margin)
|
|
|
|
rawset(self, "right_margin" , margin)
|
|
|
|
rawset(self, "top_margin" , margin)
|
|
|
|
rawset(self, "bottom_margin", margin)
|
2016-02-21 08:31:31 +01:00
|
|
|
end
|
|
|
|
|
2016-03-02 10:19:44 +01:00
|
|
|
--- Set if the wibox take into account the other wiboxes.
|
|
|
|
-- @tparam boolean val Take the other wiboxes position into account
|
|
|
|
function wb_func:set_relative(val)
|
|
|
|
rawset(self, "is_relative", val)
|
|
|
|
end
|
|
|
|
|
2016-02-21 08:31:31 +01:00
|
|
|
--- A brilliant idea to totally turn the whole hierarchy on its head
|
|
|
|
-- and create a widget that own a wibox...
|
|
|
|
local function create_auto_resize_widget(self, wdg, args)
|
|
|
|
assert(wdg)
|
|
|
|
local ii = wibox.widget.base.make_widget()
|
|
|
|
util.table.crush(ii, main_widget)
|
|
|
|
|
|
|
|
ii:set_widget(wdg)
|
|
|
|
|
|
|
|
-- Create a wibox to host the widget
|
|
|
|
local w = wibox(args or {})
|
|
|
|
|
|
|
|
-- Wibox use metatable inheritance, rawset is necessary
|
|
|
|
for k, v in pairs(wb_func) do
|
|
|
|
rawset(w, k, v)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Cross-link the wibox and widget
|
|
|
|
ii._wb = w
|
|
|
|
w:set_widget(ii)
|
|
|
|
rawset(w, "widget", wdg)
|
|
|
|
|
|
|
|
-- Changing the widget is not supported
|
|
|
|
rawset(w, "set_widget", function()end)
|
|
|
|
|
|
|
|
w:set_shape_border_color()
|
|
|
|
|
2016-03-15 08:55:20 +01:00
|
|
|
w:add_signal("property::direction")
|
2016-02-21 08:31:31 +01:00
|
|
|
|
|
|
|
if args and args.preferred_positions then
|
|
|
|
if type(args.preferred_positions) == "table" then
|
|
|
|
w:set_preferred_positions(unpack(args.preferred_positions))
|
|
|
|
else
|
|
|
|
w:set_preferred_positions(args.preferred_positions)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if args.shape then
|
|
|
|
w:set_shape(args.shape, unpack(args.shape_args or {}))
|
|
|
|
end
|
|
|
|
|
2016-03-02 10:19:44 +01:00
|
|
|
if args.relative ~= nil then
|
|
|
|
w:set_relative(args.relative)
|
|
|
|
end
|
|
|
|
|
2016-02-21 08:31:31 +01:00
|
|
|
for k,v in ipairs{"shape_border_color", "shape_border_width"} do
|
|
|
|
if args[v] then
|
|
|
|
w["set_"..v](w, args[v])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return w
|
|
|
|
end
|
|
|
|
|
2016-03-02 09:49:18 +01:00
|
|
|
return setmetatable(module, {__call = create_auto_resize_widget})
|