diff options
Diffstat (limited to 'lib/awful/widget')
-rw-r--r-- | lib/awful/widget/button.lua | 263 | ||||
-rw-r--r-- | lib/awful/widget/common.lua | 119 | ||||
-rw-r--r-- | lib/awful/widget/graph.lua | 16 | ||||
-rw-r--r-- | lib/awful/widget/init.lua | 24 | ||||
-rw-r--r-- | lib/awful/widget/keyboardlayout.lua | 308 | ||||
-rw-r--r-- | lib/awful/widget/launcher.lua | 41 | ||||
-rw-r--r-- | lib/awful/widget/layoutbox.lua | 73 | ||||
-rw-r--r-- | lib/awful/widget/progressbar.lua | 16 | ||||
-rw-r--r-- | lib/awful/widget/prompt.lua | 64 | ||||
-rw-r--r-- | lib/awful/widget/taglist.lua | 452 | ||||
-rw-r--r-- | lib/awful/widget/tasklist.lua | 573 | ||||
-rw-r--r-- | lib/awful/widget/textclock.lua | 16 | ||||
-rw-r--r-- | lib/awful/widget/watch.lua | 91 |
13 files changed, 2056 insertions, 0 deletions
diff --git a/lib/awful/widget/button.lua b/lib/awful/widget/button.lua new file mode 100644 index 0000000..388f9dd --- /dev/null +++ b/lib/awful/widget/button.lua @@ -0,0 +1,263 @@ +--------------------------------------------------------------------------- +-- A simple button widget. +-- @usage local button = awful.widget.button() +-- button:buttons(awful.util.table.join( +-- button:buttons(), +-- awful.button({}, 1, nil, function () +-- print("Mouse was clicked") +-- end) +-- )) +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008-2009 Julien Danjou +-- @classmod awful.widget.button +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local abutton = require("awful.button") +local imagebox = require("wibox.widget.imagebox") +local widget = require("wibox.widget.base") +local surface = require("gears.surface") +local cairo = require("lgi").cairo + +local button = { mt = {} } + +--- Create a button widget. When clicked, the image is deplaced to make it like +-- a real button. +-- +-- @param args Widget arguments. "image" is the image to display. +-- @return A textbox widget configured as a button. +function button.new(args) + if not args or not args.image then + return widget.empty_widget() + end + + local w = imagebox() + local orig_set_image = w.set_image + local img_release + local img_press + + function w:set_image(image) + img_release = surface.load(image) + img_press = img_release:create_similar(cairo.Content.COLOR_ALPHA, img_release.width, img_release.height) + local cr = cairo.Context(img_press) + cr:set_source_surface(img_release, 2, 2) + cr:paint() + orig_set_image(self, img_release) + end + w:set_image(args.image) + w:buttons(abutton({}, 1, function () orig_set_image(w, img_press) end, function () orig_set_image(w, img_release) end)) + + w:connect_signal("mouse::leave", function(self) orig_set_image(self, img_release) end) + + return w +end + +function button.mt:__call(...) + return button.new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(button, button.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/common.lua b/lib/awful/widget/common.lua new file mode 100644 index 0000000..e9ae699 --- /dev/null +++ b/lib/awful/widget/common.lua @@ -0,0 +1,119 @@ +--------------------------------------------------------------------------- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008-2009 Julien Danjou +-- @classmod awful.widget.common +--------------------------------------------------------------------------- + +-- Grab environment we need +local type = type +local ipairs = ipairs +local capi = { button = button } +local wibox = require("wibox") +local dpi = require("beautiful").xresources.apply_dpi + +--- Common utilities for awful widgets +local common = {} + +--- Common method to create buttons. +-- @tab buttons +-- @param object +-- @treturn table +function common.create_buttons(buttons, object) + if buttons then + local btns = {} + for _, b in ipairs(buttons) do + -- Create a proxy button object: it will receive the real + -- press and release events, and will propagate them to the + -- button object the user provided, but with the object as + -- argument. + local btn = capi.button { modifiers = b.modifiers, button = b.button } + btn:connect_signal("press", function () b:emit_signal("press", object) end) + btn:connect_signal("release", function () b:emit_signal("release", object) end) + btns[#btns + 1] = btn + end + + return btns + end +end + +--- Common update method. +-- @param w The widget. +-- @tab buttons +-- @func label Function to generate label parameters from an object. +-- The function gets passed an object from `objects`, and +-- has to return `text`, `bg`, `bg_image`, `icon`. +-- @tab data Current data/cache, indexed by objects. +-- @tab objects Objects to be displayed / updated. +function common.list_update(w, buttons, label, data, objects) + -- update the widgets, creating them if needed + w:reset() + for i, o in ipairs(objects) do + local cache = data[o] + local ib, tb, bgb, tbm, ibm, l + if cache then + ib = cache.ib + tb = cache.tb + bgb = cache.bgb + tbm = cache.tbm + ibm = cache.ibm + else + ib = wibox.widget.imagebox() + tb = wibox.widget.textbox() + bgb = wibox.container.background() + tbm = wibox.container.margin(tb, dpi(4), dpi(4)) + ibm = wibox.container.margin(ib, dpi(4)) + l = wibox.layout.fixed.horizontal() + + -- All of this is added in a fixed widget + l:fill_space(true) + l:add(ibm) + l:add(tbm) + + -- And all of this gets a background + bgb:set_widget(l) + + bgb:buttons(common.create_buttons(buttons, o)) + + data[o] = { + ib = ib, + tb = tb, + bgb = bgb, + tbm = tbm, + ibm = ibm, + } + end + + local text, bg, bg_image, icon, args = label(o, tb) + args = args or {} + + -- The text might be invalid, so use pcall. + if text == nil or text == "" then + tbm:set_margins(0) + else + if not tb:set_markup_silently(text) then + tb:set_markup("<i><Invalid text></i>") + end + end + bgb:set_bg(bg) + if type(bg_image) == "function" then + -- TODO: Why does this pass nil as an argument? + bg_image = bg_image(tb,o,nil,objects,i) + end + bgb:set_bgimage(bg_image) + if icon then + ib:set_image(icon) + else + ibm:set_margins(0) + end + + bgb.shape = args.shape + bgb.shape_border_width = args.shape_border_width + bgb.shape_border_color = args.shape_border_color + + w:add(bgb) + end +end + +return common + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/graph.lua b/lib/awful/widget/graph.lua new file mode 100644 index 0000000..e231463 --- /dev/null +++ b/lib/awful/widget/graph.lua @@ -0,0 +1,16 @@ +--------------------------------------------------------------------------- +--- This module has been moved to `wibox.widget.graph` +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Julien Danjou +-- @classmod awful.widget.graph +--------------------------------------------------------------------------- +local util = require("awful.util") + +return util.deprecate_class( + require("wibox.widget.graph"), + "awful.widget.graph", + "wibox.widget.graph" +) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/init.lua b/lib/awful/widget/init.lua new file mode 100644 index 0000000..02400a1 --- /dev/null +++ b/lib/awful/widget/init.lua @@ -0,0 +1,24 @@ +--------------------------------------------------------------------------- +--- Widget module for awful +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008-2009 Julien Danjou +-- @classmod awful.widget +--------------------------------------------------------------------------- + +return +{ + taglist = require("awful.widget.taglist"); + tasklist = require("awful.widget.tasklist"); + button = require("awful.widget.button"); + launcher = require("awful.widget.launcher"); + prompt = require("awful.widget.prompt"); + progressbar = require("awful.widget.progressbar"); + graph = require("awful.widget.graph"); + layoutbox = require("awful.widget.layoutbox"); + textclock = require("awful.widget.textclock"); + keyboardlayout = require("awful.widget.keyboardlayout"); + watch = require("awful.widget.watch"); +} + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/keyboardlayout.lua b/lib/awful/widget/keyboardlayout.lua new file mode 100644 index 0000000..4f79a81 --- /dev/null +++ b/lib/awful/widget/keyboardlayout.lua @@ -0,0 +1,308 @@ +--------------------------------------------------------------------------- +-- @author Aleksey Fedotov <lexa@cfotr.com> +-- @copyright 2015 Aleksey Fedotov +-- @classmod wibox.widget.keyboardlayout +--------------------------------------------------------------------------- + +local capi = {awesome = awesome} +local setmetatable = setmetatable +local textbox = require("wibox.widget.textbox") +local button = require("awful.button") +local util = require("awful.util") +local widget_base = require("wibox.widget.base") +local gdebug = require("gears.debug") + +--- Keyboard Layout widget. +-- awful.widget.keyboardlayout +local keyboardlayout = { mt = {} } + +-- As to the country-code-like symbols below, refer to the names of XKB's +-- data files in /.../xkb/symbols/*. +keyboardlayout.xkeyboard_country_code = { + ["ad"] = true, -- Andorra + ["af"] = true, -- Afganistan + ["al"] = true, -- Albania + ["am"] = true, -- Armenia + ["ara"] = true, -- Arabic + ["at"] = true, -- Austria + ["az"] = true, -- Azerbaijan + ["ba"] = true, -- Bosnia and Herzegovina + ["bd"] = true, -- Bangladesh + ["be"] = true, -- Belgium + ["bg"] = true, -- Bulgaria + ["br"] = true, -- Brazil + ["bt"] = true, -- Bhutan + ["bw"] = true, -- Botswana + ["by"] = true, -- Belarus + ["ca"] = true, -- Canada + ["cd"] = true, -- Congo + ["ch"] = true, -- Switzerland + ["cm"] = true, -- Cameroon + ["cn"] = true, -- China + ["cz"] = true, -- Czechia + ["de"] = true, -- Germany + ["dk"] = true, -- Denmark + ["ee"] = true, -- Estonia + ["epo"] = true, -- Esperanto + ["es"] = true, -- Spain + ["et"] = true, -- Ethiopia + ["fi"] = true, -- Finland + ["fo"] = true, -- Faroe Islands + ["fr"] = true, -- France + ["gb"] = true, -- United Kingdom + ["ge"] = true, -- Georgia + ["gh"] = true, -- Ghana + ["gn"] = true, -- Guinea + ["gr"] = true, -- Greece + ["hr"] = true, -- Croatia + ["hu"] = true, -- Hungary + ["ie"] = true, -- Ireland + ["il"] = true, -- Israel + ["in"] = true, -- India + ["iq"] = true, -- Iraq + ["ir"] = true, -- Iran + ["is"] = true, -- Iceland + ["it"] = true, -- Italy + ["jp"] = true, -- Japan + ["ke"] = true, -- Kenya + ["kg"] = true, -- Kyrgyzstan + ["kh"] = true, -- Cambodia + ["kr"] = true, -- Korea + ["kz"] = true, -- Kazakhstan + ["la"] = true, -- Laos + ["latam"] = true, -- Latin America + ["latin"] = true, -- Latin + ["lk"] = true, -- Sri Lanka + ["lt"] = true, -- Lithuania + ["lv"] = true, -- Latvia + ["ma"] = true, -- Morocco + ["mao"] = true, -- Maori + ["me"] = true, -- Montenegro + ["mk"] = true, -- Macedonia + ["ml"] = true, -- Mali + ["mm"] = true, -- Myanmar + ["mn"] = true, -- Mongolia + ["mt"] = true, -- Malta + ["mv"] = true, -- Maldives + ["ng"] = true, -- Nigeria + ["nl"] = true, -- Netherlands + ["no"] = true, -- Norway + ["np"] = true, -- Nepal + ["ph"] = true, -- Philippines + ["pk"] = true, -- Pakistan + ["pl"] = true, -- Poland + ["pt"] = true, -- Portugal + ["ro"] = true, -- Romania + ["rs"] = true, -- Serbia + ["ru"] = true, -- Russia + ["se"] = true, -- Sweden + ["si"] = true, -- Slovenia + ["sk"] = true, -- Slovakia + ["sn"] = true, -- Senegal + ["sy"] = true, -- Syria + ["th"] = true, -- Thailand + ["tj"] = true, -- Tajikistan + ["tm"] = true, -- Turkmenistan + ["tr"] = true, -- Turkey + ["tw"] = true, -- Taiwan + ["tz"] = true, -- Tanzania + ["ua"] = true, -- Ukraine + ["us"] = true, -- USA + ["uz"] = true, -- Uzbekistan + ["vn"] = true, -- Vietnam + ["za"] = true, -- South Africa +} + +-- Callback for updating current layout. +local function update_status (self) + self._current = awesome.xkb_get_layout_group(); + local text = "" + if (#self._layout > 0) then + text = (" " .. self._layout[self._current] .. " ") + end + self.widget:set_text(text) +end + +--- Auxiliary function for the local function update_layout(). +-- Create an array whose element is a table consisting of the four fields: +-- vendor, file, section and group_idx, which all correspond to the +-- xkb_symbols pattern "vendor/file(section):group_idx". +-- @tparam string group_names The string awesome.xkb_get_group_names() returns. +-- @treturn table An array of tables whose keys are vendor, file, section, and group_idx. +function keyboardlayout.get_groups_from_group_names(group_names) + if group_names == nil then + return nil + end + + -- Pattern elements to be captured. + local word_pat = "([%w_]+)" + local sec_pat = "(%b())" + local idx_pat = ":(%d)" + -- Pairs of a pattern and its callback. In callbacks, set 'group_idx' to 1 + -- and return it if there's no specification on 'group_idx' in the given + -- pattern. + local pattern_and_callback_pairs = { + -- vendor/file(section):group_idx + ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. idx_pat .. "$"] + = function(token, pattern) + local vendor, file, section, group_idx = string.match(token, pattern) + return vendor, file, section, group_idx + end, + -- vendor/file(section) + ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. "$"] + = function(token, pattern) + local vendor, file, section = string.match(token, pattern) + return vendor, file, section, 1 + end, + -- vendor/file:group_idx + ["^" .. word_pat .. "/" .. word_pat .. idx_pat .. "$"] + = function(token, pattern) + local vendor, file, group_idx = string.match(token, pattern) + return vendor, file, nil, group_idx + end, + -- vendor/file + ["^" .. word_pat .. "/" .. word_pat .. "$"] + = function(token, pattern) + local vendor, file = string.match(token, pattern) + return vendor, file, nil, 1 + end, + -- file(section):group_idx + ["^" .. word_pat .. sec_pat .. idx_pat .. "$"] + = function(token, pattern) + local file, section, group_idx = string.match(token, pattern) + return nil, file, section, group_idx + end, + -- file(section) + ["^" .. word_pat .. sec_pat .. "$"] + = function(token, pattern) + local file, section = string.match(token, pattern) + return nil, file, section, 1 + end, + -- file:group_idx + ["^" .. word_pat .. idx_pat .. "$"] + = function(token, pattern) + local file, group_idx = string.match(token, pattern) + return nil, file, nil, group_idx + end, + -- file + ["^" .. word_pat .. "$"] + = function(token, pattern) + local file = string.match(token, pattern) + return nil, file, nil, 1 + end + } + + -- Split 'group_names' into 'tokens'. The separator is "+". + local tokens = {} + string.gsub(group_names, "[^+]+", function(match) + table.insert(tokens, match) + end) + + -- For each token in 'tokens', check if it matches one of the patterns in + -- the array 'pattern_and_callback_pairs', where the patterns are used as + -- key. If a match is found, extract captured strings using the + -- corresponding callback function. Check if those extracted is country + -- specific part of a layout. If so, add it to 'layout_groups'; otherwise, + -- ignore it. + local layout_groups = {} + for i = 1, #tokens do + for pattern, callback in pairs(pattern_and_callback_pairs) do + local vendor, file, section, group_idx = callback(tokens[i], pattern) + if file then + if not keyboardlayout.xkeyboard_country_code[file] then + break + end + + if section then + section = string.gsub(section, "%(([%w-_]+)%)", "%1") + end + + table.insert(layout_groups, { vendor = vendor, + file = file, + section = section, + group_idx = tonumber(group_idx) }) + break + end + end + end + + return layout_groups +end + +-- Callback for updating list of layouts +local function update_layout(self) + self._layout = {}; + local layouts = keyboardlayout.get_groups_from_group_names(awesome.xkb_get_group_names()) + if layouts == nil or layouts[1] == nil then + gdebug.print_error("Failed to get list of keyboard groups") + return + end + if #layouts == 1 then + layouts[1].group_idx = 0 + end + for _, v in ipairs(layouts) do + local layout_name = self.layout_name(v) + -- Please note that numbers of groups reported by xkb_get_group_names + -- is greater by one than the real group number. + self._layout[v.group_idx - 1] = layout_name + end + update_status(self) +end + +--- Create a keyboard layout widget. It shows current keyboard layout name in a textbox. +-- @return A keyboard layout widget. +function keyboardlayout.new() + local widget = textbox() + local self = widget_base.make_widget(widget) + + self.widget = widget + + self.layout_name = function(v) + local name = v.file + if v.section ~= nil then + name = name .. "(" .. v.section .. ")" + end + return name + end + + self.next_layout = function() + self.set_layout((self._current + 1) % (#self._layout + 1)) + end + + self.set_layout = function(group_number) + if (0 > group_number) or (group_number > #self._layout) then + error("Invalid group number: " .. group_number .. + "expected number from 0 to " .. #self._layout) + return; + end + awesome.xkb_set_layout_group(group_number); + end + + update_layout(self); + + -- callback for processing layout changes + capi.awesome.connect_signal("xkb::map_changed", + function () update_layout(self) end) + capi.awesome.connect_signal("xkb::group_changed", + function () update_status(self) end); + + -- Mouse bindings + self:buttons( + util.table.join(button({ }, 1, self.next_layout)) + ) + + return self +end + +local _instance = nil; + +function keyboardlayout.mt:__call(...) + if _instance == nil then + _instance = keyboardlayout.new(...) + end + return _instance +end + +return setmetatable(keyboardlayout, keyboardlayout.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/launcher.lua b/lib/awful/widget/launcher.lua new file mode 100644 index 0000000..a944908 --- /dev/null +++ b/lib/awful/widget/launcher.lua @@ -0,0 +1,41 @@ +--------------------------------------------------------------------------- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008-2009 Julien Danjou +-- @classmod awful.widget.launcher +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local util = require("awful.util") +local spawn = require("awful.spawn") +local wbutton = require("awful.widget.button") +local button = require("awful.button") + +local launcher = { mt = {} } + +--- Create a button widget which will launch a command. +-- @param args Standard widget table arguments, plus image for the image path +-- and command for the command to run on click, or either menu to create menu. +-- @return A launcher widget. +function launcher.new(args) + if not args.command and not args.menu then return end + local w = wbutton(args) + if not w then return end + + local b + if args.command then + b = util.table.join(w:buttons(), button({}, 1, nil, function () spawn(args.command) end)) + elseif args.menu then + b = util.table.join(w:buttons(), button({}, 1, nil, function () args.menu:toggle() end)) + end + + w:buttons(b) + return w +end + +function launcher.mt:__call(...) + return launcher.new(...) +end + +return setmetatable(launcher, launcher.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/layoutbox.lua b/lib/awful/widget/layoutbox.lua new file mode 100644 index 0000000..c5f8ed3 --- /dev/null +++ b/lib/awful/widget/layoutbox.lua @@ -0,0 +1,73 @@ +--------------------------------------------------------------------------- +--- Layoutbox widget. +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Julien Danjou +-- @classmod awful.widget.layoutbox +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local capi = { screen = screen, tag = tag } +local layout = require("awful.layout") +local tooltip = require("awful.tooltip") +local beautiful = require("beautiful") +local imagebox = require("wibox.widget.imagebox") + +local function get_screen(s) + return s and capi.screen[s] +end + +local layoutbox = { mt = {} } + +local boxes = nil + +local function update(w, screen) + screen = get_screen(screen) + local name = layout.getname(layout.get(screen)) + w._layoutbox_tooltip:set_text(name or "[no name]") + w:set_image(name and beautiful["layout_" .. name]) +end + +local function update_from_tag(t) + local screen = get_screen(t.screen) + local w = boxes[screen] + if w then + update(w, screen) + end +end + +--- Create a layoutbox widget. It draws a picture with the current layout +-- symbol of the current tag. +-- @param screen The screen number that the layout will be represented for. +-- @return An imagebox widget configured as a layoutbox. +function layoutbox.new(screen) + screen = get_screen(screen or 1) + + -- Do we already have the update callbacks registered? + if boxes == nil then + boxes = setmetatable({}, { __mode = "kv" }) + capi.tag.connect_signal("property::selected", update_from_tag) + capi.tag.connect_signal("property::layout", update_from_tag) + layoutbox.boxes = boxes + end + + -- Do we already have a layoutbox for this screen? + local w = boxes[screen] + if not w then + w = imagebox() + w._layoutbox_tooltip = tooltip {objects = {w}, delay_show = 1} + + update(w, screen) + boxes[screen] = w + end + + return w +end + +function layoutbox.mt:__call(...) + return layoutbox.new(...) +end + +return setmetatable(layoutbox, layoutbox.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/progressbar.lua b/lib/awful/widget/progressbar.lua new file mode 100644 index 0000000..11147f4 --- /dev/null +++ b/lib/awful/widget/progressbar.lua @@ -0,0 +1,16 @@ +--------------------------------------------------------------------------- +--- This module has been moved to `wibox.widget.progressbar` +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Julien Danjou +-- @classmod awful.widget.progressbar +--------------------------------------------------------------------------- +local util = require("awful.util") + +return util.deprecate_class( + require("wibox.widget.progressbar"), + "awful.widget.progressbar", + "wibox.widget.progressbar" +) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/prompt.lua b/lib/awful/widget/prompt.lua new file mode 100644 index 0000000..ff9d904 --- /dev/null +++ b/lib/awful/widget/prompt.lua @@ -0,0 +1,64 @@ +--------------------------------------------------------------------------- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Julien Danjou +-- @classmod awful.widget.prompt +--------------------------------------------------------------------------- + +local setmetatable = setmetatable + +local completion = require("awful.completion") +local util = require("awful.util") +local spawn = require("awful.spawn") +local prompt = require("awful.prompt") +local widget_base = require("wibox.widget.base") +local textbox = require("wibox.widget.textbox") +local type = type + +local widgetprompt = { mt = {} } + +--- Run method for promptbox. +-- +-- @param promptbox The promptbox to run. +local function run(promptbox) + return prompt.run { + prompt = promptbox.prompt, + textbox = promptbox.widget, + completion_callback = completion.shell, + history_path = util.get_cache_dir() .. "/history", + exe_callback = function (...) + promptbox:spawn_and_handle_error(...) + end, + } +end + +local function spawn_and_handle_error(self, ...) + local result = spawn(...) + if type(result) == "string" then + self.widget:set_text(result) + end +end + +--- Create a prompt widget which will launch a command. +-- +-- @param args Arguments table. "prompt" is the prompt to use. +-- @return A launcher widget. +function widgetprompt.new(args) + args = args or {} + local widget = textbox() + local promptbox = widget_base.make_widget(widget) + + promptbox.widget = widget + promptbox.widget:set_ellipsize("start") + promptbox.run = run + promptbox.spawn_and_handle_error = spawn_and_handle_error + promptbox.prompt = args.prompt or "Run: " + return promptbox +end + +function widgetprompt.mt:__call(...) + return widgetprompt.new(...) +end + +return setmetatable(widgetprompt, widgetprompt.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/taglist.lua b/lib/awful/widget/taglist.lua new file mode 100644 index 0000000..d8cf475 --- /dev/null +++ b/lib/awful/widget/taglist.lua @@ -0,0 +1,452 @@ +--------------------------------------------------------------------------- +--- Taglist widget module for awful +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008-2009 Julien Danjou +-- @classmod awful.widget.taglist +--------------------------------------------------------------------------- + +-- Grab environment we need +local capi = { screen = screen, + awesome = awesome, + client = client } +local setmetatable = setmetatable +local pairs = pairs +local ipairs = ipairs +local table = table +local common = require("awful.widget.common") +local util = require("awful.util") +local tag = require("awful.tag") +local beautiful = require("beautiful") +local fixed = require("wibox.layout.fixed") +local surface = require("gears.surface") +local timer = require("gears.timer") + +local function get_screen(s) + return s and capi.screen[s] +end + +local taglist = { mt = {} } +taglist.filter = {} + +--- The tag list main foreground (text) color. +-- @beautiful beautiful.taglist_fg_focus +-- @param[opt=fg_focus] color +-- @see gears.color + +--- The tag list main background color. +-- @beautiful beautiful.taglist_bg_focus +-- @param[opt=bg_focus] color +-- @see gears.color + +--- The tag list urgent elements foreground (text) color. +-- @beautiful beautiful.taglist_fg_urgent +-- @param[opt=fg_urgent] color +-- @see gears.color + +--- The tag list urgent elements background color. +-- @beautiful beautiful.taglist_bg_urgent +-- @param[opt=bg_urgent] color +-- @see gears.color + +--- The tag list occupied elements background color. +-- @beautiful beautiful.taglist_bg_occupied +-- @param color +-- @see gears.color + +--- The tag list occupied elements foreground (text) color. +-- @beautiful beautiful.taglist_fg_occupied +-- @param color +-- @see gears.color + +--- The tag list empty elements background color. +-- @beautiful beautiful.taglist_bg_empty +-- @param color +-- @see gears.color + +--- The tag list empty elements foreground (text) color. +-- @beautiful beautiful.taglist_fg_empty +-- @param color +-- @see gears.color + +--- The selected elements background image. +-- @beautiful beautiful.taglist_squares_sel +-- @param surface +-- @see gears.surface + +--- The unselected elements background image. +-- @beautiful beautiful.taglist_squares_unsel +-- @param surface +-- @see gears.surface + +--- The selected empty elements background image. +-- @beautiful beautiful.taglist_squares_sel_empty +-- @param surface +-- @see gears.surface + +--- The unselected empty elements background image. +-- @beautiful beautiful.taglist_squares_unsel_empty +-- @param surface +-- @see gears.surface + +--- If the background images can be resized. +-- @beautiful beautiful.taglist_squares_resize +-- @param boolean + +--- Do not display the tag icons, even if they are set. +-- @beautiful beautiful.taglist_disable_icon +-- @param boolean + +--- The taglist font. +-- @beautiful beautiful.taglist_font +-- @param string + +--- The main shape used for the elements. +-- This will be the fallback for state specific shapes. +-- To get a shape for the whole taglist, use `wibox.container.background`. +-- @beautiful beautiful.taglist_shape +-- @param[opt=rectangle] gears.shape +-- @see gears.shape +-- @see beautiful.taglist_shape_empty +-- @see beautiful.taglist_shape_focus +-- @see beautiful.taglist_shape_urgent + +--- The shape elements border width. +-- @beautiful beautiful.taglist_shape_border_width +-- @param[opt=0] number +-- @see wibox.container.background + +--- The elements shape border color. +-- @beautiful beautiful.taglist_shape_border_color +-- @param color +-- @see gears.color + +--- The shape used for the empty elements. +-- @beautiful beautiful.taglist_shape_empty +-- @param[opt=rectangle] gears.shape +-- @see gears.shape + +--- The shape used for the empty elements border width. +-- @beautiful beautiful.taglist_shape_border_width_empty +-- @param[opt=0] number +-- @see wibox.container.background + +--- The empty elements shape border color. +-- @beautiful beautiful.taglist_shape_border_color_empty +-- @param color +-- @see gears.color + +--- The shape used for the selected elements. +-- @beautiful beautiful.taglist_shape_focus +-- @param[opt=rectangle] gears.shape +-- @see gears.shape + +--- The shape used for the selected elements border width. +-- @beautiful beautiful.taglist_shape_border_width_focus +-- @param[opt=0] number +-- @see wibox.container.background + +--- The selected elements shape border color. +-- @beautiful beautiful.taglist_shape_border_color_focus +-- @param color +-- @see gears.color + +--- The shape used for the urgent elements. +-- @beautiful beautiful.taglist_shape_urgent +-- @param[opt=rectangle] gears.shape +-- @see gears.shape + +--- The shape used for the urgent elements border width. +-- @beautiful beautiful.taglist_shape_border_width_urgent +-- @param[opt=0] number +-- @see wibox.container.background + +--- The urgents elements shape border color. +-- @beautiful beautiful.taglist_shape_border_color_urgent +-- @param color +-- @see gears.color + +local instances = nil + +function taglist.taglist_label(t, args) + if not args then args = {} end + local theme = beautiful.get() + local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus + local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus + local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent + local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent + local bg_occupied = args.bg_occupied or theme.taglist_bg_occupied + local fg_occupied = args.fg_occupied or theme.taglist_fg_occupied + local bg_empty = args.bg_empty or theme.taglist_bg_empty + local fg_empty = args.fg_empty or theme.taglist_fg_empty + local taglist_squares_sel = args.squares_sel or theme.taglist_squares_sel + local taglist_squares_unsel = args.squares_unsel or theme.taglist_squares_unsel + local taglist_squares_sel_empty = args.squares_sel_empty or theme.taglist_squares_sel_empty + local taglist_squares_unsel_empty = args.squares_unsel_empty or theme.taglist_squares_unsel_empty + local taglist_squares_resize = theme.taglist_squares_resize or args.squares_resize or "true" + local taglist_disable_icon = args.taglist_disable_icon or theme.taglist_disable_icon or false + local font = args.font or theme.taglist_font or theme.font or "" + local text = nil + local sel = capi.client.focus + local bg_color = nil + local fg_color = nil + local bg_image + local icon + local shape = args.shape or theme.taglist_shape + local shape_border_width = args.shape_border_width or theme.taglist_shape_border_width + local shape_border_color = args.shape_border_color or theme.taglist_shape_border_color + -- TODO: Re-implement bg_resize + local bg_resize = false -- luacheck: ignore + local is_selected = false + local cls = t:clients() + + if sel and taglist_squares_sel then + -- Check that the selected client is tagged with 't'. + local seltags = sel:tags() + for _, v in ipairs(seltags) do + if v == t then + bg_image = taglist_squares_sel + bg_resize = taglist_squares_resize == "true" + is_selected = true + break + end + end + end + if #cls == 0 and t.selected and taglist_squares_sel_empty then + bg_image = taglist_squares_sel_empty + bg_resize = taglist_squares_resize == "true" + elseif not is_selected then + if #cls > 0 then + if taglist_squares_unsel then + bg_image = taglist_squares_unsel + bg_resize = taglist_squares_resize == "true" + end + if bg_occupied then bg_color = bg_occupied end + if fg_occupied then fg_color = fg_occupied end + else + if taglist_squares_unsel_empty then + bg_image = taglist_squares_unsel_empty + bg_resize = taglist_squares_resize == "true" + end + if bg_empty then bg_color = bg_empty end + if fg_empty then fg_color = fg_empty end + + if args.shape_empty or theme.taglist_shape_empty then + shape = args.shape_empty or theme.taglist_shape_empty + end + + if args.shape_border_width_empty or theme.taglist_shape_border_width_empty then + shape_border_width = args.shape_border_width_empty or theme.taglist_shape_border_width_empty + end + + if args.shape_border_color_empty or theme.taglist_shape_border_color_empty then + shape_border_color = args.shape_border_color_empty or theme.taglist_shape_border_color_empty + end + end + end + if t.selected then + bg_color = bg_focus + fg_color = fg_focus + + if args.shape_focus or theme.taglist_shape_focus then + shape = args.shape_focus or theme.taglist_shape_focus + end + + if args.shape_border_width_focus or theme.taglist_shape_border_width_focus then + shape = args.shape_border_width_focus or theme.taglist_shape_border_width_focus + end + + if args.shape_border_color_focus or theme.taglist_shape_border_color_focus then + shape = args.shape_border_color_focus or theme.taglist_shape_border_color_focus + end + + elseif tag.getproperty(t, "urgent") then + if bg_urgent then bg_color = bg_urgent end + if fg_urgent then fg_color = fg_urgent end + + if args.shape_urgent or theme.taglist_shape_urgent then + shape = args.shape_urgent or theme.taglist_shape_urgent + end + + if args.shape_border_width_urgent or theme.taglist_shape_border_width_urgent then + shape_border_width = args.shape_border_width_urgent or theme.taglist_shape_border_width_urgent + end + + if args.shape_border_color_urgent or theme.taglist_shape_border_color_urgent then + shape_border_color = args.shape_border_color_urgent or theme.taglist_shape_border_color_urgent + end + end + + if not tag.getproperty(t, "icon_only") then + text = "<span font_desc='"..font.."'>" + if fg_color then + text = text .. "<span color='" .. util.ensure_pango_color(fg_color) .. + "'>" .. (util.escape(t.name) or "") .. "</span>" + else + text = text .. (util.escape(t.name) or "") + end + text = text .. "</span>" + end + if not taglist_disable_icon then + if t.icon then + icon = surface.load(t.icon) + end + end + + local other_args = { + shape = shape, + shape_border_width = shape_border_width, + shape_border_color = shape_border_color, + } + + return text, bg_color, bg_image, not taglist_disable_icon and icon or nil, other_args +end + +local function taglist_update(s, w, buttons, filter, data, style, update_function) + local tags = {} + for _, t in ipairs(s.tags) do + if not tag.getproperty(t, "hide") and filter(t) then + table.insert(tags, t) + end + end + + local function label(c) return taglist.taglist_label(c, style) end + + update_function(w, buttons, label, data, tags) +end + +--- Create a new taglist widget. The last two arguments (update_function +-- and base_widget) serve to customize the layout of the taglist (eg. to +-- make it vertical). For that, you will need to copy the +-- awful.widget.common.list_update function, make your changes to it +-- and pass it as update_function here. Also change the base_widget if the +-- default is not what you want. +-- @param screen The screen to draw taglist for. +-- @param filter Filter function to define what clients will be listed. +-- @param buttons A table with buttons binding to set. +-- @tparam[opt={}] table style The style overrides default theme. +-- @tparam[opt=nil] string|pattern style.fg_focus +-- @tparam[opt=nil] string|pattern style.bg_focus +-- @tparam[opt=nil] string|pattern style.fg_urgent +-- @tparam[opt=nil] string|pattern style.bg_urgent +-- @tparam[opt=nil] string|pattern style.bg_occupied +-- @tparam[opt=nil] string|pattern style.fg_occupied +-- @tparam[opt=nil] string|pattern style.bg_empty +-- @tparam[opt=nil] string|pattern style.fg_empty +-- @tparam[opt=nil] string style.taglist_squares_sel +-- @tparam[opt=nil] string style.taglist_squares_unsel +-- @tparam[opt=nil] string style.taglist_squares_sel_empty +-- @tparam[opt=nil] string style.taglist_squares_unsel_empty +-- @tparam[opt=nil] string style.taglist_squares_resize +-- @tparam[opt=nil] string style.taglist_disable_icon +-- @tparam[opt=nil] string style.font +-- @tparam[opt=nil] number style.spacing The spacing between tags. +-- @param[opt] update_function Function to create a tag widget on each +-- update. See `awful.widget.common`. +-- @param[opt] base_widget Optional container widget for tag widgets. Default +-- is wibox.layout.fixed.horizontal(). +-- @param base_widget.bg_focus The background color for focused client. +-- @param base_widget.fg_focus The foreground color for focused client. +-- @param base_widget.bg_urgent The background color for urgent clients. +-- @param base_widget.fg_urgent The foreground color for urgent clients. +-- @param[opt] base_widget.squares_sel A user provided image for selected squares. +-- @param[opt] base_widget.squares_unsel A user provided image for unselected squares. +-- @param[opt] base_widget.squares_sel_empty A user provided image for selected squares for empty tags. +-- @param[opt] base_widget.squares_unsel_empty A user provided image for unselected squares for empty tags. +-- @param[opt] base_widget.squares_resize True or false to resize squares. +-- @param base_widget.font The font. +-- @function awful.taglist +function taglist.new(screen, filter, buttons, style, update_function, base_widget) + screen = get_screen(screen) + local uf = update_function or common.list_update + local w = base_widget or fixed.horizontal() + + if w.set_spacing and (style and style.spacing or beautiful.taglist_spacing) then + w:set_spacing(style and style.spacing or beautiful.taglist_spacing) + end + + local data = setmetatable({}, { __mode = 'k' }) + + local queued_update = {} + function w._do_taglist_update() + -- Add a delayed callback for the first update. + if not queued_update[screen] then + timer.delayed_call(function() + if screen.valid then + taglist_update(screen, w, buttons, filter, data, style, uf) + end + queued_update[screen] = false + end) + queued_update[screen] = true + end + end + if instances == nil then + instances = setmetatable({}, { __mode = "k" }) + local function u(s) + local i = instances[get_screen(s)] + if i then + for _, tlist in pairs(i) do + tlist._do_taglist_update() + end + end + end + local uc = function (c) return u(c.screen) end + local ut = function (t) return u(t.screen) end + capi.client.connect_signal("focus", uc) + capi.client.connect_signal("unfocus", uc) + tag.attached_connect_signal(nil, "property::selected", ut) + tag.attached_connect_signal(nil, "property::icon", ut) + tag.attached_connect_signal(nil, "property::hide", ut) + tag.attached_connect_signal(nil, "property::name", ut) + tag.attached_connect_signal(nil, "property::activated", ut) + tag.attached_connect_signal(nil, "property::screen", ut) + tag.attached_connect_signal(nil, "property::index", ut) + tag.attached_connect_signal(nil, "property::urgent", ut) + capi.client.connect_signal("property::screen", function(c, old_screen) + u(c.screen) + u(old_screen) + end) + capi.client.connect_signal("tagged", uc) + capi.client.connect_signal("untagged", uc) + capi.client.connect_signal("unmanage", uc) + capi.screen.connect_signal("removed", function(s) + instances[get_screen(s)] = nil + end) + end + w._do_taglist_update() + local list = instances[screen] + if not list then + list = setmetatable({}, { __mode = "v" }) + instances[screen] = list + end + table.insert(list, w) + return w +end + +--- Filtering function to include all nonempty tags on the screen. +-- @param t The tag. +-- @return true if t is not empty, else false +function taglist.filter.noempty(t) + return #t:clients() > 0 or t.selected +end + +--- Filtering function to include selected tags on the screen. +-- @param t The tag. +-- @return true if t is not empty, else false +function taglist.filter.selected(t) + return t.selected +end + +--- Filtering function to include all tags on the screen. +-- @return true +function taglist.filter.all() + return true +end + +function taglist.mt:__call(...) + return taglist.new(...) +end + +return setmetatable(taglist, taglist.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/tasklist.lua b/lib/awful/widget/tasklist.lua new file mode 100644 index 0000000..d5580c1 --- /dev/null +++ b/lib/awful/widget/tasklist.lua @@ -0,0 +1,573 @@ +--------------------------------------------------------------------------- +--- Tasklist widget module for awful. +-- +-- <a name="status_icons"></a> +-- **Status icons:** +-- +-- By default, the tasklist prepends some symbols in front of the client name. +-- This is used to notify that the client has some specific properties that are +-- currently enabled. This can be disabled using +-- `beautiful.tasklist_plain_task_name`=true in the theme. +-- +-- <table class='widget_list' border=1> +-- <tr style='font-weight: bold;'> +-- <th align='center'>Icon</th> +-- <th align='center'>Client property</th> +-- </tr> +-- <tr><td>▪</td><td><a href="./client.html#client.sticky">sticky</a></td></tr> +-- <tr><td>⌃</td><td><a href="./client.html#client.ontop">ontop</a></td></tr> +-- <tr><td>▴</td><td><a href="./client.html#client.above">above</a></td></tr> +-- <tr><td>▾</td><td><a href="./client.html#client.below">below</a></td></tr> +-- <tr><td>✈</td><td><a href="./client.html#client.floating">floating</a></td></tr> +-- <tr><td>+</td><td><a href="./client.html#client.maximized">maximized</a></td></tr> +-- <tr><td>⬌</td><td><a href="./client.html#client.maximized_horizontal">maximized_horizontal</a></td></tr> +-- <tr><td>⬍</td><td><a href="./client.html#client.maximized_vertical">maximized_vertical</a></td></tr> +-- </table> +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008-2009 Julien Danjou +-- @classmod awful.widget.tasklist +--------------------------------------------------------------------------- + +-- Grab environment we need +local capi = { screen = screen, + client = client } +local ipairs = ipairs +local setmetatable = setmetatable +local table = table +local common = require("awful.widget.common") +local beautiful = require("beautiful") +local util = require("awful.util") +local tag = require("awful.tag") +local flex = require("wibox.layout.flex") +local timer = require("gears.timer") + +local function get_screen(s) + return s and screen[s] +end + +local tasklist = { mt = {} } + +local instances + +--- The default foreground (text) color. +-- @beautiful beautiful.tasklist_fg_normal +-- @tparam[opt=nil] string|pattern fg_normal +-- @see gears.color + +--- The default background color. +-- @beautiful beautiful.tasklist_bg_normal +-- @tparam[opt=nil] string|pattern bg_normal +-- @see gears.color + +--- The focused client foreground (text) color. +-- @beautiful beautiful.tasklist_fg_focus +-- @tparam[opt=nil] string|pattern fg_focus +-- @see gears.color + +--- The focused client background color. +-- @beautiful beautiful.tasklist_bg_focus +-- @tparam[opt=nil] string|pattern bg_focus +-- @see gears.color + +--- The urgent clients foreground (text) color. +-- @beautiful beautiful.tasklist_fg_urgent +-- @tparam[opt=nil] string|pattern fg_urgent +-- @see gears.color + +--- The urgent clients background color. +-- @beautiful beautiful.tasklist_bg_urgent +-- @tparam[opt=nil] string|pattern bg_urgent +-- @see gears.color + +--- The minimized clients foreground (text) color. +-- @beautiful beautiful.tasklist_fg_minimize +-- @tparam[opt=nil] string|pattern fg_minimize +-- @see gears.color + +--- The minimized clients background color. +-- @beautiful beautiful.tasklist_bg_minimize +-- @tparam[opt=nil] string|pattern bg_minimize +-- @see gears.color + +--- The elements default background image. +-- @beautiful beautiful.tasklist_bg_image_normal +-- @tparam[opt=nil] string bg_image_normal + +--- The focused client background image. +-- @beautiful beautiful.tasklist_bg_image_focus +-- @tparam[opt=nil] string bg_image_focus + +--- The urgent clients background image. +-- @beautiful beautiful.tasklist_bg_image_urgent +-- @tparam[opt=nil] string bg_image_urgent + +--- The minimized clients background image. +-- @beautiful beautiful.tasklist_bg_image_minimize +-- @tparam[opt=nil] string bg_image_minimize + +--- Disable the tasklist client icons. +-- @beautiful beautiful.tasklist_tasklist_disable_icon +-- @tparam[opt=false] boolean tasklist_disable_icon + +--- Disable the extra tasklist client property notification icons. +-- +-- See the <a href="status_icons">Status icons</a> section for more details. +-- +-- @beautiful beautiful.tasklist_plain_task_name +-- @tparam[opt=false] boolean tasklist_plain_task_name + +--- The tasklist font. +-- @beautiful beautiful.tasklist_font +-- @tparam[opt=nil] string font + +--- The focused client alignment. +-- @beautiful beautiful.tasklist_align +-- @tparam[opt=left] string align *left*, *right* or *center* + +--- The focused client title alignment. +-- @beautiful beautiful.tasklist_font_focus +-- @tparam[opt=nil] string font_focus + +--- The minimized clients font. +-- @beautiful beautiful.tasklist_font_minimized +-- @tparam[opt=nil] string font_minimized + +--- The urgent clients font. +-- @beautiful beautiful.tasklist_font_urgent +-- @tparam[opt=nil] string font_urgent + +--- The space between the tasklist elements. +-- @beautiful beautiful.tasklist_spacing +-- @tparam[opt=0] number spacing The spacing between tags. + +--- The default tasklist elements shape. +-- @beautiful beautiful.tasklist_shape +-- @tparam[opt=nil] gears.shape shape + +--- The default tasklist elements border width. +-- @beautiful beautiful.tasklist_shape_border_width +-- @tparam[opt=0] number shape_border_width + +--- The default tasklist elements border color. +-- @beautiful beautiful.tasklist_shape_border_color +-- @tparam[opt=nil] string|color shape_border_color +-- @see gears.color + +--- The focused client shape. +-- @beautiful beautiful.tasklist_shape_focus +-- @tparam[opt=nil] gears.shape shape_focus + +--- The focused client border width. +-- @beautiful beautiful.tasklist_shape_border_width_focus +-- @tparam[opt=0] number shape_border_width_focus + +--- The focused client border color. +-- @beautiful beautiful.tasklist_shape_border_color_focus +-- @tparam[opt=nil] string|color shape_border_color_focus +-- @see gears.color + +--- The minimized clients shape. +-- @beautiful beautiful.tasklist_shape_minimized +-- @tparam[opt=nil] gears.shape shape_minimized + +--- The minimized clients border width. +-- @beautiful beautiful.tasklist_shape_border_width_minimized +-- @tparam[opt=0] number shape_border_width_minimized + +--- The minimized clients border color. +-- @beautiful beautiful.tasklist_shape_border_color_minimized +-- @tparam[opt=nil] string|color shape_border_color_minimized +-- @see gears.color + +--- The urgent clients shape. +-- @beautiful beautiful.tasklist_shape_urgent +-- @tparam[opt=nil] gears.shape shape_urgent + +--- The urgent clients border width. +-- @beautiful beautiful.tasklist_shape_border_width_urgent +-- @tparam[opt=0] number shape_border_width_urgent + +--- The urgent clients border color. +-- @beautiful beautiful.tasklist_shape_border_color_urgent +-- @tparam[opt=nil] string|color shape_border_color_urgent +-- @see gears.color + +-- Public structures +tasklist.filter = {} + +local function tasklist_label(c, args, tb) + if not args then args = {} end + local theme = beautiful.get() + local align = args.align or theme.tasklist_align or "left" + local fg_normal = util.ensure_pango_color(args.fg_normal or theme.tasklist_fg_normal or theme.fg_normal, "white") + local bg_normal = args.bg_normal or theme.tasklist_bg_normal or theme.bg_normal or "#000000" + local fg_focus = util.ensure_pango_color(args.fg_focus or theme.tasklist_fg_focus or theme.fg_focus, fg_normal) + local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus or bg_normal + local fg_urgent = util.ensure_pango_color(args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent, fg_normal) + local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent or bg_normal + local fg_minimize = util.ensure_pango_color(args.fg_minimize or theme.tasklist_fg_minimize or theme.fg_minimize, fg_normal) + local bg_minimize = args.bg_minimize or theme.tasklist_bg_minimize or theme.bg_minimize or bg_normal + local bg_image_normal = args.bg_image_normal or theme.bg_image_normal + local bg_image_focus = args.bg_image_focus or theme.bg_image_focus + local bg_image_urgent = args.bg_image_urgent or theme.bg_image_urgent + local bg_image_minimize = args.bg_image_minimize or theme.bg_image_minimize + local tasklist_disable_icon = args.tasklist_disable_icon or theme.tasklist_disable_icon or false + local font = args.font or theme.tasklist_font or theme.font or "" + local font_focus = args.font_focus or theme.tasklist_font_focus or theme.font_focus or font or "" + local font_minimized = args.font_minimized or theme.tasklist_font_minimized or theme.font_minimized or font or "" + local font_urgent = args.font_urgent or theme.tasklist_font_urgent or theme.font_urgent or font or "" + local text = "" + local name = "" + local bg + local bg_image + local shape = args.shape or theme.tasklist_shape + local shape_border_width = args.shape_border_width or theme.tasklist_shape_border_width + local shape_border_color = args.shape_border_color or theme.tasklist_shape_border_color + + -- symbol to use to indicate certain client properties + local sticky = args.sticky or theme.tasklist_sticky or "▪" + local ontop = args.ontop or theme.tasklist_ontop or '⌃' + local above = args.above or theme.tasklist_above or '▴' + local below = args.below or theme.tasklist_below or '▾' + local floating = args.floating or theme.tasklist_floating or '✈' + local maximized = args.maximized or theme.tasklist_maximized or '<b>+</b>' + local maximized_horizontal = args.maximized_horizontal or theme.tasklist_maximized_horizontal or '⬌' + local maximized_vertical = args.maximized_vertical or theme.tasklist_maximized_vertical or '⬍' + + tb:set_align(align) + + if not theme.tasklist_plain_task_name then + if c.sticky then name = name .. sticky end + + if c.ontop then name = name .. ontop + elseif c.above then name = name .. above + elseif c.below then name = name .. below end + + if c.maximized then + name = name .. maximized + else + if c.maximized_horizontal then name = name .. maximized_horizontal end + if c.maximized_vertical then name = name .. maximized_vertical end + if c.floating then name = name .. floating end + end + end + + if c.minimized then + name = name .. (util.escape(c.icon_name) or util.escape(c.name) or util.escape("<untitled>")) + else + name = name .. (util.escape(c.name) or util.escape("<untitled>")) + end + + local focused = capi.client.focus == c + -- Handle transient_for: the first parent that does not skip the taskbar + -- is considered to be focused, if the real client has skip_taskbar. + if not focused and capi.client.focus and capi.client.focus.skip_taskbar + and capi.client.focus:get_transient_for_matching(function(cl) + return not cl.skip_taskbar + end) == c then + focused = true + end + + if focused then + bg = bg_focus + text = text .. "<span color='"..fg_focus.."'>"..name.."</span>" + bg_image = bg_image_focus + font = font_focus + + if args.shape_focus or theme.tasklist_shape_focus then + shape = args.shape_focus or theme.tasklist_shape_focus + end + + if args.shape_border_width_focus or theme.tasklist_shape_border_width_focus then + shape_border_width = args.shape_border_width_focus or theme.tasklist_shape_border_width_focus + end + + if args.shape_border_color_focus or theme.tasklist_shape_border_color_focus then + shape_border_color = args.shape_border_color_focus or theme.tasklist_shape_border_color_focus + end + elseif c.urgent then + bg = bg_urgent + text = text .. "<span color='"..fg_urgent.."'>"..name.."</span>" + bg_image = bg_image_urgent + font = font_urgent + + if args.shape_urgent or theme.tasklist_shape_urgent then + shape = args.shape_urgent or theme.tasklist_shape_urgent + end + + if args.shape_border_width_urgent or theme.tasklist_shape_border_width_urgent then + shape_border_width = args.shape_border_width_urgent or theme.tasklist_shape_border_width_urgent + end + + if args.shape_border_color_urgent or theme.tasklist_shape_border_color_urgent then + shape_border_color = args.shape_border_color_urgent or theme.tasklist_shape_border_color_urgent + end + elseif c.minimized then + bg = bg_minimize + text = text .. "<span color='"..fg_minimize.."'>"..name.."</span>" + bg_image = bg_image_minimize + font = font_minimized + + if args.shape_minimized or theme.tasklist_shape_minimized then + shape = args.shape_minimized or theme.tasklist_shape_minimized + end + + if args.shape_border_width_minimized or theme.tasklist_shape_border_width_minimized then + shape_border_width = args.shape_border_width_minimized or theme.tasklist_shape_border_width_minimized + end + + if args.shape_border_color_minimized or theme.tasklist_shape_border_color_minimized then + shape_border_color = args.shape_border_color_minimized or theme.tasklist_shape_border_color_minimized + end + else + bg = bg_normal + text = text .. "<span color='"..fg_normal.."'>"..name.."</span>" + bg_image = bg_image_normal + end + tb:set_font(font) + + local other_args = { + shape = shape, + shape_border_width = shape_border_width, + shape_border_color = shape_border_color, + } + + return text, bg, bg_image, not tasklist_disable_icon and c.icon or nil, other_args +end + +local function tasklist_update(s, w, buttons, filter, data, style, update_function) + local clients = {} + for _, c in ipairs(capi.client.get()) do + if not (c.skip_taskbar or c.hidden + or c.type == "splash" or c.type == "dock" or c.type == "desktop") + and filter(c, s) then + table.insert(clients, c) + end + end + + local function label(c, tb) return tasklist_label(c, style, tb) end + + update_function(w, buttons, label, data, clients) +end + +--- Create a new tasklist widget. The last two arguments (update_function +-- and base_widget) serve to customize the layout of the tasklist (eg. to +-- make it vertical). For that, you will need to copy the +-- awful.widget.common.list_update function, make your changes to it +-- and pass it as update_function here. Also change the base_widget if the +-- default is not what you want. +-- @param screen The screen to draw tasklist for. +-- @param filter Filter function to define what clients will be listed. +-- @param buttons A table with buttons binding to set. +-- @tparam[opt={}] table style The style overrides default theme. +-- @tparam[opt=nil] string|pattern style.fg_normal +-- @tparam[opt=nil] string|pattern style.bg_normal +-- @tparam[opt=nil] string|pattern style.fg_focus +-- @tparam[opt=nil] string|pattern style.bg_focus +-- @tparam[opt=nil] string|pattern style.fg_urgent +-- @tparam[opt=nil] string|pattern style.bg_urgent +-- @tparam[opt=nil] string|pattern style.fg_minimize +-- @tparam[opt=nil] string|pattern style.bg_minimize +-- @tparam[opt=nil] string style.bg_image_normal +-- @tparam[opt=nil] string style.bg_image_focus +-- @tparam[opt=nil] string style.bg_image_urgent +-- @tparam[opt=nil] string style.bg_image_minimize +-- @tparam[opt=nil] boolean style.tasklist_disable_icon +-- @tparam[opt=nil] string style.font +-- @tparam[opt=left] string style.align *left*, *right* or *center* +-- @tparam[opt=nil] string style.font_focus +-- @tparam[opt=nil] string style.font_minimized +-- @tparam[opt=nil] string style.font_urgent +-- @tparam[opt=nil] number style.spacing The spacing between tags. +-- @tparam[opt=nil] gears.shape style.shape +-- @tparam[opt=nil] number style.shape_border_width +-- @tparam[opt=nil] string|color style.shape_border_color +-- @tparam[opt=nil] gears.shape style.shape_focus +-- @tparam[opt=nil] number style.shape_border_width_focus +-- @tparam[opt=nil] string|color style.shape_border_color_focus +-- @tparam[opt=nil] gears.shape style.shape_minimized +-- @tparam[opt=nil] number style.shape_border_width_minimized +-- @tparam[opt=nil] string|color style.shape_border_color_minimized +-- @tparam[opt=nil] gears.shape style.shape_urgent +-- @tparam[opt=nil] number style.shape_border_width_urgent +-- @tparam[opt=nil] string|color style.shape_border_color_urgent +-- @param[opt] update_function Function to create a tag widget on each +-- update. See `awful.widget.common.list_update`. +-- @tparam[opt] table base_widget Container widget for tag widgets. Default +-- is `wibox.layout.flex.horizontal`. +-- @function awful.tasklist +function tasklist.new(screen, filter, buttons, style, update_function, base_widget) + screen = get_screen(screen) + local uf = update_function or common.list_update + local w = base_widget or flex.horizontal() + + local data = setmetatable({}, { __mode = 'k' }) + + if w.set_spacing and (style and style.spacing or beautiful.taglist_spacing) then + w:set_spacing(style and style.spacing or beautiful.taglist_spacing) + end + + local queued_update = false + function w._do_tasklist_update() + -- Add a delayed callback for the first update. + if not queued_update then + timer.delayed_call(function() + queued_update = false + if screen.valid then + tasklist_update(screen, w, buttons, filter, data, style, uf) + end + end) + queued_update = true + end + end + function w._unmanage(c) + data[c] = nil + end + if instances == nil then + instances = setmetatable({}, { __mode = "k" }) + local function us(s) + local i = instances[get_screen(s)] + if i then + for _, tlist in pairs(i) do + tlist._do_tasklist_update() + end + end + end + local function u() + for s in pairs(instances) do + if s.valid then + us(s) + end + end + end + + tag.attached_connect_signal(nil, "property::selected", u) + tag.attached_connect_signal(nil, "property::activated", u) + capi.client.connect_signal("property::urgent", u) + capi.client.connect_signal("property::sticky", u) + capi.client.connect_signal("property::ontop", u) + capi.client.connect_signal("property::above", u) + capi.client.connect_signal("property::below", u) + capi.client.connect_signal("property::floating", u) + capi.client.connect_signal("property::maximized_horizontal", u) + capi.client.connect_signal("property::maximized_vertical", u) + capi.client.connect_signal("property::minimized", u) + capi.client.connect_signal("property::name", u) + capi.client.connect_signal("property::icon_name", u) + capi.client.connect_signal("property::icon", u) + capi.client.connect_signal("property::skip_taskbar", u) + capi.client.connect_signal("property::screen", function(c, old_screen) + us(c.screen) + us(old_screen) + end) + capi.client.connect_signal("property::hidden", u) + capi.client.connect_signal("tagged", u) + capi.client.connect_signal("untagged", u) + capi.client.connect_signal("unmanage", function(c) + u(c) + for _, i in pairs(instances) do + for _, tlist in pairs(i) do + tlist._unmanage(c) + end + end + end) + capi.client.connect_signal("list", u) + capi.client.connect_signal("focus", u) + capi.client.connect_signal("unfocus", u) + capi.screen.connect_signal("removed", function(s) + instances[get_screen(s)] = nil + end) + end + w._do_tasklist_update() + local list = instances[screen] + if not list then + list = setmetatable({}, { __mode = "v" }) + instances[screen] = list + end + table.insert(list, w) + return w +end + +--- Filtering function to include all clients. +-- @return true +function tasklist.filter.allscreen() + return true +end + +--- Filtering function to include the clients from all tags on the screen. +-- @param c The client. +-- @param screen The screen we are drawing on. +-- @return true if c is on screen, false otherwise +function tasklist.filter.alltags(c, screen) + -- Only print client on the same screen as this widget + return get_screen(c.screen) == get_screen(screen) +end + +--- Filtering function to include only the clients from currently selected tags. +-- @param c The client. +-- @param screen The screen we are drawing on. +-- @return true if c is in a selected tag on screen, false otherwise +function tasklist.filter.currenttags(c, screen) + screen = get_screen(screen) + -- Only print client on the same screen as this widget + if get_screen(c.screen) ~= screen then return false end + -- Include sticky client too + if c.sticky then return true end + local tags = screen.tags + for _, t in ipairs(tags) do + if t.selected then + local ctags = c:tags() + for _, v in ipairs(ctags) do + if v == t then + return true + end + end + end + end + return false +end + +--- Filtering function to include only the minimized clients from currently selected tags. +-- @param c The client. +-- @param screen The screen we are drawing on. +-- @return true if c is in a selected tag on screen and is minimized, false otherwise +function tasklist.filter.minimizedcurrenttags(c, screen) + screen = get_screen(screen) + -- Only print client on the same screen as this widget + if get_screen(c.screen) ~= screen then return false end + -- Check client is minimized + if not c.minimized then return false end + -- Include sticky client + if c.sticky then return true end + local tags = screen.tags + for _, t in ipairs(tags) do + -- Select only minimized clients + if t.selected then + local ctags = c:tags() + for _, v in ipairs(ctags) do + if v == t then + return true + end + end + end + end + return false +end + +--- Filtering function to include only the currently focused client. +-- @param c The client. +-- @param screen The screen we are drawing on. +-- @return true if c is focused on screen, false otherwise +function tasklist.filter.focused(c, screen) + -- Only print client on the same screen as this widget + return get_screen(c.screen) == get_screen(screen) and capi.client.focus == c +end + +function tasklist.mt:__call(...) + return tasklist.new(...) +end + +return setmetatable(tasklist, tasklist.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/textclock.lua b/lib/awful/widget/textclock.lua new file mode 100644 index 0000000..002aa0e --- /dev/null +++ b/lib/awful/widget/textclock.lua @@ -0,0 +1,16 @@ +--------------------------------------------------------------------------- +-- This widget has moved to `wibox.widget.textclock` +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008-2009 Julien Danjou +-- @classmod awful.widget.textclock +--------------------------------------------------------------------------- +local util = require("awful.util") + +return util.deprecate_class( + require("wibox.widget.textclock"), + "awful.widget.textclock", + "wibox.widget.textclock" +) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/watch.lua b/lib/awful/widget/watch.lua new file mode 100644 index 0000000..bc4c9af --- /dev/null +++ b/lib/awful/widget/watch.lua @@ -0,0 +1,91 @@ +--------------------------------------------------------------------------- +--- Watch widget. +-- Here is an example of simple temperature widget which will update each 15 +-- seconds implemented in two different ways. +-- The first, simpler one, will just display the return command output +-- (so output is stripped by shell commands). +-- In the other example `sensors` returns to the widget its full output +-- and it's trimmed in the widget callback function: +-- +-- 211 mytextclock, +-- 212 wibox.widget.textbox(' | '), +-- 213 -- one way to do that: +-- 214 awful.widget.watch('bash -c "sensors | grep temp1"', 15), +-- 215 -- another way: +-- 216 awful.widget.watch('sensors', 15, function(widget, stdout) +-- 217 for line in stdout:gmatch("[^\r\n]+") do +-- 218 if line:match("temp1") then +-- 219 widget:set_text(line) +-- 220 return +-- 221 end +-- 222 end +-- 223 end), +-- 224 s.mylayoutbox, +-- +-- ![Example screenshot](../images/awful_widget_watch.png) +-- +-- @author Benjamin Petrenko +-- @author Yauheni Kirylau +-- @copyright 2015, 2016 Benjamin Petrenko, Yauheni Kirylau +-- @classmod awful.widget.watch +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local textbox = require("wibox.widget.textbox") +local timer = require("gears.timer") +local spawn = require("awful.spawn") + +local watch = { mt = {} } + +--- Create a textbox that shows the output of a command +-- and updates it at a given time interval. +-- +-- @tparam string|table command The command. +-- +-- @tparam[opt=5] integer timeout The time interval at which the textbox +-- will be updated. +-- +-- @tparam[opt] function callback The function that will be called after +-- the command output will be received. it is shown in the textbox. +-- Defaults to: +-- function(widget, stdout, stderr, exitreason, exitcode) +-- widget:set_text(stdout) +-- end +-- @param callback.widget Base widget instance. +-- @tparam string callback.stdout Output on stdout. +-- @tparam string callback.stderr Output on stderr. +-- @tparam string callback.exitreason Exit Reason. +-- The reason can be "exit" or "signal". +-- @tparam integer callback.exitcode Exit code. +-- For "exit" reason it's the exit code. +-- For "signal" reason — the signal causing process termination. +-- +-- @param[opt=wibox.widget.textbox()] base_widget Base widget. +-- +-- @return The widget used by this watch +function watch.new(command, timeout, callback, base_widget) + timeout = timeout or 5 + base_widget = base_widget or textbox() + callback = callback or function(widget, stdout, stderr, exitreason, exitcode) -- luacheck: no unused args + widget:set_text(stdout) + end + local t = timer { timeout = timeout } + t:connect_signal("timeout", function() + t:stop() + spawn.easy_async(command, function(stdout, stderr, exitreason, exitcode) + callback(base_widget, stdout, stderr, exitreason, exitcode) + t:again() + end) + end) + t:start() + t:emit_signal("timeout") + return base_widget +end + +function watch.mt.__call(_, ...) + return watch.new(...) +end + +return setmetatable(watch, watch.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 |