Import lib/wibox/, a new widget system in lua
Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
a924a92e07
commit
2eae7e5cf4
|
@ -0,0 +1,254 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
require("wibox.layout")
|
||||
require("wibox.widget")
|
||||
|
||||
local capi = {
|
||||
drawin = drawin,
|
||||
oocairo = oocairo,
|
||||
timer = timer
|
||||
}
|
||||
local setmetatable = setmetatable
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
local table = table
|
||||
local color = require("gears.color")
|
||||
local object = require("gears.object")
|
||||
local sort = require("gears.sort")
|
||||
local beautiful = require("beautiful")
|
||||
|
||||
module("wibox")
|
||||
|
||||
local function do_redraw(wibox)
|
||||
if not wibox.drawin.screen or not wibox.drawin.visible then
|
||||
return
|
||||
end
|
||||
|
||||
local geom = wibox.drawin:geometry()
|
||||
local cr = capi.oocairo.context_create(wibox.drawin.surface)
|
||||
|
||||
-- Clear the drawin
|
||||
cr:save()
|
||||
cr:set_operator("source")
|
||||
cr:set_source(wibox.background_color)
|
||||
cr:paint()
|
||||
cr:restore()
|
||||
|
||||
-- Draw the widget
|
||||
wibox._widget_geometries = {}
|
||||
if wibox.widget and not wibox.widget.__fake_widget then
|
||||
cr:set_source(wibox.foreground_color)
|
||||
wibox.widget:draw(wibox, cr, geom.width, geom.height)
|
||||
wibox:widget_at(wibox.widget, 0, 0, geom.width, geom.height)
|
||||
end
|
||||
|
||||
wibox.drawin:refresh()
|
||||
end
|
||||
|
||||
--- Register a widget's position.
|
||||
-- This is internal, don't call it yourself! Only wibox.layout.base.draw_widget
|
||||
-- is allowed to call this.
|
||||
function widget_at(wibox, widget, x, y, width, height)
|
||||
local t = {
|
||||
widget = widget,
|
||||
x = x, y = y,
|
||||
width = width, height = height
|
||||
}
|
||||
table.insert(wibox._widget_geometries, t)
|
||||
end
|
||||
|
||||
--- Find a widget by a point.
|
||||
-- The wibox must have drawn itself at least once for this to work.
|
||||
-- @param wibox The wibox to look at
|
||||
-- @param x X coordinate of the point
|
||||
-- @param y Y coordinate of the point
|
||||
-- @return A sorted table with all widgets that contain the given point. The
|
||||
-- widgets are sorted by relevance.
|
||||
function find_widgets(wibox, x, y)
|
||||
local matches = {}
|
||||
-- Find all widgets that contain the point
|
||||
for k, v in pairs(wibox._widget_geometries) do
|
||||
local match = true
|
||||
if v.x > x or v.x + v.width < x then match = false end
|
||||
if v.y > y or v.y + v.height < y then match = false end
|
||||
if match then
|
||||
table.insert(matches, v)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort the matches by area, the assumption here is that widgets don't
|
||||
-- overlap and so smaller widgets are "more specific".
|
||||
local function cmp(a, b)
|
||||
local area_a = a.width * a.height
|
||||
local area_b = b.width * b.height
|
||||
return area_a < area_b
|
||||
end
|
||||
sort(matches, cmp)
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
--- Set the widget that the wibox displays
|
||||
function set_widget(wibox, widget)
|
||||
if wibox.widget and not wibox.widget.__fake_widget then
|
||||
-- Disconnect from the old widget so that we aren't updated due to it
|
||||
wibox.widget:disconnect_signal("widget::updated", wibox.draw)
|
||||
end
|
||||
|
||||
if not widget then
|
||||
wibox.widget = { __fake_widget = true }
|
||||
else
|
||||
wibox.widget = widget
|
||||
widget:connect_signal("widget::updated", wibox.draw)
|
||||
end
|
||||
|
||||
-- Make sure the wibox is updated
|
||||
wibox.draw()
|
||||
end
|
||||
|
||||
--- Set the background of the wibox
|
||||
-- @param wibox The wibox to use
|
||||
-- @param c The background to use. This must either be a cairo pattern object,
|
||||
-- nil or a string that gears.color() understands.
|
||||
function set_bg(wibox, c)
|
||||
local c = c
|
||||
if type(c) == "string" then
|
||||
c = color(c)
|
||||
end
|
||||
wibox.background_color = c
|
||||
wibox.draw()
|
||||
end
|
||||
|
||||
--- Set the foreground of the wibox
|
||||
-- @param wibox The wibox to use
|
||||
-- @param c The foreground to use. This must either be a cairo pattern object,
|
||||
-- nil or a string that gears.color() understands.
|
||||
function set_fg(wibox, c)
|
||||
local c = c
|
||||
if type(c) == "string" then
|
||||
c = color(c)
|
||||
end
|
||||
wibox.foreground_color = c
|
||||
wibox.draw()
|
||||
end
|
||||
|
||||
--- Helper function to make wibox:buttons() work as expected
|
||||
function buttons(box, ...)
|
||||
return box.drawin:buttons(...)
|
||||
end
|
||||
|
||||
--- Helper function to make wibox:struts() work as expected
|
||||
function struts(box, ...)
|
||||
return box.drawin:struts(...)
|
||||
end
|
||||
|
||||
--- Helper function to make wibox:geometry() work as expected
|
||||
function geometry(box, ...)
|
||||
return box.drawin:geometry(...)
|
||||
end
|
||||
|
||||
local function setup_signals(wibox)
|
||||
local w = wibox.drawin
|
||||
|
||||
local function clone_signal(name)
|
||||
wibox:add_signal(name)
|
||||
-- When "name" is emitted on wibox.drawin, also emit it on wibox
|
||||
w:connect_signal(name, function(_, ...)
|
||||
wibox:emit_signal(name, wibox, ...)
|
||||
end)
|
||||
end
|
||||
clone_signal("mouse::enter")
|
||||
clone_signal("mouse::leave")
|
||||
clone_signal("property::border_color")
|
||||
clone_signal("property::border_width")
|
||||
clone_signal("property::buttons")
|
||||
clone_signal("property::cursor")
|
||||
clone_signal("property::height")
|
||||
clone_signal("property::ontop")
|
||||
clone_signal("property::opacity")
|
||||
clone_signal("property::screen")
|
||||
clone_signal("property::struts")
|
||||
clone_signal("property::visible")
|
||||
clone_signal("property::widgets")
|
||||
clone_signal("property::width")
|
||||
clone_signal("property::x")
|
||||
clone_signal("property::y")
|
||||
|
||||
-- Update the wibox when its geometry changes
|
||||
w:connect_signal("property::height", wibox.draw)
|
||||
w:connect_signal("property::width", wibox.draw)
|
||||
w:connect_signal("property::screen", wibox.draw)
|
||||
w:connect_signal("property::visible", wibox.draw)
|
||||
|
||||
local function button_signal(name)
|
||||
w:connect_signal(name, function(w, x, y, ...)
|
||||
local widgets = wibox:find_widgets(x, y)
|
||||
for k, v in pairs(widgets) do
|
||||
-- Calculate x/y inside of the widget
|
||||
local lx = x - v.x
|
||||
local ly = y - v.y
|
||||
v.widget:emit_signal(name, lx, ly, ...)
|
||||
end
|
||||
end)
|
||||
end
|
||||
button_signal("button::press")
|
||||
button_signal("button::release")
|
||||
end
|
||||
|
||||
local function new(args)
|
||||
local ret = object()
|
||||
local w = capi.drawin(args)
|
||||
|
||||
ret.drawin = w
|
||||
|
||||
for k, v in pairs(_M) do
|
||||
if type(v) == "function" then
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- We use a timer with timeout 0, this makes sure that the wibox will only
|
||||
-- redrawn once even if there are multiple widget::updated events
|
||||
local t = capi.timer({ timeout = 0 })
|
||||
local function update_wibox()
|
||||
t:stop()
|
||||
do_redraw(ret)
|
||||
end
|
||||
t:connect_signal("timeout", update_wibox)
|
||||
|
||||
-- Start our timer when the widget changed
|
||||
ret.draw = function()
|
||||
if not t.started then
|
||||
t:start()
|
||||
end
|
||||
end
|
||||
|
||||
setup_signals(ret)
|
||||
|
||||
-- Set the default background
|
||||
ret:set_bg(args.bg or beautiful.bg_normal)
|
||||
ret:set_fg(args.fg or beautiful.fg_normal)
|
||||
|
||||
-- Make sure the wibox is drawn at least once
|
||||
ret.draw()
|
||||
|
||||
-- Due to the metatable below, we need this trick
|
||||
ret.widget = { __fake_widget = true }
|
||||
ret._widget_geometries = {}
|
||||
|
||||
-- Redirect all non-existing indexes to the "real" drawin
|
||||
setmetatable(ret, {
|
||||
__index = w,
|
||||
__newindex = w
|
||||
})
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function(_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,149 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local setmetatable = setmetatable
|
||||
local table = table
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
local base = require("wibox.layout.base")
|
||||
local widget_base = require("wibox.widget.base")
|
||||
|
||||
module("wibox.layout.align")
|
||||
|
||||
-- Draw the given align layout. dir describes the orientation of the layout, "x"
|
||||
-- means horizontal while "y" is vertical.
|
||||
local function draw(dir, layout, wibox, cr, width, height)
|
||||
local size_first = 0
|
||||
local size_third = 0
|
||||
local size_limit = dir == "y" and height or width
|
||||
|
||||
if layout.first then
|
||||
local w, h = width, height
|
||||
if dir == "y" then
|
||||
_, h = layout.first:fit(w, h)
|
||||
size_first = h
|
||||
else
|
||||
w, _ = layout.first:fit(w, h)
|
||||
size_first = w
|
||||
end
|
||||
base.draw_widget(wibox, cr, layout.first, 0, 0, w, h)
|
||||
end
|
||||
|
||||
if layout.third and size_first < size_limit then
|
||||
local w, h, x, y
|
||||
if dir == "y" then
|
||||
w, h = width, height - size_first
|
||||
_, h = layout.third:fit(w, h)
|
||||
x, y = 0, height - h
|
||||
size_third = h
|
||||
else
|
||||
w, h = width - size_first, height
|
||||
w, _ = layout.third:fit(w, h)
|
||||
x, y = width - w, 0
|
||||
size_third = w
|
||||
end
|
||||
base.draw_widget(wibox, cr, layout.third, x, y, w, h)
|
||||
end
|
||||
|
||||
if layout.second and size_first + size_third < size_limit then
|
||||
local x, y, w, h
|
||||
if dir == "y" then
|
||||
x, y = 0, size_first
|
||||
w, h = width, size_limit - size_first - size_third
|
||||
else
|
||||
x, y = size_first, 0
|
||||
w, h = size_limit - size_first - size_third, height
|
||||
end
|
||||
base.draw_widget(wibox, cr, layout.second, x, y, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
local function widget_changed(layout, old_w, new_w)
|
||||
if old_w then
|
||||
old_w:disconnect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
if new_w then
|
||||
widget_base.check_widget(new_w)
|
||||
new_w:connect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
layout._emit_updated()
|
||||
end
|
||||
|
||||
--- Set the layout's first widget. This is the widget that is at the left/top
|
||||
function set_first(layout, widget)
|
||||
widget_changed(layout, layout.first, widget)
|
||||
layout.first = widget
|
||||
end
|
||||
|
||||
--- Set the layout's second widget. This is the centered one.
|
||||
function set_second(layout, widget)
|
||||
widget_changed(layout, widget)
|
||||
layout.second = widget
|
||||
end
|
||||
|
||||
--- Set the layout's third widget. This is the widget that is at the right/bottom
|
||||
function set_third(layout, widget)
|
||||
widget_changed(layout, widget)
|
||||
layout.third = widget
|
||||
end
|
||||
|
||||
function reset(layout)
|
||||
for k, v in pairs({ "first", "second", "third" }) do
|
||||
layout[v] = nil
|
||||
end
|
||||
layout:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
local function get_layout(dir)
|
||||
local function draw_dir(layout, wibox, cr, width, height)
|
||||
draw(dir, layout, wibox, cr, width, height)
|
||||
end
|
||||
|
||||
local ret = widget_base.make_widget()
|
||||
ret.draw = draw_dir
|
||||
ret.fit = function(box, ...) return ... end
|
||||
ret._emit_updated = function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
for k, v in pairs(_M) do
|
||||
if type(v) == "function" then
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Returns a new horizontal align layout. An align layout can display up to
|
||||
-- three widgets. The widget set via :set_left() is left-aligned. :set_right()
|
||||
-- sets a widget which will be right-aligned. The remaining space between those
|
||||
-- two will be given to the widget set via :set_middle().
|
||||
function horizontal()
|
||||
local ret = get_layout("x")
|
||||
|
||||
ret.set_left = ret.set_first
|
||||
ret.set_middle = ret.set_second
|
||||
ret.set_right = ret.set_third
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Returns a new vertical align layout. An align layout can display up to
|
||||
-- three widgets. The widget set via :set_top() is top-aligned. :set_bottom()
|
||||
-- sets a widget which will be bottom-aligned. The remaining space between those
|
||||
-- two will be given to the widget set via :set_middle().
|
||||
function vertical()
|
||||
local ret = get_layout("y")
|
||||
|
||||
ret.set_top = ret.set_first
|
||||
ret.set_middle = ret.set_second
|
||||
ret.set_bottom = ret.set_third
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,66 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local pairs = pairs
|
||||
local pcall = pcall
|
||||
local print = print
|
||||
|
||||
module("wibox.layout.base")
|
||||
|
||||
--- Figure out the geometry in device coordinate space. This will break if
|
||||
-- someone rotates the coordinate space by a non-multiple of 90°.
|
||||
function rect_to_device_geometry(cr, x, y, width, height)
|
||||
local function min(a, b)
|
||||
if a < b then return a end
|
||||
return b
|
||||
end
|
||||
local function max(a, b)
|
||||
if a > b then return a end
|
||||
return b
|
||||
end
|
||||
|
||||
local x1, y1 = cr:user_to_device(x, y)
|
||||
local x2, y2 = cr:user_to_device(x + width, y + height)
|
||||
local x = min(x1, x2)
|
||||
local y = min(y1, y2)
|
||||
local width = max(x1, x2) - x
|
||||
local height = max(y1, y2) - y
|
||||
|
||||
return x, y, width, height
|
||||
end
|
||||
|
||||
--- Draw a widget via a cairo context
|
||||
-- @param wibox The wibox on which we are drawing
|
||||
-- @param cr The cairo context used
|
||||
-- @param widget The widget to draw (this uses widget:draw(cr, width, height)).
|
||||
-- @param x The position that the widget should get
|
||||
-- @param y The position that the widget should get
|
||||
-- @param width The widget's width
|
||||
-- @param height The widget's height
|
||||
function draw_widget(wibox, cr, widget, x, y, width, height)
|
||||
-- Use save() / restore() so that our modifications aren't permanent
|
||||
cr:save()
|
||||
|
||||
-- Move (0, 0) to the place where the widget should show up
|
||||
cr:translate(x, y)
|
||||
|
||||
-- Make sure the widget cannot draw outside of the allowed area
|
||||
cr:rectangle(0, 0, width, height)
|
||||
cr:clip()
|
||||
|
||||
-- Let the widget draw itself
|
||||
local success, msg = pcall(widget.draw, widget, wibox, cr, width, height)
|
||||
if not success then
|
||||
print("Error while drawing widget: " .. msg)
|
||||
end
|
||||
|
||||
-- Register the widget for input handling
|
||||
wibox:widget_at(widget, rect_to_device_geometry(cr, 0, 0, width, height))
|
||||
|
||||
cr:restore()
|
||||
end
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,153 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local base = require("wibox.layout.base")
|
||||
local widget_base = require("wibox.widget.base")
|
||||
local table = table
|
||||
local pairs = pairs
|
||||
|
||||
module("wibox.layout.fixed")
|
||||
|
||||
--- Draw a fixed layout. Each widget gets just the space it asks for.
|
||||
-- @param dir "x" for a horizontal layout and "y" for vertical.
|
||||
-- @param widgets The widgets to draw.
|
||||
-- @param fill_space Use all the available space, giving all that is left to the
|
||||
-- last widget
|
||||
-- @param cr The cairo context to use.
|
||||
-- @param width The available width.
|
||||
-- @param height The available height.
|
||||
-- @return The total space needed by the layout.
|
||||
function draw_fixed(dir, widgets, fill_space, wibox, cr, width, height)
|
||||
local pos = 0
|
||||
|
||||
for k, v in pairs(widgets) do
|
||||
local x, y, w, h
|
||||
if dir == "y" then
|
||||
x, y = 0, pos
|
||||
w, h = width, height - pos
|
||||
if k ~= #widgets or not fill_space then
|
||||
_, h = v:fit(w, h);
|
||||
end
|
||||
pos = pos + h
|
||||
else
|
||||
x, y = pos, 0
|
||||
w, h = width - pos, height
|
||||
if k ~= #widgets or not fill_space then
|
||||
w, _ = v:fit(w, h);
|
||||
end
|
||||
pos = pos + w
|
||||
end
|
||||
base.draw_widget(wibox, cr, v, x, y, w, h)
|
||||
|
||||
if (dir == "y" and pos >= height) or
|
||||
(dir ~= "y" and pos >= width) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Add a widget to the given fixed layout
|
||||
function add(layout, widget)
|
||||
widget_base.check_widget(widget)
|
||||
table.insert(layout.widgets, widget)
|
||||
widget:connect_signal("widget::updated", layout._emit_updated)
|
||||
layout._emit_updated()
|
||||
end
|
||||
|
||||
--- Fit the fixed layout into the given space
|
||||
-- @param dir "x" for a horizontal layout and "y" for vertical.
|
||||
-- @param widgets The widgets to fit.
|
||||
-- @param orig_width The available width.
|
||||
-- @param orig_height The available height.
|
||||
function fit_fixed(dir, widgets, orig_width, orig_height)
|
||||
local width, height = orig_width, orig_height
|
||||
local used_in_dir, used_max = 0, 0
|
||||
|
||||
for k, v in pairs(widgets) do
|
||||
local w, h = v:fit(width, height)
|
||||
local in_dir, max
|
||||
if dir == "y" then
|
||||
max, in_dir = w, h
|
||||
height = height - in_dir
|
||||
else
|
||||
in_dir, max = w, h
|
||||
width = width - in_dir
|
||||
end
|
||||
if max > used_max then
|
||||
used_max = max
|
||||
end
|
||||
used_in_dir = used_in_dir + in_dir
|
||||
|
||||
if width <= 0 or height <= 0 then
|
||||
if dir == "y" then
|
||||
used_in_dir = orig_height
|
||||
else
|
||||
used_in_dir = orig_width
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if dir == "y" then
|
||||
return used_max, used_in_dir
|
||||
end
|
||||
return used_in_dir, used_max
|
||||
end
|
||||
|
||||
--- Reset a fixed layout. This removes all widgets from the layout.
|
||||
function reset(layout)
|
||||
for k, v in pairs(layout.widget) do
|
||||
v:disconnect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
layout.widget = {}
|
||||
layout:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
--- Set the layout's fill_space property. If this property is true, the last
|
||||
-- widget will get all the space that is left. If this is false, the last widget
|
||||
-- won't be handled specially and there can be space left unused.
|
||||
function fill_space(layout, val)
|
||||
layout._fill_space = val
|
||||
layout:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
local function get_layout(dir)
|
||||
local function draw(layout, ...)
|
||||
draw_fixed(dir, layout.widgets, layout._fill_space, ...)
|
||||
end
|
||||
local function fit(layout, ...)
|
||||
return fit_fixed(dir, layout.widgets, ...)
|
||||
end
|
||||
|
||||
local ret = widget_base.make_widget()
|
||||
ret.draw = draw
|
||||
ret.fit = fit
|
||||
ret.add = add
|
||||
ret.reset = reset
|
||||
ret.fill_space = fill_space
|
||||
ret.widgets = {}
|
||||
ret._emit_updated = function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Returns a new horizontal fixed layout. Each widget will get as much space as it
|
||||
-- asks for and each widget will be drawn next to its neighboring widget.
|
||||
-- Widgets can be added via :add().
|
||||
function horizontal()
|
||||
return get_layout("x")
|
||||
end
|
||||
|
||||
--- Returns a new vertical fixed layout. Each widget will get as much space as it
|
||||
-- asks for and each widget will be drawn next to its neighboring widget.
|
||||
-- Widgets can be added via :add().
|
||||
function vertical()
|
||||
return get_layout("y")
|
||||
end
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,107 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local base = require("wibox.layout.base")
|
||||
local fixed = require("wibox.layout.fixed")
|
||||
local widget_base = require("wibox.widget.base")
|
||||
local table = table
|
||||
local pairs = pairs
|
||||
local floor = math.floor
|
||||
|
||||
module("wibox.layout.flex")
|
||||
|
||||
local function round(x)
|
||||
return floor(x + 0.5)
|
||||
end
|
||||
|
||||
--- Draw a flex layout. Each widget gets an equal share of the available space
|
||||
-- @param dir "x" for a horizontal layout and "y" for vertical.
|
||||
-- @param widgets The widgets to draw.
|
||||
-- @param cr The cairo context to use.
|
||||
-- @param width The available width.
|
||||
-- @param height The available height.
|
||||
-- @return The total space needed by the layout.
|
||||
function draw_flex(dir, widgets, wibox, cr, width, height)
|
||||
local pos = 0
|
||||
|
||||
local num = #widgets
|
||||
local space_per_item
|
||||
if dir == "y" then
|
||||
space_per_item = height / num
|
||||
else
|
||||
space_per_item = width / num
|
||||
end
|
||||
|
||||
for k, v in pairs(widgets) do
|
||||
local x, y, w, h
|
||||
if dir == "y" then
|
||||
x, y = 0, round(pos)
|
||||
w, h = width, floor(space_per_item)
|
||||
else
|
||||
x, y = round(pos), 0
|
||||
w, h = floor(space_per_item), height
|
||||
end
|
||||
base.draw_widget(wibox, cr, v, x, y, w, h)
|
||||
|
||||
pos = pos + space_per_item
|
||||
|
||||
if (dir == "y" and pos >= height) or
|
||||
(dir ~= "y" and pos >= width) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function add(layout, widget)
|
||||
widget_base.check_widget(widget)
|
||||
table.insert(layout.widgets, widget)
|
||||
widget:connect_signal("widget::updated", layout._emit_updated)
|
||||
layout._emit_updated()
|
||||
end
|
||||
|
||||
local function reset(layout)
|
||||
for k, v in pairs(layout.widgets) do
|
||||
v:disconnect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
layout.widgets = {}
|
||||
layout:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
local function get_layout(dir)
|
||||
local function draw(layout, wibox, cr, width, height)
|
||||
draw_flex(dir, layout.widgets, wibox, cr, width, height)
|
||||
end
|
||||
|
||||
local function fit(layout, width, height)
|
||||
return fixed.fit_fixed(dir, layout.widgets, width, height)
|
||||
end
|
||||
|
||||
local ret = widget_base.make_widget()
|
||||
ret.draw = draw
|
||||
ret.fit = fit
|
||||
ret.add = add
|
||||
ret.reset = reset
|
||||
ret.widgets = {}
|
||||
ret._emit_updated = function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Returns a new horizontal flex layout. A flex layout shares the available space
|
||||
-- equally among all widgets. Widgets can be added via :add(widget).
|
||||
function horizontal()
|
||||
return get_layout("x")
|
||||
end
|
||||
|
||||
--- Returns a new vertical flex layout. A flex layout shares the available space
|
||||
-- equally among all widgets. Widgets can be added via :add(widget).
|
||||
function vertical()
|
||||
return get_layout("y")
|
||||
end
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,16 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
require("wibox.layout.base")
|
||||
require("wibox.layout.fixed")
|
||||
require("wibox.layout.align")
|
||||
require("wibox.layout.flex")
|
||||
require("wibox.layout.rotate")
|
||||
require("wibox.layout.margin")
|
||||
|
||||
module("wibox.layout")
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,127 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
local setmetatable = setmetatable
|
||||
local base = require("wibox.layout.base")
|
||||
local widget_base = require("wibox.widget.base")
|
||||
|
||||
module("wibox.layout.margin")
|
||||
|
||||
--- Draw a margin layout
|
||||
function draw(layout, wibox, cr, width, height)
|
||||
local x = layout.left
|
||||
local y = layout.top
|
||||
local w = layout.right
|
||||
local h = layout.bottom
|
||||
|
||||
if not layout.widget or width <= x + w or height <= y + h then
|
||||
return
|
||||
end
|
||||
|
||||
base.draw_widget(wibox, cr, layout.widget, x, y, width - x - w, height - y - h)
|
||||
end
|
||||
|
||||
--- Fit a margin layout into the given space
|
||||
function fit(layout, width, height)
|
||||
local extra_w = layout.left + layout.right
|
||||
local extra_h = layout.top + layout.bottom
|
||||
local w, h = 0, 0
|
||||
if layout.widget then
|
||||
w, h = layout.widget:fit(width - extra_w, height - extra_h)
|
||||
end
|
||||
return w + extra_w, h + extra_h
|
||||
end
|
||||
|
||||
--- Set the widget that this layout adds a margin on.
|
||||
function set_widget(layout, widget)
|
||||
if layout.widget then
|
||||
layout.widget:disconnect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
if widget then
|
||||
widget_base.check_widget(widget)
|
||||
widget:connect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
layout.widget = widget
|
||||
layout._emit_updated()
|
||||
end
|
||||
|
||||
--- Set all the margins to val.
|
||||
function set_margins(layout, val)
|
||||
layout.left = val
|
||||
layout.right = val
|
||||
layout.top = val
|
||||
layout.bottom = val
|
||||
layout:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
--- Reset this layout. The widget will be unreferenced and the margins set to 0.
|
||||
function reset(layout)
|
||||
layout:set_widget(nil)
|
||||
layout:set_margins(0)
|
||||
end
|
||||
|
||||
--- Set the left margin that this layout adds to its widget.
|
||||
-- @param layout The layout you are modifying.
|
||||
-- @param margin The new margin to use.
|
||||
-- @name set_left
|
||||
-- @class function
|
||||
|
||||
--- Set the right margin that this layout adds to its widget.
|
||||
-- @param layout The layout you are modifying.
|
||||
-- @param margin The new margin to use.
|
||||
-- @name set_right
|
||||
-- @class function
|
||||
|
||||
--- Set the top margin that this layout adds to its widget.
|
||||
-- @param layout The layout you are modifying.
|
||||
-- @param margin The new margin to use.
|
||||
-- @name set_top
|
||||
-- @class function
|
||||
|
||||
--- Set the bottom margin that this layout adds to its widget.
|
||||
-- @param layout The layout you are modifying.
|
||||
-- @param margin The new margin to use.
|
||||
-- @name set_bottom
|
||||
-- @class function
|
||||
|
||||
-- Create setters for each direction
|
||||
for k, v in pairs({ "left", "right", "top", "bottom" }) do
|
||||
_M["set_" .. v] = function(layout, val)
|
||||
layout[v] = val
|
||||
layout:emit_signal("widget::updated")
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns a new margin layout.
|
||||
-- @param widget A widget to use (optional)
|
||||
-- @param margin A margin to use on every side of the widget (optional)
|
||||
local function new(widget, margin)
|
||||
local ret = widget_base.make_widget()
|
||||
|
||||
for k, v in pairs(_M) do
|
||||
if type(v) == "function" then
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
ret._emit_updated = function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
ret:set_margins(margin or 0)
|
||||
|
||||
if widget then
|
||||
ret:set_widget(widget)
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function(_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,123 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local error = error
|
||||
local pairs = pairs
|
||||
local pi = math.pi
|
||||
local type = type
|
||||
local setmetatable = setmetatable
|
||||
local tostring = tostring
|
||||
local base = require("wibox.layout.base")
|
||||
local widget_base = require("wibox.widget.base")
|
||||
|
||||
module("wibox.layout.rotate")
|
||||
|
||||
local function transform(layout, width, height)
|
||||
local dir = layout:get_direction()
|
||||
if dir == "east" or dir == "west" then
|
||||
return height, width
|
||||
end
|
||||
return width, height
|
||||
end
|
||||
|
||||
--- Draw this layout
|
||||
function draw(layout, wibox, cr, width, height)
|
||||
if not layout.widget then return { width = 0, height = 0 } end
|
||||
|
||||
cr:save()
|
||||
local dir = layout:get_direction()
|
||||
|
||||
if dir == "west" then
|
||||
cr:rotate(pi / 2)
|
||||
cr:translate(0, -width)
|
||||
elseif dir == "south" then
|
||||
cr:rotate(pi)
|
||||
cr:translate(-width, -height)
|
||||
elseif dir == "east" then
|
||||
cr:rotate(3 * pi / 2)
|
||||
cr:translate(-height, 0)
|
||||
end
|
||||
|
||||
-- Since we rotated, we might have to swap width and height.
|
||||
-- transform() does that for us.
|
||||
layout.widget:draw(wibox, cr, transform(layout, width, height))
|
||||
|
||||
-- Undo the rotation and translation from above.
|
||||
cr:restore()
|
||||
end
|
||||
|
||||
--- Fit this layout into the given area
|
||||
function fit(layout, width, height)
|
||||
if not layout.widget then
|
||||
return 0, 0
|
||||
end
|
||||
return transform(layout, layout.widget:fit(transform(layout, width, height)))
|
||||
end
|
||||
|
||||
--- Set the widget that this layout rotates.
|
||||
function set_widget(layout, widget)
|
||||
if layout.widget then
|
||||
layout.widget:disconnect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
if widget then
|
||||
widget_base.check_widget(widget)
|
||||
widget:connect_signal("widget::updated", layout._emit_updated)
|
||||
end
|
||||
layout.widget = widget
|
||||
layout._emit_updated()
|
||||
end
|
||||
|
||||
--- Reset this layout. The widget will be removed and the rotation reset.
|
||||
function reset(layout)
|
||||
layout.direction = nil
|
||||
layout:set_widget(nil)
|
||||
end
|
||||
|
||||
--- Set the direction of this rotating layout. Valid values are "north", "east",
|
||||
-- "south" and "west". On an invalid value, this function will throw an error.
|
||||
function set_direction(layout, dir)
|
||||
local allowed = {
|
||||
north = true,
|
||||
east = true,
|
||||
south = true,
|
||||
west = true
|
||||
}
|
||||
|
||||
if not allowed[dir] then
|
||||
error("Invalid direction for rotate layout: " .. tostring(dir))
|
||||
end
|
||||
|
||||
layout.direction = dir
|
||||
layout._emit_updated()
|
||||
end
|
||||
|
||||
--- Get the direction of this rotating layout
|
||||
function get_direction(layout)
|
||||
return layout.direction or "north"
|
||||
end
|
||||
|
||||
--- Returns a new rotate layout. A rotate layout rotates a given widget. Use
|
||||
-- :set_widget() to set the widget and :set_direction() for the direction.
|
||||
-- The default direction is "north" which doesn't change anything.
|
||||
local function new()
|
||||
local ret = widget_base.make_widget()
|
||||
|
||||
for k, v in pairs(_M) do
|
||||
if type(v) == "function" then
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
ret._emit_updated = function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function(_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,102 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local oocairo = require("oocairo")
|
||||
local base = require("wibox.widget.base")
|
||||
local color = require("gears.color")
|
||||
local widget_base = require("wibox.widget.base")
|
||||
local setmetatable = setmetatable
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
|
||||
module("wibox.widget.background")
|
||||
|
||||
--- Draw this widget
|
||||
function draw(box, wibox, cr, width, height)
|
||||
if not box.widget then
|
||||
return
|
||||
end
|
||||
|
||||
cr:save()
|
||||
|
||||
if box.background then
|
||||
cr:set_source(box.background)
|
||||
cr:paint()
|
||||
end
|
||||
if box.bgimage then
|
||||
local pattern = oocairo.pattern_create_for_surface(box.bgimage)
|
||||
cr:set_source(pattern)
|
||||
cr:paint()
|
||||
end
|
||||
|
||||
cr:restore()
|
||||
|
||||
box.widget:draw(wibox, cr, width, height)
|
||||
end
|
||||
|
||||
--- Fit this widget into the given area
|
||||
function fit(box, width, height)
|
||||
if not box.widget then
|
||||
return 0, 0
|
||||
end
|
||||
|
||||
return box.widget:fit(width, height)
|
||||
end
|
||||
|
||||
--- Set the widget that is drawn on top of the background
|
||||
function set_widget(box, widget)
|
||||
if box.widget then
|
||||
box.widget:disconnect_signal("widget::updated", box._emit_updated)
|
||||
end
|
||||
if widget then
|
||||
widget_base.check_widget(widget)
|
||||
widget:connect_signal("widget::updated", box._emit_updated)
|
||||
end
|
||||
box.widget = widget
|
||||
box._emit_updated()
|
||||
end
|
||||
|
||||
--- Set the background to use
|
||||
function set_bg(box, bg)
|
||||
if bg then
|
||||
box.background = color(bg)
|
||||
else
|
||||
box.background = nil
|
||||
end
|
||||
box._emit_updated()
|
||||
end
|
||||
|
||||
--- Set the background image to use
|
||||
function set_bgimage(box, image)
|
||||
local image = image
|
||||
|
||||
if type(image) == "string" then
|
||||
image = oocairo.image_surface_create_from_png(image)
|
||||
end
|
||||
|
||||
box.bgimage = image
|
||||
box._emit_updated()
|
||||
end
|
||||
|
||||
local function new()
|
||||
local ret = base.make_widget()
|
||||
|
||||
for k, v in pairs(_M) do
|
||||
if type(v) == "function" then
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
ret._emit_updated = function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function (_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,122 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local object = require("gears.object")
|
||||
local pairs = pairs
|
||||
local error = error
|
||||
local type = type
|
||||
local table = table
|
||||
|
||||
module("wibox.widget.base")
|
||||
|
||||
--- Set/get a widget's buttons
|
||||
function buttons(widget, buttons)
|
||||
if buttons then
|
||||
widget.widget_buttons = buttons
|
||||
end
|
||||
|
||||
return widget.widget_buttons
|
||||
end
|
||||
|
||||
--- Handle a button event on a widget. This is used internally.
|
||||
function handle_button(event, widget, x, y, button, modifiers)
|
||||
local function is_any(mod)
|
||||
if #mod ~= 1 then
|
||||
return false
|
||||
end
|
||||
if mod[1] ~= "Any" then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function tables_equal(a, b)
|
||||
if #a ~= #b then
|
||||
return false
|
||||
end
|
||||
for k, v in pairs(b) do
|
||||
if a[k] ~= v then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Find all matching button objects
|
||||
local matches = {}
|
||||
for k, v in pairs(widget.widget_buttons) do
|
||||
local match = true
|
||||
-- Is it the right button?
|
||||
if v.button ~= 0 and v.button ~= button then match = false end
|
||||
-- Are the correct modifiers pressed?
|
||||
if (not is_any(v.modifiers)) and (not tables_equal(v.modifiers, modifiers)) then match = false end
|
||||
if match then
|
||||
table.insert(matches, v)
|
||||
end
|
||||
end
|
||||
|
||||
-- Emit the signals
|
||||
for k, v in pairs(matches) do
|
||||
v:emit_signal(event)
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a new widget. All widgets have to be generated via this function so
|
||||
-- that the needed signals are added and mouse input handling is set up.
|
||||
-- @param proxy If this is set, the returned widget will be a proxy for this
|
||||
-- widget. It will be equivalent to this widget.
|
||||
function make_widget(proxy)
|
||||
local ret = object()
|
||||
|
||||
-- This signal is used by layouts to find out when they have to update.
|
||||
ret:add_signal("widget::updated")
|
||||
-- Mouse input, oh noes!
|
||||
ret:add_signal("button::press")
|
||||
ret:add_signal("button::release")
|
||||
|
||||
-- No buttons yet
|
||||
ret.widget_buttons = {}
|
||||
ret.buttons = buttons
|
||||
|
||||
-- Make buttons work
|
||||
ret:connect_signal("button::press", function(...)
|
||||
return handle_button("press", ...)
|
||||
end)
|
||||
ret:connect_signal("button::release", function(...)
|
||||
return handle_button("release", ...)
|
||||
end)
|
||||
|
||||
if proxy then
|
||||
ret.size = function(_, ...) return proxy:size(...) end
|
||||
ret.draw = function(_, ...) return proxy:draw(...) end
|
||||
ret.fit = function(_, ...) return proxy:fit(...) end
|
||||
proxy:connect_signal("widget::updated", function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end)
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Do some sanity checking on widget. This function raises a lua error if
|
||||
-- widget is not a valid widget.
|
||||
function check_widget(widget)
|
||||
if type(widget) ~= "table" then
|
||||
error("widget is not a table?!")
|
||||
end
|
||||
for k, func in pairs({ "draw", "fit", "add_signal", "connect_signal", "disconnect_signal" }) do
|
||||
if type(widget[func]) ~= "function" then
|
||||
error("widget's " .. func .. "() is not a function?!")
|
||||
end
|
||||
end
|
||||
|
||||
local width, height = widget:fit(0, 0)
|
||||
if type(width) ~= "number" or type(height) ~= "number" then
|
||||
error("widget's fit() didn't return two numbers")
|
||||
end
|
||||
end
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,114 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local oocairo = require("oocairo")
|
||||
local base = require("wibox.widget.base")
|
||||
local setmetatable = setmetatable
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
local pcall = pcall
|
||||
local print = print
|
||||
|
||||
module("wibox.widget.imagebox")
|
||||
|
||||
--- Draw an imagebox with the given cairo context in the given geometry.
|
||||
function draw(box, wibox, cr, width, height)
|
||||
if not box.image then return end
|
||||
|
||||
cr:save()
|
||||
|
||||
if not box.resize_forbidden then
|
||||
-- Let's scale the image so that it fits into (width, height)
|
||||
local w = box.image:get_width()
|
||||
local h = box.image:get_height()
|
||||
local aspect = width / w
|
||||
local aspect_h = height / h
|
||||
if aspect > aspect_h then aspect = aspect_h end
|
||||
|
||||
cr:scale(aspect, aspect)
|
||||
end
|
||||
cr:set_source(box.image)
|
||||
cr:paint()
|
||||
|
||||
cr:restore()
|
||||
end
|
||||
|
||||
--- Fit the imagebox into the given geometry
|
||||
function fit(box, width, height)
|
||||
if not box.image then
|
||||
return 0, 0
|
||||
end
|
||||
|
||||
local w = box.image:get_width()
|
||||
local h = box.image:get_height()
|
||||
|
||||
if w > width then
|
||||
h = h * width / w
|
||||
w = width
|
||||
end
|
||||
if h > height then
|
||||
w = w * height / h
|
||||
h = height
|
||||
end
|
||||
|
||||
if not box.resize_forbidden then
|
||||
local aspect = width / w
|
||||
local aspect_h = height / h
|
||||
|
||||
-- Use the smaller one of the two aspect ratios.
|
||||
if aspect > aspect_h then aspect = aspect_h end
|
||||
|
||||
w, h = w * aspect, h * aspect
|
||||
end
|
||||
|
||||
return w, h
|
||||
end
|
||||
|
||||
--- Set an imagebox' image
|
||||
-- @param image Either a string or a cairo image surface. A string is
|
||||
-- interpreted as the path to a png image file.
|
||||
function set_image(box, image)
|
||||
local image = image
|
||||
|
||||
if type(image) == "string" then
|
||||
local success, result = pcall(oocairo.image_surface_create_from_png, image)
|
||||
if not success then
|
||||
print("Error while reading '" .. image .. "': " .. result)
|
||||
return false
|
||||
end
|
||||
image = result
|
||||
end
|
||||
|
||||
box.image = image
|
||||
|
||||
box:emit_signal("widget::updated")
|
||||
return true
|
||||
end
|
||||
|
||||
--- Should the image be resized to fit into the available space?
|
||||
-- @param allowed If false, the image will be clipped, else it will be resized
|
||||
-- to fit into the available space.
|
||||
function set_resize(box, allowed)
|
||||
box.resize_forbidden = not allowed
|
||||
box:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
-- Returns a new imagebox
|
||||
local function new()
|
||||
local ret = base.make_widget()
|
||||
|
||||
for k, v in pairs(_M) do
|
||||
if type(v) == "function" then
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function (_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,15 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
require("wibox.widget.base")
|
||||
require("wibox.widget.textbox")
|
||||
require("wibox.widget.imagebox")
|
||||
require("wibox.widget.background")
|
||||
require("wibox.widget.systray")
|
||||
|
||||
module("wibox.widget")
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,68 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local wbase = require("wibox.widget.base")
|
||||
local lbase = require("wibox.layout.base")
|
||||
local capi = { awesome = awesome }
|
||||
local setmetatable = setmetatable
|
||||
local error = error
|
||||
|
||||
module("wibox.widget.systray")
|
||||
|
||||
local created_systray = false
|
||||
local horizontal = true
|
||||
local base_size = 16
|
||||
|
||||
function draw(box, wibox, cr, width, height)
|
||||
local x, y, width, height = lbase.rect_to_device_geometry(cr, 0, 0, width, height)
|
||||
local num_entries = capi.awesome.systray()
|
||||
|
||||
local width, height = width, height
|
||||
local in_dir, ortho, base_size
|
||||
if horizontal then
|
||||
in_dir, ortho = width, height
|
||||
else
|
||||
ortho, in_dir = width, height
|
||||
end
|
||||
if ortho * num_entries <= in_dir then
|
||||
base = ortho
|
||||
else
|
||||
base = in_dir / num_entries
|
||||
end
|
||||
capi.awesome.systray(wibox.drawin, x, y, base, horizontal)
|
||||
end
|
||||
|
||||
function fit(box, width, height)
|
||||
local num_entries = capi.awesome.systray()
|
||||
if horizontal then
|
||||
return base_size * num_entries, base_size
|
||||
end
|
||||
return base_size, base_size * num_entries
|
||||
end
|
||||
|
||||
local function new(horiz)
|
||||
local ret = wbase.make_widget()
|
||||
|
||||
horizontal = horiz
|
||||
if created_systray then
|
||||
error("More than one systray created!")
|
||||
end
|
||||
created_systray = true
|
||||
|
||||
ret.fit = fit
|
||||
ret.draw = draw
|
||||
ret.set_base_size = function(_, size) base_size = size end
|
||||
|
||||
capi.awesome.connect_signal("systray::update", function()
|
||||
ret:emit_signal("widget::updated")
|
||||
end)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function (_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
|
@ -0,0 +1,167 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Uli Schlachter
|
||||
-- @copyright 2010 Uli Schlachter
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local oopango = require("oopango")
|
||||
local oocairo = require("oocairo")
|
||||
local base = require("wibox.widget.base")
|
||||
local beautiful = require("beautiful")
|
||||
local type = type
|
||||
local unpack = unpack
|
||||
local setmetatable = setmetatable
|
||||
local pairs = pairs
|
||||
|
||||
module("wibox.widget.textbox")
|
||||
|
||||
-- Setup a pango layout for the given textbox and cairo context
|
||||
local function setup_layout(box, cr, width, height)
|
||||
local layout = oopango.cairo.layout_create(cr)
|
||||
layout:set_ellipsize(box.ellipsize)
|
||||
layout:set_wrap(box.wrap)
|
||||
layout:set_width(oopango.units_from_number(width))
|
||||
layout:set_height(oopango.units_from_number(height))
|
||||
|
||||
if box.markup then
|
||||
layout:set_markup(box.text)
|
||||
else
|
||||
layout:set_text(box.text)
|
||||
end
|
||||
|
||||
if box.font then
|
||||
layout:set_font_description(box.font)
|
||||
else
|
||||
layout:set_font_description(beautiful.get_font())
|
||||
end
|
||||
|
||||
local ink, logical = layout:get_pixel_extents()
|
||||
local offset = 0
|
||||
if box.valign == "center" then
|
||||
offset = (height - logical.height) / 2
|
||||
elseif box.valign == "bottom" then
|
||||
offset = height - logical.height
|
||||
end
|
||||
if offset > 0 then
|
||||
cr:move_to(0, offset)
|
||||
end
|
||||
|
||||
return layout
|
||||
end
|
||||
|
||||
-- Get the size that the given textbox covers.
|
||||
-- If layout is given, it's :get_width()/get_height() is honoured.
|
||||
local function get_size(box, layout, width, height)
|
||||
local ret = {
|
||||
width = 0,
|
||||
height = 0
|
||||
}
|
||||
|
||||
if box.text then
|
||||
local layout = layout
|
||||
|
||||
if not layout then
|
||||
-- Create a temporary surface that we need for computing the extents :(
|
||||
local surface = oocairo.image_surface_create("argb32", 1, 1)
|
||||
local cr = oocairo.context_create(surface)
|
||||
layout = setup_layout(box, cr, width, height)
|
||||
end
|
||||
|
||||
local ink, logical = layout:get_pixel_extents()
|
||||
|
||||
ret.width = logical.width
|
||||
ret.height = logical.height
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Draw the given textbox on the given cairo context in the given geometry
|
||||
function draw(box, wibox, cr, width, height)
|
||||
if not box.text then return end
|
||||
|
||||
local layout = setup_layout(box, cr, width, height)
|
||||
|
||||
oopango.cairo.update_layout(cr, layout)
|
||||
oopango.cairo.show_layout(cr, layout)
|
||||
end
|
||||
|
||||
--- Fit the given textbox
|
||||
function fit(box, width, height)
|
||||
local res = get_size(box, nil, width, height)
|
||||
return res.width, res.height
|
||||
end
|
||||
|
||||
--- Test if a textbox' text is valid. If it isn't, a lua error will be thrown.
|
||||
function check(box)
|
||||
-- This creates a pango layout for the string and so forces pango to verify
|
||||
-- whether we have some sane input for it
|
||||
get_size(box, nil, -1, -1)
|
||||
end
|
||||
|
||||
--- Set a textbox' text.
|
||||
-- @param text The text to set. This can contain pango markup (e.g. <b>bold</b>)
|
||||
function set_markup(box, text)
|
||||
box.text = text
|
||||
box.markup = true
|
||||
box:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
--- Set a textbox' text.
|
||||
-- @param text The text to display. Pango markup is ignored and shown as-is.
|
||||
function set_text(box, text)
|
||||
box.text = text
|
||||
box.markup = false
|
||||
box:emit_signal("widget::updated")
|
||||
end
|
||||
|
||||
--- Set a textbox' ellipsize mode.
|
||||
-- @param mode Where should long lines be shortened? "start", "middle" or "end"
|
||||
function set_ellipsize(box, mode)
|
||||
local allowed = { none = true, start = true, middle = true, ["end"] = true }
|
||||
if allowed[mode] then
|
||||
box.ellipsize = mode
|
||||
box:emit_signal("widget::updated")
|
||||
end
|
||||
end
|
||||
|
||||
--- Set a textbox' wrap mode.
|
||||
-- @param mode Where to wrap? After "word", "char" or "word_char"
|
||||
function set_wrap(box, mode)
|
||||
local allowed = { word = true, char = true, word_char = true }
|
||||
if allowed[mode] then
|
||||
box.wrap = mode
|
||||
box:emit_signal("widget::updated")
|
||||
end
|
||||
end
|
||||
|
||||
--- Set a textbox' vertical alignment
|
||||
-- @param mode Where should the textbox be drawn? "top", "center" or "bottom"
|
||||
function set_valign(box, mode)
|
||||
local allowed = { top = true, center = true, bottom = true }
|
||||
if allowed[mode] then
|
||||
box.valign = mode
|
||||
box:emit_signal("widget::updated")
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns a new textbox
|
||||
local function new()
|
||||
local ret = base.make_widget()
|
||||
|
||||
for k, v in pairs(_M) do
|
||||
if type(v) == "function" then
|
||||
ret[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
ret.ellipsize = "end"
|
||||
ret.wrap = "word_char"
|
||||
ret.valign = "center"
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function (_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
Loading…
Reference in New Issue