Merge pull request #923 from Elv13/widget_api_refactor

Widget api refactor
This commit is contained in:
Emmanuel Lepage Vallée 2016-05-30 23:22:40 -04:00
commit fc659b79ce
95 changed files with 3572 additions and 2285 deletions

View File

@ -84,14 +84,14 @@ install:
- travis_retry sudo luarocks install lua-discount
# Instal luacov-coveralls for code coverage testing.
- if [ "$DO_COVERAGE" = "true" ]; then sudo luarocks install luacov-coveralls; fi
- if [ "$DO_COVERAGE" = "true" ]; then sudo luarocks install luacov-coveralls; else export DO_COVERAGE=0; fi
# Determine custom version.
- export AWESOME_VERSION="${TRAVIS_BRANCH}-g$(git rev-parse --short HEAD)"
- 'if [ "$TRAVIS_PULL_REQUEST" != false ]; then AWESOME_VERSION="${AWESOME_VERSION}-PR${TRAVIS_PULL_REQUEST}"; fi'
script:
- export CMAKE_ARGS="-DLUA_LIBRARY=${LUALIBRARY} -DLUA_INCLUDE_DIR=${LUAINCLUDE} -D OVERRIDE_VERSION=$AWESOME_VERSION"
- export CMAKE_ARGS="-DLUA_LIBRARY=${LUALIBRARY} -DLUA_INCLUDE_DIR=${LUAINCLUDE} -D OVERRIDE_VERSION=$AWESOME_VERSION -D DO_COVERAGE=${DO_COVERAGE}"
- |
if [ -n "$BUILD_IN_DIR" ]; then
SOURCE_DIRECTORY="$PWD"

View File

@ -300,13 +300,13 @@ if(GENERATE_DOC)
# Load the common documentation
include(docs/load_ldoc.cmake)
# Generate some images and examples
include(docs/generate_examples.cmake)
# Use `include`, rather than `add_subdirectory`, to keep the variables
# The file is a valid CMakeLists.txt and can be executed directly if only
# the image artefacts are needed.
include(tests/examples/CMakeLists.txt)
# Generate the widget lists
include(docs/widget_lists.cmake)
endif()
# {{{ Configure files
@ -345,8 +345,18 @@ set(AWESOME_ADDITIONAL_FILES
foreach(file ${AWESOME_ADDITIONAL_FILES})
configure_file(${SOURCE_DIR}/${file}
${BUILD_DIR}/${file}
COPYONLY)
@ONLY)
endforeach()
#}}}
# The examples coverage need to be done again after the configure_file has
# inserted the additional code. Otherwise, the result will be off, rendering
# the coverage useless as a tool to track untested code.
if(GENERATE_DOC AND DO_COVERAGE)
message(STATUS "Running tests again with coverage")
set(USE_LCOV 1)
include(tests/examples/CMakeLists.txt)
endif()
# vim: filetype=cmake:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80:foldmethod=marker

View File

@ -115,7 +115,7 @@ mykeyboardlayout = awful.widget.keyboardlayout()
-- {{{ Wibox
-- Create a textclock widget
mytextclock = awful.widget.textclock()
mytextclock = wibox.widget.textclock()
-- Create a wibox for each screen and add it
mywibox = {}

View File

@ -3,7 +3,35 @@
This system provide an alternative to the system used in Awesome 3.5 and is
inspired by the one once used by Awesome 3.2-3.4 and Qt QML system.
## A simple layout
## The default widgets
### Widgets
Awesome provide 2 collections of widgets:
* `wibox.widget`: Generic widgets, containers and layouts
* `awful.widget`: The Awesome specific widgets
@DOC_widget_WIDGET_LIST@
### Containers
Containers are widget wrapping another widget. It can be used to add decorations
or to modify the content of the child widget.
@DOC_container_WIDGET_LIST@
### Layouts
Layouts are collection of children widgets. They place them according to rules
and usually provide some options.
@DOC_layout_WIDGET_LIST@
## Placing widgets
### A simple layout
* Display `my_first_widget` only on screen one
* Add a background color to `my_third_widget`
@ -17,7 +45,7 @@ Code:
{ -- Add a background color/pattern for my_third_widget
my_third_widget,
bg = beautiful.bg_focus,
widget = wibox.widget.background,
widget = wibox.container.background,
},
layout = wibox.layout.fixed.horizontal,
}
@ -29,12 +57,12 @@ logic expression can be used as long as it return a valid widget, or a
declarative layout, or `nil`.
## Define widgets inline and place them
### Define widgets inline and place them
* Create a `wibox.widget.textbox` with various properties
* Force the textbox size using `wibox.layout.constraint`
* Add a margin around another textbox
* Add a `wibox.widget.background` (for visualization)
* Add a `wibox.container.background` (for visualization)
Code:
@ -48,7 +76,7 @@ Code:
widget = wibox.widget.textbox
},
bg = "#ff0000",
widget = wibox.widget.background,
widget = wibox.container.background,
},
width = 300,
strategy = "min",
@ -62,13 +90,13 @@ Code:
widget = wibox.widget.textbox
},
bg = "#0000ff",
widget = wibox.widget.background
widget = wibox.container.background
},
left = 10,
right = 10,
top = 1,
bottom = 2,
layout = wibox.layout.margin
layout = wibox.container.margin
},
layout = wibox.layout.fixed.horizontal,
}
@ -78,7 +106,7 @@ Result:
![Example2 screenshot](../images/widgetlayout1.png)
## Use an `wibox.layout.align` layout
### Use an `wibox.layout.align` layout
The `wibox.layout.align` is a little different. While most layouts will
ignore any `nil` lines, the `align` layout rely on them so `left`, `middle`
and `right` can be defined
@ -94,7 +122,7 @@ Code:
## Define new widgets
### Define new widgets
New trivial widgets can be created directly in the layout declaration. Here
is a simple circle widget:
@ -126,7 +154,7 @@ For more information about how to draw widgets, refer to the `Cairo` api:
* [Pango text](https://developer.gnome.org/pango/stable/)
## Externally defined widgets and layouts
### Externally defined widgets and layouts
This is useful when the widget is provided by an external module or when it
requires complex manipulations which would make the declaration unreadable.
@ -146,7 +174,7 @@ Code:
## Accessing widgets
### Accessing widgets
For each widget or container, it is possible to add an `identifier` attribute
so the widget can be accessed later.
@ -188,7 +216,7 @@ Code:
## Extending the system
### Extending the system
This system is very flexible. Each section attribute (the entries with string
keys) is directly linked to the layout or widget API. When setting the
@ -221,7 +249,7 @@ used directly in the layout declaration. This example will update the textbox
every 3 seconds to show the CPU usage.
## Handling sections
### Handling sections
The system allows sections to be defined externally, then composed into
the final layout declaration. Here is an example re-using one of the above

View File

@ -29,11 +29,9 @@
-- @name swap_widgets
-- @class function
--- Get all children of this layout.
--- Get all direct children of this layout.
-- @param layout The layout you are modifying.
-- @return a list of all widgets
-- @name get_children
-- @class function
-- @property children
--- Reset a ratio layout. This removes all widgets from the layout.
-- @param layout The layout you are modifying.

35
docs/common/object.ldoc Normal file
View File

@ -0,0 +1,35 @@
--- Disonnect to a signal.
-- @tparam string name The name of the signal
-- @tparam function func The callback that should be disconnected
-- @see add_signal
-- @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
--- Add a signal to an object. All signals must be added before they can be used.
--
-- @tparam string name The name of the new signal.
-- @function add_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
-- @see add_signal
-- @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

110
docs/common/widget.ldoc Normal file
View File

@ -0,0 +1,110 @@
--- 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
--- When the layout (size) change.
-- This signal is emited when the previous results of `:layout()` and `:fit()`
-- are no longer valid.
-- @signal widget::layout_changed
-- @see widget::redraw_needed
--- When the widget content changed.
-- Unless this signal is emitted, `:layout()` and `:fit()` must return the same
-- result when called with the same arguments. In case this isn't the case,
-- use `widget::layout_changed`.
-- @signal widget::redraw_needed
--- When a mouse button is pressed over the widget.
-- The position of the mouse press relative to the widget while geometry
-- contains the geometry of the widget relative to the wibox.
-- @signal button::press
-- @tparam table widget The widget
-- @tparam number lx The relative horizontal position.
-- @tparam number ly The relative vertical position.
-- @tparam number button The button number.
-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift)
-- @tparam table geometry
-- @tparam number geometry.x The vertical position
-- @tparam number geometry.y The horizontal position
-- @tparam number geometry.width The widget
-- @tparam number geometry.height The height
-- @tparam drawable geometry.drawable The `drawable`
-- @see mouse
--- When a mouse button is released over the widget.
-- The position of the mouse press relative to the widget while geometry
-- contains the geometry of the widget relative to the wibox.
-- @signal button::release
-- @tparam table widget The widget
-- @tparam number lx The relative horizontal position.
-- @tparam number ly The relative vertical position.
-- @tparam number button The button number.
-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift)
-- @tparam table geometry
-- @tparam number geometry.x The vertical position
-- @tparam number geometry.y The horizontal position
-- @tparam number geometry.width The widget
-- @tparam number geometry.height The height
-- @tparam drawable geometry.drawable The `drawable`
-- @see mouse
--- When the mouse enter a widget.
-- @signal mouse::enter
-- @tparam table widget The widget
-- @tparam table geometry
-- @tparam number geometry.x The vertical position
-- @tparam number geometry.y The horizontal position
-- @tparam number geometry.width The widget
-- @tparam number geometry.height The height
-- @tparam drawable geometry.drawable The `drawable`
-- @see mouse
--- When the mouse leave a widget.
-- @signal mouse::leave
-- @tparam table widget The widget
-- @tparam table geometry
-- @tparam number geometry.x The vertical position
-- @tparam number geometry.y The horizontal position
-- @tparam number geometry.width The widget
-- @tparam number geometry.height The height
-- @tparam drawable geometry.drawable The `drawable`
-- @see mouse

View File

@ -82,6 +82,7 @@ file = {
'../lib/gears/init.lua',
'../lib/wibox/layout/init.lua',
'../lib/wibox/widget/init.lua',
'../lib/wibox/container/init.lua',
-- Ignore some parts of the widget library
'../lib/awful/widget/init.lua',

View File

@ -1,51 +0,0 @@
# Get and update the LUA_PATH so the scripts can be executed without Awesome
execute_process(COMMAND lua -e print\(package.path\) OUTPUT_VARIABLE LUA_PATH_)
set(ENV{LUA_PATH} "${SOURCE_DIR}/lib/?;${SOURCE_DIR}/lib/?.lua;${LUA_PATH_}")
file(MAKE_DIRECTORY "${BUILD_DIR}/doc/images")
set(ENV{BUILD_DIRECTORY} "${BUILD_DIR}/")
set(ENV{AWESOME_LIB_DIR} "${SOURCE_DIR}/lib/")
#
# Shape API
#
foreach (SHAPE_NAME "circle" "arrow" "rounded_rect" "hexagon" "infobubble"
"powerline" "isosceles_triangle" "cross" "octogon" "parallelogram"
"losange" "partially_rounded_rect" "radial_progress" "rounded_bar"
"rectangle" "rectangular_tag")
set(SHAPE_FILE "${SOURCE_DIR}/tests/shape/${SHAPE_NAME}.lua")
set(SHAPE_SVG "${BUILD_DIR}/doc/images/shape_${SHAPE_NAME}.svg")
# Generate some SVG for the documentation and load the examples for the doc
execute_process(
COMMAND lua ${SOURCE_DIR}/tests/shape/test-shape.lua ${SHAPE_FILE} ${SHAPE_SVG} ${SOURCE_DIR}/.luacov
ERROR_VARIABLE SHAPE_ERROR
)
if (NOT SHAPE_ERROR STREQUAL "")
message(${SHAPE_ERROR})
message(FATAL_ERROR ${SHAPE_NAME} " SVG generation failed, bye")
endif()
# Set the SVG paths for the doc
set(SHAPE_${SHAPE_NAME}_SVG "../images/shape_${SHAPE_NAME}.svg")
# Use the .lua as code example
file(READ ${SHAPE_FILE} SHAPE_EXAMPLE)
STRING(REGEX REPLACE "\n" ";" SHAPE_EXAMPLE "${SHAPE_EXAMPLE}")
SET(SHAPE_COMMENTED
"![Shape example](../images/shape_${SHAPE_NAME}.svg)\n--\n-- @usage"
)
foreach (EXAMPLE_FILE ${SHAPE_EXAMPLE})
if(NOT EXAMPLE_FILE MATCHES "^.+--DOC_HIDE$")
SET(SHAPE_COMMENTED ${SHAPE_COMMENTED}\n--${EXAMPLE_FILE})
endif()
endforeach()
set(SHAPE_${SHAPE_NAME}_EXAMPLE ${SHAPE_COMMENTED})
endforeach()
# vim: filetype=cmake:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80:foldmethod=marker

48
docs/widget_lists.cmake Normal file
View File

@ -0,0 +1,48 @@
# This file gather the different default example screenshots and create an HTML
# table. Those tables are re-used in the official documentation.
# Ldoc wont parse the HTML content and discount tables are disabled, so here is
# some raw HTML
function(add_to_table name namespace group current_table new_table)
set(${new_table} "${current_table}\n\
<tr>\n\
<td>
<a href='../classes/${namespace}${name}.html'>${namespace}${name}</a>
</td>\n\
<td><img src='../images/AUTOGEN_wibox_${group}_defaults_${name}.svg' /></td>\n\
</tr>\n\
" PARENT_SCOPE)
endfunction()
# Use the generated "defaults" images to build a list
function(generate_widget_list name)
file(GLOB ex_files RELATIVE "${SOURCE_DIR}/tests/examples/wibox/${name}/defaults"
"${SOURCE_DIR}/tests/examples/wibox/${name}/defaults/*")
# Add the table header
set(MY_LIST "<table class='widget_list' border=1>\n\
<tr style='font-weight: bold;'>\n\
<th align='center'>Name</th>\n\
<th align='center'>Example</th>\n\
</tr>"
)
# Loop all examples (and assume an image exist for them)
foreach(ex_file_name ${ex_files})
string(REGEX REPLACE "\\.lua" "" ex_file_name ${ex_file_name})
if(NOT ${ex_file_name} STREQUAL "template")
add_to_table(${ex_file_name} "wibox.${name}." "${name}" "${MY_LIST}" MY_LIST)
endif()
endforeach()
# Add the table footer
set(MY_LIST "${MY_LIST}</table>\n\n")
set(DOC_${name}_WIDGET_LIST ${MY_LIST} PARENT_SCOPE)
endfunction()
generate_widget_list( "container" )
generate_widget_list( "layout" )
generate_widget_list( "widget" )

View File

@ -195,7 +195,7 @@ local function group_label(group, color)
)
)
)
local margin = wibox.layout.margin()
local margin = wibox.container.margin()
margin:set_widget(textbox)
margin:set_top(widget.group_margin)
return margin
@ -315,7 +315,7 @@ local function create_wibox(s, available_groups)
else
available_width_px = available_width_px - item.max_width
end
local column_margin = wibox.layout.margin()
local column_margin = wibox.container.margin()
column_margin:set_widget(item.layout)
column_margin:set_left(widget.group_margin)
columns:add(column_margin)

View File

@ -249,7 +249,7 @@ function menu:exec(num, opts)
self.active_child:hide()
end
self.active_child = self.child[num]
if not self.active_child.visible then
if not self.active_child:get_visible() then
self.active_child:show()
end
elseif type(cmd) == "string" then
@ -384,7 +384,7 @@ function menu:add(args, index)
item.width = item.width or theme.width
item.height = item.height or theme.height
wibox.widget.base.check_widget(item.widget)
item._background = wibox.widget.background()
item._background = wibox.container.background()
item._background:set_widget(item.widget)
item._background:set_fg(item.theme.fg_normal)
item._background:set_bg(item.theme.bg_normal)
@ -431,7 +431,7 @@ function menu:delete(num)
local item = self.items[num]
if not item then return end
item.widget:disconnect_signal("mouse::enter", item._mouse)
item.widget.visible = false
item.widget:set_visible(false)
table.remove(self.items, num)
if self.sel == num then
self:item_leave(self.sel)
@ -516,7 +516,7 @@ function menu.entry(parent, args) -- luacheck: no unused args
end, 1))
-- Set icon if needed
local icon, iconbox
local margin = wibox.layout.margin()
local margin = wibox.container.margin()
margin:set_widget(label)
if args.icon then
icon = surface.load(args.icon)

View File

@ -204,8 +204,8 @@ function titlebar.widget.button(c, name, selector, action)
local ret = imagebox()
if titlebar.enable_tooltip then
ret.tooltip = atooltip({ objects = {ret}, delay_show = 1 })
ret.tooltip:set_text(name)
ret._private.tooltip = atooltip({ objects = {ret}, delay_show = 1 })
ret._private.tooltip:set_text(name)
end
local function update()

View File

@ -10,7 +10,7 @@
-- How to create a tooltip?
-- ---
--
-- myclock = awful.widget.textclock({}, "%T", 1)
-- myclock = wibox.widget.textclock({}, "%T", 1)
-- myclock_t = awful.tooltip({
-- objects = { myclock },
-- timer_function = function()
@ -48,7 +48,7 @@ local a_placement = require("awful.placement")
local abutton = require("awful.button")
local beautiful = require("beautiful")
local textbox = require("wibox.widget.textbox")
local background = require("wibox.widget.background")
local background = require("wibox.container.background")
local dpi = require("beautiful").xresources.apply_dpi
local setmetatable = setmetatable
local ipairs = ipairs
@ -265,7 +265,7 @@ tooltip.new = function(args)
-- Add margin.
local m_lr = args.margin_leftright or dpi(5)
local m_tb = args.margin_topbottom or dpi(3)
self.marginbox = wibox.layout.margin(self.background, m_lr, m_lr, m_tb, m_tb)
self.marginbox = wibox.container.margin(self.background, m_lr, m_lr, m_tb, m_tb)
-- Add tooltip to objects
if args.objects then

View File

@ -60,6 +60,36 @@ function util.deprecate(see)
gears_debug.print_warning(msg .. ".\n" .. tb)
end
--- Create a class proxy with deprecation messages.
-- This is useful when a class has moved somewhere else.
-- @tparam table fallback The new class
-- @tparam string old_name The old class name
-- @tparam string new_name The new class name
-- @treturn table A proxy class.
function util.deprecate_class(fallback, old_name, new_name)
local message = old_name.." has been renamed to "..new_name
local function call(_,...)
util.deprecate(message)
return fallback(...)
end
local function index(_, k)
util.deprecate(message)
return fallback[k]
end
local function newindex(_, k, v)
util.deprecate(message)
fallback[k] = v
end
return setmetatable({}, {__call = call, __index = index, __newindex = newindex})
end
--- Get a valid color for Pango markup
-- @param color The color.
-- @tparam string fallback The color to return if the first is invalid. (default: black)
@ -336,13 +366,22 @@ function util.table.join(...)
return ret
end
--- Override elements in the first table by the one in the second
--- Override elements in the first table by the one in the second.
--
-- Note that this method doesn't copy entries found in `__index`.
-- @tparam table t the table to be overriden
-- @tparam table set the table used to override members of `t`
-- @tparam[opt=false] boolean raw Use rawset (avoid the metatable)
-- @treturn table t (for convenience)
function util.table.crush(t, set)
for k, v in pairs(set) do
t[k] = v
function util.table.crush(t, set, raw)
if raw then
for k, v in pairs(set) do
rawset(t, k, v)
end
else
for k, v in pairs(set) do
t[k] = v
end
end
return t

View File

@ -7,26 +7,7 @@
-- @module awful.wibox
---------------------------------------------------------------------------
local util = require("awful.util")
local wibar = require("awful.wibar")
local function call(_,...)
util.deprecate("awful.wibox has been renamed to awful.wibar")
return wibar(...)
end
local function index(_, k)
util.deprecate("awful.wibox has been renamed to awful.wibar")
return wibar[k]
end
local function newindex(_, k, v)
util.deprecate("awful.wibox has been renamed to awful.wibar")
wibar[k] = v
end
return setmetatable({}, {__call = call, __index = index, __newindex = newindex})
return util.deprecate_class(require("awful.wibar"), "awful.wibox", "awful.wibar")
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -60,9 +60,9 @@ function common.list_update(w, buttons, label, data, objects)
else
ib = wibox.widget.imagebox()
tb = wibox.widget.textbox()
bgb = wibox.widget.background()
tbm = wibox.layout.margin(tb, dpi(4), dpi(4))
ibm = wibox.layout.margin(ib, dpi(4))
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

View File

@ -1,279 +1,17 @@
---------------------------------------------------------------------------
--- A graph widget.
--- This module has been moved to `wibox.widget.graph`
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release @AWESOME_VERSION@
-- @classmod awful.widget.graph
---------------------------------------------------------------------------
local util = require("awful.util")
local setmetatable = setmetatable
local ipairs = ipairs
local math = math
local table = table
local type = type
local color = require("gears.color")
local base = require("wibox.widget.base")
local graph = { mt = {} }
--- Set the graph border color.
-- If the value is nil, no border will be drawn.
--
-- @function set_border_color
-- @param graph The graph.
-- @param color The border color to set.
--- Set the graph foreground color.
--
-- @function set_color
-- @param graph The graph.
-- @param color The graph color.
-- @see gears.color.create_pattern
--- Set the graph background color.
--
-- @function set_background_color
-- @param graph The graph.
-- @param color The graph background color.
--- Set the maximum value the graph should handle.
-- If "scale" is also set, the graph never scales up below this value, but it
-- automatically scales down to make all data fit.
--
-- @function set_max_value
-- @param graph The graph.
-- @param value The value.
--- Set the graph to automatically scale its values. Default is false.
--
-- @function set_scale
-- @param graph The graph.
-- @param scale A boolean value
--- Set the graph to draw stacks. Default is false.
--
-- @function set_stack
-- @param graph The graph.
-- @param stack A boolean value.
--- Set the graph stacking colors. Order matters.
--
-- @function set_stack_colors
-- @param graph The graph.
-- @param stack_colors A table with stacking colors.
local properties = { "width", "height", "border_color", "stack",
"stack_colors", "color", "background_color",
"max_value", "scale" }
function graph.draw(_graph, _, cr, width, height)
local max_value = _graph._data.max_value
local values = _graph._data.values
cr:set_line_width(1)
-- Draw the background first
cr:set_source(color(_graph._data.background_color or "#000000aa"))
cr:paint()
-- Account for the border width
cr:save()
if _graph._data.border_color then
cr:translate(1, 1)
width, height = width - 2, height - 2
end
-- Draw a stacked graph
if _graph._data.stack then
if _graph._data.scale then
for _, v in ipairs(values) do
for _, sv in ipairs(v) do
if sv > max_value then
max_value = sv
end
end
end
end
for i = 0, width do
local rel_i = 0
local rel_x = i + 0.5
if _graph._data.stack_colors then
for idx, col in ipairs(_graph._data.stack_colors) do
local stack_values = values[idx]
if stack_values and i < #stack_values then
local value = stack_values[#stack_values - i] + rel_i
cr:move_to(rel_x, height * (1 - (rel_i / max_value)))
cr:line_to(rel_x, height * (1 - (value / max_value)))
cr:set_source(color(col or "#ff0000"))
cr:stroke()
rel_i = value
end
end
end
end
else
if _graph._data.scale then
for _, v in ipairs(values) do
if v > max_value then
max_value = v
end
end
end
-- Draw the background on no value
if #values ~= 0 then
-- Draw reverse
for i = 0, #values - 1 do
local value = values[#values - i]
if value >= 0 then
value = value / max_value
cr:move_to(i + 0.5, height * (1 - value))
cr:line_to(i + 0.5, height)
end
end
cr:set_source(color(_graph._data.color or "#ff0000"))
cr:stroke()
end
end
-- Undo the cr:translate() for the border
cr:restore()
-- Draw the border last so that it overlaps already drawn values
if _graph._data.border_color then
-- We decremented these by two above
width, height = width + 2, height + 2
-- Draw the border
cr:rectangle(0.5, 0.5, width - 1, height - 1)
cr:set_source(color(_graph._data.border_color or "#ffffff"))
cr:stroke()
end
end
function graph.fit(_graph)
return _graph._data.width, _graph._data.height
end
--- Add a value to the graph
--
-- @param value The value to be added to the graph
-- @param group The stack color group index.
function graph:add_value(value, group)
value = value or 0
local values = self._data.values
local max_value = self._data.max_value
value = math.max(0, value)
if not self._data.scale then
value = math.min(max_value, value)
end
if self._data.stack and group then
if not self._data.values[group]
or type(self._data.values[group]) ~= "table"
then
self._data.values[group] = {}
end
values = self._data.values[group]
end
table.insert(values, value)
local border_width = 0
if self._data.border_color then border_width = 2 end
-- Ensure we never have more data than we can draw
while #values > self._data.width - border_width do
table.remove(values, 1)
end
self:emit_signal("widget::redraw_needed")
return self
end
--- Clear the graph.
function graph:clear()
self._data.values = {}
self:emit_signal("widget::redraw_needed")
return self
end
--- Set the graph height.
-- @param height The height to set.
function graph:set_height(height)
if height >= 5 then
self._data.height = height
self:emit_signal("widget::layout_changed")
end
return self
end
--- Set the graph width.
-- @param width The width to set.
function graph:set_width(width)
if width >= 5 then
self._data.width = width
self:emit_signal("widget::layout_changed")
end
return self
end
-- Build properties function
for _, prop in ipairs(properties) do
if not graph["set_" .. prop] then
graph["set_" .. prop] = function(_graph, value)
if _graph._data[prop] ~= value then
_graph._data[prop] = value
_graph:emit_signal("widget::redraw_needed")
end
return _graph
end
end
if not graph["get_" .. prop] then
graph["get_" .. prop] = function(_graph)
return _graph._data[prop]
end
end
end
--- Create a graph widget.
-- @param args Standard widget() arguments. You should add width and height
-- key to set graph geometry.
-- @return A graph widget.
function graph.new(args)
args = args or {}
local width = args.width or 100
local height = args.height or 20
if width < 5 or height < 5 then return end
local _graph = base.make_widget()
_graph._data = { width = width, height = height, values = {}, max_value = 1 }
-- Set methods
_graph.add_value = graph["add_value"]
_graph.clear = graph["clear"]
_graph.draw = graph.draw
_graph.fit = graph.fit
for _, prop in ipairs(properties) do
_graph["set_" .. prop] = graph["set_" .. prop]
_graph["get_" .. prop] = graph["get_" .. prop]
end
return _graph
end
function graph.mt:__call(...)
return graph.new(...)
end
return setmetatable(graph, graph.mt)
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

View File

@ -1,230 +1,17 @@
---------------------------------------------------------------------------
--- A progressbar widget.
--- This module has been moved to `wibox.widget.progressbar`
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release @AWESOME_VERSION@
-- @classmod awful.widget.progressbar
---------------------------------------------------------------------------
local util = require("awful.util")
local setmetatable = setmetatable
local ipairs = ipairs
local math = math
local base = require("wibox.widget.base")
local color = require("gears.color")
local progressbar = { mt = {} }
local data = setmetatable({}, { __mode = "k" })
--- Set the progressbar border color.
-- If the value is nil, no border will be drawn.
--
-- @function set_border_color
-- @param progressbar The progressbar.
-- @param color The border color to set.
--- Set the progressbar foreground color.
--
-- @function set_color
-- @param progressbar The progressbar.
-- @param color The progressbar color.
--- Set the progressbar background color.
--
-- @function set_background_color
-- @param progressbar The progressbar.
-- @param color The progressbar background color.
--- Set the progressbar to draw vertically. Default is false.
--
-- @function set_vertical
-- @param progressbar The progressbar.
-- @param vertical A boolean value.
--- Set the progressbar to draw ticks. Default is false.
--
-- @function set_ticks
-- @param progressbar The progressbar.
-- @param ticks A boolean value.
--- Set the progressbar ticks gap.
--
-- @function set_ticks_gap
-- @param progressbar The progressbar.
-- @param value The value.
--- Set the progressbar ticks size.
--
-- @function set_ticks_size
-- @param progressbar The progressbar.
-- @param value The value.
--- Set the maximum value the progressbar should handle.
--
-- @function set_max_value
-- @param progressbar The progressbar.
-- @param value The value.
local properties = { "width", "height", "border_color",
"color", "background_color",
"vertical", "value", "max_value",
"ticks", "ticks_gap", "ticks_size" }
function progressbar.draw(pbar, _, cr, width, height)
local ticks_gap = data[pbar].ticks_gap or 1
local ticks_size = data[pbar].ticks_size or 4
-- We want one pixel wide lines
cr:set_line_width(1)
local value = data[pbar].value
local max_value = data[pbar].max_value
if value >= 0 then
value = value / max_value
end
local over_drawn_width = width
local over_drawn_height = height
local border_width = 0
if data[pbar].border_color then
-- Draw border
cr:rectangle(0.5, 0.5, width - 1, height - 1)
cr:set_source(color(data[pbar].border_color))
cr:stroke()
over_drawn_width = width - 2 -- remove 2 for borders
over_drawn_height = height - 2 -- remove 2 for borders
border_width = 1
end
cr:rectangle(border_width, border_width,
over_drawn_width, over_drawn_height)
cr:set_source(color(data[pbar].color or "#ff0000"))
cr:fill()
-- Cover the part that is not set with a rectangle
if data[pbar].vertical then
local rel_height = over_drawn_height * (1 - value)
cr:rectangle(border_width,
border_width,
over_drawn_width,
rel_height)
cr:set_source(color(data[pbar].background_color or "#000000aa"))
cr:fill()
-- Place smaller pieces over the gradient if ticks are enabled
if data[pbar].ticks then
for i=0, height / (ticks_size+ticks_gap)-border_width do
local rel_offset = over_drawn_height / 1 - (ticks_size+ticks_gap) * i
if rel_offset >= rel_height then
cr:rectangle(border_width,
rel_offset,
over_drawn_width,
ticks_gap)
end
end
cr:set_source(color(data[pbar].background_color or "#000000aa"))
cr:fill()
end
else
local rel_x = over_drawn_width * value
cr:rectangle(border_width + rel_x,
border_width,
over_drawn_width - rel_x,
over_drawn_height)
cr:set_source(color(data[pbar].background_color or "#000000aa"))
cr:fill()
if data[pbar].ticks then
for i=0, width / (ticks_size+ticks_gap)-border_width do
local rel_offset = over_drawn_width / 1 - (ticks_size+ticks_gap) * i
if rel_offset <= rel_x then
cr:rectangle(rel_offset,
border_width,
ticks_gap,
over_drawn_height)
end
end
cr:set_source(color(data[pbar].background_color or "#000000aa"))
cr:fill()
end
end
end
function progressbar.fit(pbar)
return data[pbar].width, data[pbar].height
end
--- Set the progressbar value.
-- @param value The progress bar value between 0 and 1.
function progressbar:set_value(value)
value = value or 0
local max_value = data[self].max_value
data[self].value = math.min(max_value, math.max(0, value))
self:emit_signal("widget::redraw_needed")
return self
end
--- Set the progressbar height.
-- @param height The height to set.
function progressbar:set_height(height)
data[self].height = height
self:emit_signal("widget::layout_changed")
return self
end
--- Set the progressbar width.
-- @param width The width to set.
function progressbar:set_width(width)
data[self].width = width
self:emit_signal("widget::layout_changed")
return self
end
-- Build properties function
for _, prop in ipairs(properties) do
if not progressbar["set_" .. prop] then
progressbar["set_" .. prop] = function(pbar, value)
data[pbar][prop] = value
pbar:emit_signal("widget::redraw_needed")
return pbar
end
end
end
--- Create a progressbar widget.
-- @param args Standard widget() arguments. You should add width and height
-- key to set progressbar geometry.
-- @return A progressbar widget.
function progressbar.new(args)
args = args or {}
local width = args.width or 100
local height = args.height or 20
args.type = "imagebox"
local pbar = base.make_widget()
data[pbar] = { width = width, height = height, value = 0, max_value = 1 }
-- Set methods
for _, prop in ipairs(properties) do
pbar["set_" .. prop] = progressbar["set_" .. prop]
end
pbar.draw = progressbar.draw
pbar.fit = progressbar.fit
return pbar
end
function progressbar.mt:__call(...)
return progressbar.new(...)
end
return setmetatable(progressbar, progressbar.mt)
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

View File

@ -1,52 +1,17 @@
---------------------------------------------------------------------------
--- Text clock widget.
-- This widget has moved to `wibox.widget.textclock`
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @copyright 2008-2009 Julien Danjou
-- @release @AWESOME_VERSION@
-- @classmod awful.widget.textclock
---------------------------------------------------------------------------
local util = require("awful.util")
local setmetatable = setmetatable
local os = os
local textbox = require("wibox.widget.textbox")
local timer = require("gears.timer")
local DateTime = require("lgi").GLib.DateTime
local textclock = { mt = {} }
--- This lowers the timeout so that it occurs "correctly". For example, a timeout
-- of 60 is rounded so that it occurs the next time the clock reads ":00 seconds".
local function calc_timeout(real_timeout)
return real_timeout - os.time() % real_timeout
end
--- Create a textclock widget. It draws the time it is in a textbox.
--
-- @param format The time format. Default is " %a %b %d, %H:%M ".
-- @param timeout How often update the time. Default is 60.
-- @return A textbox widget.
function textclock.new(format, timeout)
format = format or " %a %b %d, %H:%M "
timeout = timeout or 60
local w = textbox()
local t
function w._textclock_update_cb()
w:set_markup(DateTime.new_now_local():format(format))
t.timeout = calc_timeout(timeout)
t:again()
return true -- Continue the timer
end
t = timer.weak_start_new(timeout, w._textclock_update_cb)
t:emit_signal("timeout")
return w
end
function textclock.mt:__call(...)
return textclock.new(...)
end
return setmetatable(textclock, textclock.mt)
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

View File

@ -230,7 +230,7 @@ end
--
-- @tparam[opt=2] integer level Level for `debug.getinfo(level, "S")`.
-- Typically 2 or 3.
-- @treturn string The module name, e.g. "wibox.widget.background".
-- @treturn string The module name, e.g. "wibox.container.background".
function object.modulename(level)
return debug.getinfo(level, "S").source:gsub(".*/lib/", ""):gsub("/", "."):gsub("%.lua", "")
end

View File

@ -28,13 +28,15 @@ local module = {}
--- Add a rounded rectangle to the current path.
-- Note: If the radius is bigger than either half side, it will be reduced.
--
-- @SHAPE_rounded_rect_EXAMPLE@
-- @DOC_gears_shape_rounded_rect_EXAMPLE@
--
-- @param cr A cairo content
-- @tparam number width The rectangle width
-- @tparam number height The rectangle height
-- @tparam number radius the corner radius
function module.rounded_rect(cr, width, height, radius)
radius = radius or 10
if width / 2 < radius then
radius = width / 2
end
@ -53,7 +55,7 @@ end
--- Add a rectangle delimited by 2 180 degree arcs to the path.
--
-- @SHAPE_rounded_bar_EXAMPLE@
-- @DOC_gears_shape_rounded_bar_EXAMPLE@
--
-- @param cr A cairo content
-- @param width The rectangle width
@ -64,7 +66,7 @@ end
--- A rounded rect with only some of the corners rounded.
--
-- @SHAPE_partially_rounded_rect_EXAMPLE@
-- @DOC_gears_shape_partially_rounded_rect_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -117,7 +119,7 @@ end
--- A rounded rectangle with a triangle at the top.
--
-- @SHAPE_infobubble_EXAMPLE@
-- @DOC_gears_shape_infobubble_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -152,7 +154,7 @@ end
--- A rectangle terminated by an arrow.
--
-- @SHAPE_rectangular_tag_EXAMPLE@
-- @DOC_gears_shape_rectangular_tag_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -179,7 +181,7 @@ end
--- A simple arrow shape.
--
-- @SHAPE_arrow_EXAMPLE@
-- @DOC_gears_shape_arrow_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -206,7 +208,7 @@ end
--- A squeezed hexagon filling the rectangle.
--
-- @SHAPE_hexagon_EXAMPLE@
-- @DOC_gears_shape_hexagon_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -224,7 +226,7 @@ end
--- Double arrow popularized by the vim-powerline module.
--
-- @SHAPE_powerline_EXAMPLE@
-- @DOC_gears_shape_powerline_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -252,7 +254,7 @@ end
--- An isosceles triangle.
--
-- @SHAPE_isosceles_triangle_EXAMPLE@
-- @DOC_gears_shape_isosceles_triangle_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -266,7 +268,7 @@ end
--- A cross (**+**) symbol.
--
-- @SHAPE_cross_EXAMPLE@
-- @DOC_gears_shape_cross_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -293,7 +295,7 @@ end
--- A similar shape to the `rounded_rect`, but with sharp corners.
--
-- @SHAPE_octogon_EXAMPLE@
-- @DOC_gears_shape_octogon_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -316,7 +318,7 @@ end
--- A circle shape.
--
-- @SHAPE_circle_EXAMPLE@
-- @DOC_gears_shape_circle_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -329,7 +331,7 @@ end
--- A simple rectangle.
--
-- @SHAPE_rectangle_EXAMPLE@
-- @DOC_gears_shape_rectangle_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -341,7 +343,7 @@ end
--- A diagonal parallelogram with the bottom left corner at x=0 and top right
-- at x=width.
--
-- @SHAPE_parallelogram_EXAMPLE@
-- @DOC_gears_shape_parallelogram_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -358,7 +360,7 @@ end
--- A losange.
--
-- @SHAPE_losange_EXAMPLE@
-- @DOC_gears_shape_losange_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number width The shape width
@ -377,7 +379,7 @@ end
-- Note that this shape is not closed and thus filling it doesn't make much
-- sense.
--
-- @SHAPE_radial_progress_EXAMPLE@
-- @DOC_gears_shape_radial_progress_EXAMPLE@
--
-- @param cr A cairo context
-- @tparam number w The shape width

View File

@ -522,7 +522,7 @@ function naughty.notify(args)
-- create textbox
local textbox = wibox.widget.textbox()
local marginbox = wibox.layout.margin()
local marginbox = wibox.container.margin()
marginbox:set_margins(margin)
marginbox:set_widget(textbox)
textbox:set_valign("middle")
@ -538,7 +538,7 @@ function naughty.notify(args)
if actions then
for action, callback in pairs(actions) do
local actiontextbox = wibox.widget.textbox()
local actionmarginbox = wibox.layout.margin()
local actionmarginbox = wibox.container.margin()
actionmarginbox:set_margins(margin)
actionmarginbox:set_widget(actiontextbox)
actiontextbox:set_valign("middle")
@ -582,7 +582,7 @@ function naughty.notify(args)
-- if we have an icon, use it
if icon then
iconbox = wibox.widget.imagebox()
iconmargin = wibox.layout.margin(iconbox, margin, margin, margin, margin)
iconmargin = wibox.container.margin(iconbox, margin, margin, margin, margin)
if icon_size then
local scaled = cairo.ImageSurface(cairo.Format.ARGB32, icon_size, icon_size)
local cr = cairo.Context(scaled)

View File

@ -0,0 +1,292 @@
---------------------------------------------------------------------------
-- A container capable of changing the background color, foreground color
-- widget shape.
--
--@DOC_wibox_container_defaults_background_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.container.background
---------------------------------------------------------------------------
local base = require("wibox.widget.base")
local color = require("gears.color")
local surface = require("gears.surface")
local beautiful = require("beautiful")
local cairo = require("lgi").cairo
local util = require("awful.util")
local setmetatable = setmetatable
local type = type
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local background = { mt = {} }
-- Draw this widget
function background:draw(context, cr, width, height)
if not self._private.widget or not self._private.widget:get_visible() then
return
end
-- Keep the shape path in case there is a border
self._private.path = nil
if self._private.shape then
-- Only add the offset if there is something to draw
local offset = ((self._private.shape_border_width and self._private.shape_border_color)
and self._private.shape_border_width or 0) / 2
cr:translate(offset, offset)
self._private.shape(cr, width - 2*offset, height - 2*offset, unpack(self._private.shape_args or {}))
cr:translate(-offset, -offset)
self._private.path = cr:copy_path()
cr:clip()
end
if self._private.background then
cr:set_source(self._private.background)
cr:paint()
end
if self._private.bgimage then
if type(self._private.bgimage) == "function" then
self._private.bgimage(context, cr, width, height,unpack(self._private.bgimage_args))
else
local pattern = cairo.Pattern.create_for_surface(self._private.bgimage)
cr:set_source(pattern)
cr:paint()
end
end
end
-- Draw the border
function background:after_draw_children(_, cr)
-- Draw the border
if self._private.path and self._private.shape_border_width and self._private.shape_border_width > 0 then
cr:append_path(self._private.path)
cr:set_source(color(self._private.shape_border_color or self._private.foreground or beautiful.fg_normal))
cr:set_line_width(self._private.shape_border_width)
cr:stroke()
self._private.path = nil
end
end
-- Prepare drawing the children of this widget
function background:before_draw_children(_, cr)
if self._private.foreground then
cr:set_source(self._private.foreground)
end
-- Clip the shape
if self._private.path and self._private.shape_clip then
cr:append_path(self._private.path)
cr:clip()
end
end
-- Layout this widget
function background:layout(_, width, height)
if self._private.widget then
return { base.place_widget_at(self._private.widget, 0, 0, width, height) }
end
end
-- Fit this widget into the given area
function background:fit(context, width, height)
if not self._private.widget then
return 0, 0
end
return base.fit_widget(self, context, self._private.widget, width, height)
end
--- The widget displayed in the background widget.
-- @property widget
-- @tparam widget widget The widget to be disaplayed inside of the background
-- area
function background:set_widget(widget)
if widget then
base.check_widget(widget)
end
self._private.widget = widget
self:emit_signal("widget::layout_changed")
end
function background:get_widget()
return self._private.widget
end
-- Get children element
-- @treturn table The children
function background:get_children()
return {self._private.widget}
end
-- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function background:set_children(children)
self:set_widget(children[1])
end
--- The background color/pattern/gradient to use.
--@DOC_wibox_container_background_bg_EXAMPLE@
-- @property bg
-- @param bg A color string, pattern or gradient
-- @see gears.color
function background:set_bg(bg)
if bg then
self._private.background = color(bg)
else
self._private.background = nil
end
self:emit_signal("widget::redraw_needed")
end
function background:get_bg()
return self._private.background
end
--- The foreground (text) color/pattern/gradient to use.
--@DOC_wibox_container_background_fg_EXAMPLE@
-- @property fg
-- @param fg A color string, pattern or gradient
-- @see gears.color
function background:set_fg(fg)
if fg then
self._private.foreground = color(fg)
else
self._private.foreground = nil
end
self:emit_signal("widget::redraw_needed")
end
function background:get_fg()
return self._private.foreground
end
--- The background shap e.
--
-- Use `set_shape` to set additional shape paramaters.
--
--@DOC_wibox_container_background_shape_EXAMPLE@
-- @property shape
-- @param shape A function taking a context, width and height as arguments
-- @see gears.shape
-- @see set_shape
--- Set the background shape.
--
-- Any other arguments will be passed to the shape function
-- @param shape A function taking a context, width and height as arguments
-- @see gears.shape
-- @see shape
function background:set_shape(shape, ...)
self._private.shape = shape
self._private.shape_args = {...}
self:emit_signal("widget::redraw_needed")
end
function background:get_shape()
return self._private.shape
end
--- When a `shape` is set, also draw a border.
--
-- See `wibox.container.background.shape` for an usage example.
-- @property shape_border_width
-- @tparam number width The border width
function background:set_shape_border_width(width)
self._private.shape_border_width = width
self:emit_signal("widget::redraw_needed")
end
function background:get_shape_border_width()
return self._private.shape_border_width
end
--- When a `shape` is set, also draw a border.
--
-- See `wibox.container.background.shape` for an usage example.
-- @property shape_border_color
-- @param[opt=self._private.foreground] fg The border color, pattern or gradient
-- @see gears.color
function background:set_shape_border_color(fg)
self._private.shape_border_color = fg
self:emit_signal("widget::redraw_needed")
end
function background:get_shape_border_color()
return self._private.shape_border_color
end
--- When a `shape` is set, make sure nothing is drawn outside of it.
--@DOC_wibox_container_background_clip_EXAMPLE@
-- @property shape_clip
-- @tparam boolean value If the shape clip is enable
function background:set_shape_clip(value)
self._private.shape_clip = value
self:emit_signal("widget::redraw_needed")
end
function background:get_shape_clip()
return self._private.shape_clip or false
end
--- The background image to use
-- If `image` is a function, it will be called with `(context, cr, width, height)`
-- as arguments. Any other arguments passed to this method will be appended.
-- @property bgimage
-- @param image A background image or a function
-- @see gears.surface
function background:set_bgimage(image, ...)
self._private.bgimage = type(image) == "function" and image or surface.load(image)
self._private.bgimage_args = {...}
self:emit_signal("widget::redraw_needed")
end
function background:get_bgimage()
return self._private.bgimage
end
--- Returns a new background container.
--
-- A background container applies a background and foreground color
-- to another widget.
-- @param[opt] widget The widget to display.
-- @param[opt] bg The background to use for that widget.
-- @param[opt] shape A `gears.shape` compatible shape function
-- @function wibox.container.background
local function new(widget, bg, shape)
local ret = base.make_widget(nil, nil, {
enable_properties = true,
})
util.table.crush(ret, background, true)
ret._private.shape = shape
ret:set_widget(widget)
ret:set_bg(bg)
return ret
end
function background.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(background, background.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,171 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_container_defaults_constraint_EXAMPLE@
-- @author Lukáš Hrázký
-- @copyright 2012 Lukáš Hrázký
-- @release @AWESOME_VERSION@
-- @classmod wibox.container.constraint
---------------------------------------------------------------------------
local setmetatable = setmetatable
local base = require("wibox.widget.base")
local util = require("awful.util")
local math = math
local constraint = { mt = {} }
-- Layout a constraint layout
function constraint:layout(_, width, height)
if self._private.widget then
return { base.place_widget_at(self._private.widget, 0, 0, width, height) }
end
end
-- Fit a constraint layout into the given space
function constraint:fit(context, width, height)
local w, h
if self._private.widget then
w = self._private.strategy(width, self._private.width)
h = self._private.strategy(height, self._private.height)
w, h = base.fit_widget(self, context, self._private.widget, w, h)
else
w, h = 0, 0
end
w = self._private.strategy(w, self._private.width)
h = self._private.strategy(h, self._private.height)
return w, h
end
--- The widget to be constrained.
-- @property widget
-- @tparam widget widget The widget
function constraint:set_widget(widget)
self._private.widget = widget
self:emit_signal("widget::layout_changed")
end
function constraint:get_widget()
return self._private.widget
end
--- Get the number of children element
-- @treturn table The children
function constraint:get_children()
return {self._private.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function constraint:set_children(children)
self:set_widget(children[1])
end
--- Set the strategy to use for the constraining. Valid values are 'max',
-- 'min' or 'exact'. Throws an error on invalid values.
-- @property strategy
function constraint:set_strategy(val)
local func = {
min = function(real_size, limit)
return limit and math.max(limit, real_size) or real_size
end,
max = function(real_size, limit)
return limit and math.min(limit, real_size) or real_size
end,
exact = function(real_size, limit)
return limit or real_size
end
}
if not func[val] then
error("Invalid strategy for constraint layout: " .. tostring(val))
end
self._private.strategy = func[val]
self:emit_signal("widget::layout_changed")
end
function constraint:get_strategy()
return self._private.strategy
end
--- Set the maximum width to val. nil for no width limit.
-- @property height
-- @param number
function constraint:set_width(val)
self._private.width = val
self:emit_signal("widget::layout_changed")
end
function constraint:get_width()
return self._private.width
end
--- Set the maximum height to val. nil for no height limit.
-- @property width
-- @param number
function constraint:set_height(val)
self._private.height = val
self:emit_signal("widget::layout_changed")
end
function constraint:get_height()
return self._private.height
end
--- Reset this layout. The widget will be unreferenced, strategy set to "max"
-- and the constraints set to nil.
function constraint:reset()
self._private.width = nil
self._private.height = nil
self:set_strategy("max")
self:set_widget(nil)
end
--- Returns a new constraint container.
-- This container will constraint the size of a
-- widget according to the strategy. Note that this will only work for layouts
-- that respect the widget's size, eg. fixed layout. In layouts that don't
-- (fully) respect widget's requested size, the inner widget still might get
-- drawn with a size that does not fit the constraint, eg. in flex layout.
-- @param[opt] widget A widget to use.
-- @param[opt] strategy How to constraint the size. 'max' (default), 'min' or
-- 'exact'.
-- @param[opt] width The maximum width of the widget. nil for no limit.
-- @param[opt] height The maximum height of the widget. nil for no limit.
-- @treturn table A new constraint container
-- @function wibox.container.constraint
local function new(widget, strategy, width, height)
local ret = base.make_widget(nil, nil, {enable_properties = true})
util.table.crush(ret, constraint, true)
ret:set_strategy(strategy or "max")
ret:set_width(width)
ret:set_height(height)
if widget then
ret:set_widget(widget)
end
return ret
end
function constraint.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(constraint, constraint.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,20 @@
---------------------------------------------------------------------------
--- Collection of containers that can be used in widget boxes
--
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.container
---------------------------------------------------------------------------
local base = require("wibox.widget.base")
return setmetatable({
rotate = require("wibox.container.rotate");
margin = require("wibox.container.margin");
mirror = require("wibox.container.mirror");
constraint = require("wibox.container.constraint");
scroll = require("wibox.container.scroll");
background = require("wibox.container.background");
}, {__call = function(_, args) return base.make_widget_declarative(args) end})
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,219 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_container_defaults_margin_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.container.margin
---------------------------------------------------------------------------
local pairs = pairs
local setmetatable = setmetatable
local base = require("wibox.widget.base")
local gcolor = require("gears.color")
local cairo = require("lgi").cairo
local util = require("awful.util")
local margin = { mt = {} }
-- Draw a margin layout
function margin:draw(_, cr, width, height)
local x = self._private.left
local y = self._private.top
local w = self._private.right
local h = self._private.bottom
local color = self._private.color
if not self._private.widget or width <= x + w or height <= y + h then
return
end
if color then
cr:set_source(color)
cr:rectangle(0, 0, width, height)
cr:rectangle(x, y, width - x - w, height - y - h)
cr:set_fill_rule(cairo.FillRule.EVEN_ODD)
cr:fill()
end
end
-- Layout a margin layout
function margin:layout(_, width, height)
if self._private.widget then
local x = self._private.left
local y = self._private.top
local w = self._private.right
local h = self._private.bottom
return { base.place_widget_at(self._private.widget, x, y, width - x - w, height - y - h) }
end
end
-- Fit a margin layout into the given space
function margin:fit(context, width, height)
local extra_w = self._private.left + self._private.right
local extra_h = self._private.top + self._private.bottom
local w, h = 0, 0
if self._private.widget then
w, h = base.fit_widget(self, context, self._private.widget, width - extra_w, height - extra_h)
end
if self._private.draw_empty == false and (w == 0 or h == 0) then
return 0, 0
end
return w + extra_w, h + extra_h
end
--- The widget to be wrapped the the margins.
-- @property widget
-- @tparam widget widget The widget
function margin:set_widget(widget)
if widget then
base.check_widget(widget)
end
self._private.widget = widget
self:emit_signal("widget::layout_changed")
end
function margin:get_widget()
return self._private.widget
end
-- Get the number of children element
-- @treturn table The children
function margin:get_children()
return {self._private.widget}
end
-- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function margin:set_children(children)
self:set_widget(children[1])
end
--- Set all the margins to val.
-- @property margins
-- @tparam number val The margin value
function margin:set_margins(val)
if self._private.left == val and
self._private.right == val and
self._private.top == val and
self._private.bottom == val then
return
end
self._private.left = val
self._private.right = val
self._private.top = val
self._private.bottom = val
self:emit_signal("widget::layout_changed")
end
--- Set the margins color to create a border.
-- @property color
-- @param color A color used to fill the margin.
function margin:set_color(color)
self._private.color = color and gcolor(color)
self:emit_signal("widget::redraw_needed")
end
function margin:get_color()
return self._private.color
end
--- Draw the margin even if the content size is 0x0 (default: true)
-- @function draw_empty
-- @tparam boolean draw_empty Draw nothing is content is 0x0 or draw the margin anyway
function margin:set_draw_empty(draw_empty)
self._private.draw_empty = draw_empty
self:emit_signal("widget::layout_changed")
end
function margin:get_draw_empty()
return self._private.draw_empty
end
--- Reset this layout. The widget will be unreferenced, the margins set to 0
-- and the color erased
function margin:reset()
self:set_widget(nil)
self:set_margins(0)
self:set_color(nil)
end
--- Set the left margin that this layout adds to its widget.
-- @param margin The new margin to use.
-- @property left
--- Set the right margin that this layout adds to its widget.
-- @param margin The new margin to use.
-- @property right
--- Set the top margin that this layout adds to its widget.
-- @param margin The new margin to use.
-- @property top
--- Set the bottom margin that this layout adds to its widget.
-- @param margin The new margin to use.
-- @property bottom
-- Create setters for each direction
for _, v in pairs({ "left", "right", "top", "bottom" }) do
margin["set_" .. v] = function(layout, val)
if layout._private[v] == val then return end
layout._private[v] = val
layout:emit_signal("widget::layout_changed")
end
margin["get_" .. v] = function(layout)
return layout._private[v]
end
end
--- Returns a new margin container.
-- @param[opt] widget A widget to use.
-- @param[opt] left A margin to use on the left side of the widget.
-- @param[opt] right A margin to use on the right side of the widget.
-- @param[opt] top A margin to use on the top side of the widget.
-- @param[opt] bottom A margin to use on the bottom side of the widget.
-- @param[opt] color A color for the margins.
-- @param[opt] draw_empty whether or not to draw the margin when the content is empty
-- @treturn table A new margin container
-- @function wibox.container.margin
local function new(widget, left, right, top, bottom, color, draw_empty)
local ret = base.make_widget(nil, nil, {enable_properties = true})
util.table.crush(ret, margin, true)
ret:set_left(left or 0)
ret:set_right(right or 0)
ret:set_top(top or 0)
ret:set_bottom(bottom or 0)
ret:set_draw_empty(draw_empty)
ret:set_color(color)
if widget then
ret:set_widget(widget)
end
return ret
end
function margin.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(margin, margin.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,140 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_container_defaults_mirror_EXAMPLE@
-- @author dodo
-- @copyright 2012 dodo
-- @release @AWESOME_VERSION@
-- @classmod wibox.container.mirror
---------------------------------------------------------------------------
local type = type
local error = error
local ipairs = ipairs
local setmetatable = setmetatable
local base = require("wibox.widget.base")
local matrix = require("gears.matrix")
local util = require("awful.util")
local mirror = { mt = {} }
-- Layout this layout
function mirror:layout(_, width, height)
if not self._private.widget then return end
local m = matrix.identity
local t = { x = 0, y = 0 } -- translation
local s = { x = 1, y = 1 } -- scale
if self._private.horizontal then
t.x = width
s.x = -1
end
if self._private.vertical then
t.y = height
s.y = -1
end
m = m:translate(t.x, t.y)
m = m:scale(s.x, s.y)
return { base.place_widget_via_matrix(self._private.widget, m, width, height) }
end
-- Fit this layout into the given area
function mirror:fit(context, ...)
if not self._private.widget then
return 0, 0
end
return base.fit_widget(self, context, self._private.widget, ...)
end
--- The widget to be reflected.
-- @property widget
-- @tparam widget widget The widget
function mirror:set_widget(widget)
if widget then
base.check_widget(widget)
end
self._private.widget = widget
self:emit_signal("widget::layout_changed")
end
function mirror:get_widget()
return self._private.widget
end
--- Get the number of children element
-- @treturn table The children
function mirror:get_children()
return {self._private.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function mirror:set_children(children)
self:set_widget(children[1])
end
--- Reset this layout. The widget will be removed and the axes reset.
function mirror:reset()
self._private.horizontal = false
self._private.vertical = false
self:set_widget(nil)
end
function mirror:set_reflection(reflection)
if type(reflection) ~= 'table' then
error("Invalid type of reflection for mirror layout: " ..
type(reflection) .. " (should be a table)")
end
for _, ref in ipairs({"horizontal", "vertical"}) do
if reflection[ref] ~= nil then
self._private[ref] = reflection[ref]
end
end
self:emit_signal("widget::layout_changed")
end
--- Get the reflection of this mirror layout.
-- @property reflection
-- @param table reflection A table of booleans with the keys "horizontal", "vertical".
-- @param boolean reflection.horizontal
-- @param boolean reflection.vertical
function mirror:get_reflection()
return { horizontal = self._private.horizontal, vertical = self._private.vertical }
end
--- Returns a new mirror container.
-- A mirror container mirrors a given widget. Use
-- `:set_widget()` to set the widget and
-- `:set_horizontal()` and `:set_vertical()` for the direction.
-- horizontal and vertical are by default false which doesn't change anything.
-- @param[opt] widget The widget to display.
-- @param[opt] reflection A table describing the reflection to apply.
-- @treturn table A new mirror container
-- @function wibox.container.mirror
local function new(widget, reflection)
local ret = base.make_widget(nil, nil, {enable_properties = true})
ret._private.horizontal = false
ret._private.vertical = false
util.table.crush(ret, mirror, true)
ret:set_widget(widget)
ret:set_reflection(reflection or {})
return ret
end
function mirror.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(mirror, mirror.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,159 @@
---------------------------------------------------------------------------
-- A container rotating the conained widget by 90 degrees.
--
--@DOC_wibox_container_defaults_rotate_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.container.rotate
---------------------------------------------------------------------------
local error = error
local pi = math.pi
local setmetatable = setmetatable
local tostring = tostring
local base = require("wibox.widget.base")
local matrix = require("gears.matrix")
local util = require("awful.util")
local rotate = { mt = {} }
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
-- Layout this layout
function rotate:layout(_, width, height)
if not self._private.widget or not self._private.widget._private.visible then
return
end
local dir = self:get_direction()
local m = matrix.identity
if dir == "west" then
m = m:rotate(pi / 2)
m = m:translate(0, -width)
elseif dir == "south" then
m = m:rotate(pi)
m = m:translate(-width, -height)
elseif dir == "east" then
m = m:rotate(3 * pi / 2)
m = m:translate(-height, 0)
end
-- Since we rotated, we might have to swap width and height.
-- transform() does that for us.
return { base.place_widget_via_matrix(self._private.widget, m, transform(self, width, height)) }
end
-- Fit this layout into the given area
function rotate:fit(context, width, height)
if not self._private.widget then
return 0, 0
end
return transform(self, base.fit_widget(self, context, self._private.widget, transform(self, width, height)))
end
--- The widget to be rotated.
-- @property widget
-- @tparam widget widget The widget
function rotate:set_widget(widget)
if widget then
base.check_widget(widget)
end
self._private.widget = widget
self:emit_signal("widget::layout_changed")
end
function rotate:get_widget()
return self._private.widget
end
--- Get the number of children element
-- @treturn table The children
function rotate:get_children()
return {self._private.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function rotate:set_children(children)
self:set_widget(children[1])
end
--- Reset this layout. The widget will be removed and the rotation reset.
function rotate:reset()
self._private.direction = nil
self:set_widget(nil)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
--- The direction of this rotating container.
-- Valid values are:
--
-- * *north*
-- * *east*
-- * *south*
-- * *north*
--
--@DOC_wibox_container_rotate_angle_EXAMPLE@
-- @property direction
-- @tparam string dir The direction
function rotate:set_direction(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
self._private.direction = dir
self:emit_signal("widget::layout_changed")
end
--- Get the direction of this rotating layout
function rotate:get_direction()
return self._private.direction or "north"
end
--- Returns a new rotate container.
-- A rotate container 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.
-- @param[opt] widget The widget to display.
-- @param[opt] dir The direction to rotate to.
-- @treturn table A new rotate container.
-- @function wibox.container.rotate
local function new(widget, dir)
local ret = base.make_widget(nil, nil, {enable_properties = true})
util.table.crush(ret, rotate, true)
ret:set_widget(widget)
ret:set_direction(dir or "north")
return ret
end
function rotate.mt:__call(...)
return new(...)
end
return setmetatable(rotate, rotate.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,519 @@
---------------------------------------------------------------------------
-- @author Uli Schlachter (based on ideas from Saleur Geoffrey)
-- @copyright 2015 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.container.scroll
---------------------------------------------------------------------------
local cache = require("gears.cache")
local timer = require("gears.timer")
local hierarchy = require("wibox.hierarchy")
local base = require("wibox.widget.base")
local lgi = require("lgi")
local GLib = lgi.GLib
local scroll = {}
local scroll_mt = { __index = scroll }
local _need_scroll_redraw
-- "Strip" a context so that we can use it for our own drawing
local function cleanup_context(context)
local skip = { wibox = true, drawable = true, client = true, position = true }
local res = {}
for k, v in pairs(context) do
if not skip[k] then
res[k] = v
end
end
return res
end
-- Create a hierarchy (and some more stuff) for drawing the given widget. This
-- allows "some stuff" to be re-used instead of re-created all the time.
local hierarchy_cache = cache.new(function(context, widget, width, height)
context = cleanup_context(context)
local layouts = setmetatable({}, { __mode = "k" })
-- Create a widget hierarchy and update when needed
local hier
local function do_pending_updates(layout)
layouts[layout] = true
hier:update(context, widget, width, height, nil)
end
local function emit(signal)
-- Make the scroll layouts redraw
for w in pairs(layouts) do
w:emit_signal(signal)
end
end
local function redraw_callback()
emit("widget::redraw_needed")
end
local function layout_callback()
emit("widget::redraw_needed")
emit("widget::layout_changed")
end
hier = hierarchy.new(context, widget, width, height, redraw_callback, layout_callback, nil)
return hier, do_pending_updates, context
end)
--- Calculate all the information needed for scrolling.
-- @param self The instance of the scrolling layout.
-- @param context A widget context under which we are fit/drawn.
-- @param width The available width
-- @param height The available height
-- @return A table with the following entries
-- @field fit_width The width that should be returned from :fit
-- @field fit_height The height that should be returned from :fit
-- @field surface_width The width for showing the child widget
-- @field surface_height The height for showing the child widget
-- @field first_x The x offset for drawing the child the first time
-- @field first_y The y offset for drawing the child the first time
-- @field[opt] second_x The x offset for drawing the child the second time
-- @field[opt] second_y The y offset for drawing the child the second time
-- @field hierarchy The wibox.hierarchy instance representing "everything"
-- @field context The widget context for drawing the hierarchy
local function calculate_info(self, context, width, height)
local result = {}
assert(self._private.widget)
-- First, get the size of the widget (and the size of extra space)
local surface_width, surface_height = width, height
local extra_width, extra_height, extra = 0, 0, self._private.expand and self._private.extra_space or 0
local w, h
if self._private.dir == "h" then
w, h = base.fit_widget(self, context, self._private.widget, self._private.space_for_scrolling, height)
surface_width = w
extra_width = extra
else
w, h = base.fit_widget(self, context, self._private.widget, width, self._private.space_for_scrolling)
surface_height = h
extra_height = extra
end
result.fit_width, result.fit_height = w, h
if self._private.dir == "h" then
if self._private.max_size then
result.fit_width = math.min(w, self._private.max_size)
end
else
if self._private.max_size then
result.fit_height = math.min(h, self._private.max_size)
end
end
if w > width or h > height then
-- There is less space available than we need, we have to scroll
_need_scroll_redraw(self)
surface_width, surface_height = surface_width + extra_width, surface_height + extra_height
local x, y = 0, 0
local function get_scroll_offset(size, visible_size)
return self._private.step_function(self._private.timer:elapsed(), size, visible_size, self._private.speed, self._private.extra_space)
end
if self._private.dir == "h" then
x = -get_scroll_offset(surface_width - extra, width)
else
y = -get_scroll_offset(surface_height - extra, height)
end
result.first_x, result.first_y = x, y
-- Was the extra space already included elsewhere?
local extra_spacer = self._private.expand and 0 or self._private.extra_space
if self._private.dir == "h" then
x = x + surface_width + extra_spacer
else
y = y + surface_height + extra_spacer
end
result.second_x, result.second_y = x, y
else
result.first_x, result.first_y = 0, 0
end
result.surface_width, result.surface_height = surface_width, surface_height
-- Get the hierarchy and subscribe ourselves to updates
local hier, do_pending_updates, ctx = hierarchy_cache:get(context,
self._private.widget, surface_width, surface_height)
result.hierarchy = hier
result.context = ctx
do_pending_updates(self)
return result
end
-- Draw this scrolling layout.
-- @param context The context in which we are drawn.
-- @param cr The cairo context to draw to.
-- @param width The available width.
-- @param height The available height.
function scroll:draw(context, cr, width, height)
if not self._private.widget then
return
end
local info = calculate_info(self, context, width, height)
-- Draw the first instance of the child
cr:save()
cr:translate(info.first_x, info.first_y)
cr:rectangle(0, 0, info.surface_width, info.surface_height)
cr:clip()
info.hierarchy:draw(info.context, cr)
cr:restore()
-- If there is one, draw the second instance (same code as above, minus the
-- clip)
if info.second_x and info.second_y then
cr:translate(info.second_x, info.second_y)
cr:rectangle(0, 0, info.surface_width, info.surface_height)
cr:clip()
info.hierarchy:draw(info.context, cr)
end
end
-- Fit the scroll layout into the given space.
-- @param context The context in which we are fit.
-- @param width The available width.
-- @param height The available height.
function scroll:fit(context, width, height)
if not self._private.widget then
return 0, 0
end
local info = calculate_info(self, context, width, height)
return info.fit_width, info.fit_height
end
-- Internal function used for triggering redraws for scrolling.
-- The purpose is to start a timer for redrawing the widget for scrolling.
-- Redrawing works by simply emitting the `widget::redraw_needed` signal.
-- Pausing is implemented in this function: We just don't start a timer.
-- This function must be idempotent (calling it multiple times right after
-- another does not make a difference).
_need_scroll_redraw = function(self)
if not self._private.paused and not self._private.scroll_timer then
self._private.scroll_timer = timer.start_new(1 / self._private.fps, function()
self._private.scroll_timer = nil
self:emit_signal("widget::redraw_needed")
end)
end
end
--- Pause the scrolling animation.
-- @see continue
function scroll:pause()
if self._private.paused then
return
end
self._private.paused = true
self._private.timer:stop()
end
--- Continue the scrolling animation.
-- @see pause
function scroll:continue()
if not self._private.paused then
return
end
self._private.paused = false
self._private.timer:continue()
self:emit_signal("widget::redraw_needed")
end
--- Reset the scrolling state to its initial condition.
-- For must scroll step functions, the effect of this function should be to
-- display the widget without any scrolling applied.
-- This function does not undo the effect of @{pause}.
function scroll:reset_scrolling()
self._private.timer:start()
if self._private.paused then
self._private.timer:stop()
end
end
--- Set the direction in which this widget scroll.
-- @param dir Either "h" for horizontal scrolling or "v" for vertical scrolling
function scroll:set_direction(dir)
if dir == self._private.dir then
return
end
if dir ~= "h" and dir ~= "v" then
error("Invalid direction, can only be 'h' or 'v'")
end
self._private.dir = dir
self:emit_signal("widget::layout_changed")
self:emit_signal("widget::redraw_needed")
end
--- The widget to be scrolled.
-- @property widget
-- @tparam widget widget The widget
function scroll:set_widget(widget)
if widget == self._private.widget then
return
end
if widget then
base.check_widget(widget)
end
self._private.widget = widget
self:emit_signal("widget::layout_changed")
self:emit_signal("widget::redraw_needed")
end
function scroll:get_widget()
return self._private.widget
end
--- Get the number of children element
-- @treturn table The children
function scroll:get_children()
return {self._private.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function scroll:set_children(children)
self:set_widget(children[1])
end
--- Specify the expand mode that is used for extra space.
-- @tparam boolean expand If true, the widget is expanded to include the extra
-- space. If false, the extra space is simply left empty.
-- @see set_extra_space
function scroll:set_expand(expand)
if expand == self._private.expand then
return
end
self._private.expand = expand
self:emit_signal("widget::redraw_needed")
end
--- Set the number of frames per second that this widget should draw.
-- @tparam number fps The number of frames per second
function scroll:set_fps(fps)
if fps == self._private.fps then
return
end
self._private.fps = fps
-- No signal needed: If we are scrolling, the next redraw will apply the new
-- FPS, else it obviously doesn't make a difference.
end
--- Set the amount of extra space that should be included in the scrolling. This
-- extra space will likely be left empty between repetitions of the widgets.
-- @tparam number extra_space The amount of extra space
-- @see set_expand
function scroll:set_extra_space(extra_space)
if extra_space == self._private.extra_space then
return
end
self._private.extra_space = extra_space
self:emit_signal("widget::redraw_needed")
end
--- Set the speed of the scrolling animation. The exact meaning depends on the
-- step function that is used, but for the simplest step functions, this will be
-- in pixels per second.
-- @tparam number speed The speed for the animation
function scroll:set_speed(speed)
if speed == self._private.speed then
return
end
self._private.speed = speed
self:emit_signal("widget::redraw_needed")
end
--- Set the maximum size of this widget in the direction set by
-- @{set_direction}. If the child widget is smaller than this size, no scrolling
-- is done. If the child widget is larger, then only this size will be visible
-- and the rest is made visible via scrolling.
-- @tparam number max_size The maximum size of this widget or nil for unlimited.
function scroll:set_max_size(max_size)
if max_size == self._private.max_size then
return
end
self._private.max_size = max_size
self:emit_signal("widget::layout_changed")
end
--- Set the step function that determines the exact behaviour of the scrolling
-- animation.
-- The step function is called with five arguments:
--
-- * The time in seconds since the state of the animation
-- * The size of the child widget
-- * The size of the visible part of the widget
-- * The speed of the animation. This should have a linear effect on this
-- function's behaviour.
-- * The extra space configured by @{set_extra_space}. This was not yet added to
-- the size of the child widget, but should likely be added to it in most
-- cases.
--
-- The step function should return a single number. This number is the offset at
-- which the widget is drawn and should be between 0 and `size+extra_space`.
-- @tparam function step_function A step function.
-- @see step_functions
function scroll:set_step_function(step_function)
-- Call the step functions once to see if it works
step_function(0, 42, 10, 10, 5)
if step_function == self._private.step_function then
return
end
self._private.step_function = step_function
self:emit_signal("widget::redraw_needed")
end
--- Set an upper limit for the space for scrolling.
-- This restricts the child widget's maximal size.
-- @tparam number space_for_scrolling The space for scrolling
function scroll:set_space_for_scrolling(space_for_scrolling)
if space_for_scrolling == self._private.space_for_scrolling then
return
end
self._private.space_for_scrolling = space_for_scrolling
self:emit_signal("widget::layout_changed")
end
local function get_layout(dir, widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
local ret = base.make_widget(nil, nil, {enable_properties = true})
ret._priavte.paused = false
ret._private.timer = GLib.Timer()
ret._private.scroll_timer = nil
setmetatable(ret, scroll_mt)
ret:set_direction(dir)
ret:set_widget(widget)
ret:set_fps(fps or 20)
ret:set_speed(speed or 10)
ret:set_extra_space(extra_space or 0)
ret:set_expand(expand)
ret:set_max_size(max_size)
ret:set_step_function(step_function or scroll.step_functions.linear_increase)
ret:set_space_for_scrolling(space_for_scrolling or 2^1024)
return ret
end
--- Get a new horizontal scrolling container.
-- @param[opt] widget The widget that should be scrolled
-- @param[opt=20] fps The number of frames per second
-- @param[opt=10] speed The speed of the animation
-- @param[opt=0] extra_space The amount of extra space to include
-- @tparam[opt=false] boolean expand Should the widget be expanded to include the
-- extra space?
-- @param[opt] max_size The maximum size of the child widget
-- @param[opt=step_functions.linear_increase] step_function The step function to be used
-- @param[opt=2^1024] space_for_scrolling The space for scrolling
function scroll.horizontal(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
return get_layout("h", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
end
--- Get a new vertical scrolling container.
-- @param[opt] widget The widget that should be scrolled
-- @param[opt=20] fps The number of frames per second
-- @param[opt=10] speed The speed of the animation
-- @param[opt=0] extra_space The amount of extra space to include
-- @tparam[opt=false] boolean expand Should the widget be expanded to include the
-- extra space?
-- @param[opt] max_size The maximum size of the child widget
-- @param[opt=step_functions.linear_increase] step_function The step function to be used
-- @param[opt=2^1024] space_for_scrolling The space for scrolling
function scroll.vertical(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
return get_layout("v", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
end
--- A selection of step functions
-- @see set_step_function
scroll.step_functions = {}
--- A step function that scrolls the widget in an increasing direction with
-- constant speed.
function scroll.step_functions.linear_increase(elapsed, size, _, speed, extra_space)
return (elapsed * speed) % (size + extra_space)
end
--- A step function that scrolls the widget in an decreasing direction with
-- constant speed.
function scroll.step_functions.linear_decrease(elapsed, size, _, speed, extra_space)
return (-elapsed * speed) % (size + extra_space)
end
--- A step function that scrolls the widget to its end and back to its
-- beginning, then back to its end, etc. The speed is constant.
function scroll.step_functions.linear_back_and_forth(elapsed, size, visible_size, speed)
local state = ((elapsed * speed) % (2 * size)) / size
state = state <= 1 and state or 2 - state
return (size - visible_size) * state
end
--- A step function that scrolls the widget to its end and back to its
-- beginning, then back to its end, etc. The speed is null at the ends and
-- maximal in the middle.
function scroll.step_functions.nonlinear_back_and_forth(elapsed, size, visible_size, speed)
local state = ((elapsed * speed) % (2 * size)) / size
local negate = false
if state > 1 then
negate = true
state = state - 1
end
if state < 1/3 then
-- In the first 1/3rd of time, do a quadratic increase in speed
state = 2 * state * state
elseif state < 2/3 then
-- In the center, do a linear increase. That means we need:
-- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3
-- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3)
state = 5/3*state - 3/9
else
-- In the last 1/3rd of time, do a quadratic decrease in speed
state = 1 - 2 * (1 - state) * (1 - state)
end
if negate then
state = 1 - state
end
return (size - visible_size) * state
end
--- A step function that scrolls the widget to its end and back to its
-- beginning, then back to its end, etc. The speed is null at the ends and
-- maximal in the middle. At both ends the widget stands still for a moment.
function scroll.step_functions.waiting_nonlinear_back_and_forth(elapsed, size, visible_size, speed)
local state = ((elapsed * speed) % (2 * size)) / size
local negate = false
if state > 1 then
negate = true
state = state - 1
end
if state < 1/5 or state > 4/5 then
-- One fifth of time, nothing moves
state = state < 1/5 and 0 or 1
else
state = (state - 1/5) * 5/3
if state < 1/3 then
-- In the first 1/3rd of time, do a quadratic increase in speed
state = 2 * state * state
elseif state < 2/3 then
-- In the center, do a linear increase. That means we need:
-- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3
-- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3)
state = 5/3*state - 3/9
else
-- In the last 1/3rd of time, do a quadratic decrease in speed
state = 1 - 2 * (1 - state) * (1 - state)
end
end
if negate then
state = 1 - state
end
return (size - visible_size) * state
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return scroll
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -259,7 +259,7 @@ end
-- @param cr The cairo context that is used for drawing.
function hierarchy:draw(context, cr)
local widget = self:get_widget()
if not widget.visible then
if not widget._private.visible then
return
end
@ -272,7 +272,7 @@ function hierarchy:draw(context, cr)
-- Draw if needed
if not empty_clip(cr) then
local opacity = widget.opacity
local opacity = widget:get_opacity()
local function call(func, extra_arg1, extra_arg2)
if not func then return end
if not extra_arg2 then

View File

@ -24,6 +24,7 @@ local base = require("wibox.widget.base")
-- wibox
local wibox = { mt = {}, object = {} }
wibox.layout = require("wibox.layout")
wibox.container = require("wibox.container")
wibox.widget = require("wibox.widget")
wibox.drawable = require("wibox.drawable")
wibox.hierarchy = require("wibox.hierarchy")

View File

@ -1,4 +1,6 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_layout_defaults_align_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
@ -14,7 +16,7 @@ local base = require("wibox.widget.base")
local align = {}
--- Calculate the layout of an align layout.
-- Calculate the layout of an align layout.
-- @param context The context in which we are drawn.
-- @param width The available width.
-- @param height The available height.
@ -30,7 +32,7 @@ function align:layout(context, width, height)
-- allowed.
local size_first = 0
-- start with all the space given by the parent, subtract as we go along
local size_remains = self.dir == "y" and height or width
local size_remains = self._private.dir == "y" and height or width
-- This is only set & used if expand ~= "inside" and we have second width.
-- It contains the size allocated to the second widget.
local size_second
@ -39,84 +41,84 @@ function align:layout(context, width, height)
-- if it is, we prioritize the first widget by not doing this block also,
-- if the second widget doesn't exist, we will prioritise the first one
-- instead
if self._expand ~= "inside" and self.second then
local w, h = base.fit_widget(self, context, self.second, width, height)
size_second = self.dir == "y" and h or w
if self._private.expand ~= "inside" and self._private.second then
local w, h = base.fit_widget(self, context, self._private.second, width, height)
size_second = self._private.dir == "y" and h or w
-- if all the space is taken, skip the rest, and draw just the middle
-- widget
if size_second >= size_remains then
return { base.place_widget_at(self.second, 0, 0, width, height) }
return { base.place_widget_at(self._private.second, 0, 0, width, height) }
else
-- the middle widget is sized first, the outside widgets are given
-- the remaining space if available we will draw later
size_remains = floor((size_remains - size_second) / 2)
end
end
if self.first then
if self._private.first then
local w, h, _ = width, height, nil
-- we use the fit function for the "inside" and "none" modes, but
-- ignore it for the "outside" mode, which will force it to expand
-- into the remaining space
if self._expand ~= "outside" then
if self.dir == "y" then
_, h = base.fit_widget(self, context, self.first, width, size_remains)
if self._private.expand ~= "outside" then
if self._private.dir == "y" then
_, h = base.fit_widget(self, context, self._private.first, width, size_remains)
size_first = h
-- for "inside", the third widget will get a chance to use the
-- remaining space, then the middle widget. For "none" we give
-- the third widget the remaining space if there was no second
-- widget to take up any space (as the first if block is skipped
-- if this is the case)
if self._expand == "inside" or not self.second then
if self._private.expand == "inside" or not self._private.second then
size_remains = size_remains - h
end
else
w, _ = base.fit_widget(self, context, self.first, size_remains, height)
w, _ = base.fit_widget(self, context, self._private.first, size_remains, height)
size_first = w
if self._expand == "inside" or not self.second then
if self._private.expand == "inside" or not self._private.second then
size_remains = size_remains - w
end
end
else
if self.dir == "y" then
if self._private.dir == "y" then
h = size_remains
else
w = size_remains
end
end
table.insert(result, base.place_widget_at(self.first, 0, 0, w, h))
table.insert(result, base.place_widget_at(self._private.first, 0, 0, w, h))
end
-- size_remains will be <= 0 if first used all the space
if self.third and size_remains > 0 then
if self._private.third and size_remains > 0 then
local w, h, _ = width, height, nil
if self._expand ~= "outside" then
if self.dir == "y" then
_, h = base.fit_widget(self, context, self.third, width, size_remains)
if self._private.expand ~= "outside" then
if self._private.dir == "y" then
_, h = base.fit_widget(self, context, self._private.third, width, size_remains)
-- give the middle widget the rest of the space for "inside" mode
if self._expand == "inside" then
if self._private.expand == "inside" then
size_remains = size_remains - h
end
else
w, _ = base.fit_widget(self, context, self.third, size_remains, height)
if self._expand == "inside" then
w, _ = base.fit_widget(self, context, self._private.third, size_remains, height)
if self._private.expand == "inside" then
size_remains = size_remains - w
end
end
else
if self.dir == "y" then
if self._private.dir == "y" then
h = size_remains
else
w = size_remains
end
end
local x, y = width - w, height - h
table.insert(result, base.place_widget_at(self.third, x, y, w, h))
table.insert(result, base.place_widget_at(self._private.third, x, y, w, h))
end
-- here we either draw the second widget in the space set aside for it
-- in the beginning, or in the remaining space, if it is "inside"
if self.second and size_remains > 0 then
if self._private.second and size_remains > 0 then
local x, y, w, h = 0, 0, width, height
if self._expand == "inside" then
if self.dir == "y" then
if self._private.expand == "inside" then
if self._private.dir == "y" then
h = size_remains
x, y = 0, size_first
else
@ -125,62 +127,76 @@ function align:layout(context, width, height)
end
else
local _
if self.dir == "y" then
_, h = base.fit_widget(self, context, self.second, width, size_second)
if self._private.dir == "y" then
_, h = base.fit_widget(self, context, self._private.second, width, size_second)
y = floor( (height - h)/2 )
else
w, _ = base.fit_widget(self, context, self.second, size_second, height)
w, _ = base.fit_widget(self, context, self._private.second, size_second, height)
x = floor( (width -w)/2 )
end
end
table.insert(result, base.place_widget_at(self.second, x, y, w, h))
table.insert(result, base.place_widget_at(self._private.second, x, y, w, h))
end
return result
end
--- Set the layout's first widget. This is the widget that is at the left/top
--- Set the layout's first widget.
-- This is the widget that is at the left/top
-- @property first
function align:set_first(widget)
if self.first == widget then
if self._private.first == widget then
return
end
self.first = widget
self._private.first = widget
self:emit_signal("widget::layout_changed")
end
--- Set the layout's second widget. This is the centered one.
-- @property second
function align:set_second(widget)
if self.second == widget then
if self._private.second == widget then
return
end
self.second = widget
self._private.second = widget
self:emit_signal("widget::layout_changed")
end
--- Set the layout's third widget. This is the widget that is at the right/bottom
--- Set the layout's third widget.
-- This is the widget that is at the right/bottom
-- @property third
function align:set_third(widget)
if self.third == widget then
if self._private.third == widget then
return
end
self.third = widget
self._private.third = widget
self:emit_signal("widget::layout_changed")
end
--- Get all children of this layout
-- @treturn table a list of all widgets
function align:get_children()
return util.from_sparse {self.first, self.second, self.third}
for _, prop in ipairs {"first", "second", "third", "expand" } do
align["get_"..prop] = function(self)
return self._private[prop]
end
end
--- All direct children of this layout.
-- This can be used to replace all 3 widgets at once.
-- @treturn table a list of all widgets
-- @property children
function align:get_children()
return util.from_sparse {self._private.first, self._private.second, self._private.third}
end
--- Replace the layout children
-- This layout only accept three children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function align:set_children(children)
self:set_first(children[1])
self:set_second(children[2])
self:set_third(children[3])
end
--- Fit the align layout into the given space. The align layout will
-- Fit the align layout into the given space. The align layout will
-- ask for the sum of the sizes of its sub-widgets in its direction
-- and the largest sized sub widget in the other direction.
-- @param context The context in which we are fit.
@ -190,18 +206,18 @@ function align:fit(context, orig_width, orig_height)
local used_in_dir = 0
local used_in_other = 0
for _, v in pairs{self.first, self.second, self.third} do
for _, v in pairs{self._private.first, self._private.second, self._private.third} do
local w, h = base.fit_widget(self, context, v, orig_width, orig_height)
local max = self.dir == "y" and w or h
local max = self._private.dir == "y" and w or h
if max > used_in_other then
used_in_other = max
end
used_in_dir = used_in_dir + (self.dir == "y" and h or w)
used_in_dir = used_in_dir + (self._private.dir == "y" and h or w)
end
if self.dir == "y" then
if self._private.dir == "y" then
return used_in_other, used_in_dir
end
return used_in_dir, used_in_other
@ -221,11 +237,13 @@ end
-- * "none" - All widgets are sized using their fit function, drawn to only the
-- returned space, or remaining space, whichever is smaller. Center widget
-- gets priority.
-- @property expand
function align:set_expand(mode)
if mode == "none" or mode == "outside" then
self._expand = mode
self._private.expand = mode
else
self._expand = "inside"
self._private.expand = "inside"
end
self:emit_signal("widget::layout_changed")
end
@ -238,12 +256,12 @@ function align:reset()
end
local function get_layout(dir, first, second, third)
local ret = base.make_widget()
ret.dir = dir
local ret = base.make_widget(nil, nil, {enable_properties = true})
ret._private.dir = dir
for k, v in pairs(align) do
if type(v) == "function" then
ret[k] = v
rawset(ret, k, v)
end
end
@ -268,9 +286,9 @@ end
function align.horizontal(left, middle, right)
local ret = get_layout("x", left, middle, right)
ret.set_left = ret.set_first
ret.set_middle = ret.set_second
ret.set_right = ret.set_third
rawset(ret, "set_left" , ret.set_first )
rawset(ret, "set_middle", ret.set_second )
rawset(ret, "set_right" , ret.set_third )
return ret
end
@ -285,13 +303,17 @@ end
function align.vertical(top, middle, bottom)
local ret = get_layout("y", top, middle, bottom)
ret.set_top = ret.set_first
ret.set_middle = ret.set_second
ret.set_bottom = ret.set_third
rawset(ret, "set_top" , ret.set_first )
rawset(ret, "set_middle", ret.set_second )
rawset(ret, "set_bottom", ret.set_third )
return ret
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return align
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,140 +1,18 @@
---------------------------------------------------------------------------
-- This class has been moved to `wibox.container.`
--
-- @author Lukáš Hrázký
-- @copyright 2012 Lukáš Hrázký
-- @release @AWESOME_VERSION@
-- @classmod wibox.layout.constraint
---------------------------------------------------------------------------
local pairs = pairs
local type = type
local setmetatable = setmetatable
local base = require("wibox.widget.base")
local math = math
local util = require("awful.util")
local constraint = { mt = {} }
--- Layout a constraint layout
function constraint:layout(_, width, height)
if self.widget then
return { base.place_widget_at(self.widget, 0, 0, width, height) }
end
end
--- Fit a constraint layout into the given space
function constraint:fit(context, width, height)
local w, h
if self.widget then
w = self._strategy(width, self._width)
h = self._strategy(height, self._height)
w, h = base.fit_widget(self, context, self.widget, w, h)
else
w, h = 0, 0
end
w = self._strategy(w, self._width)
h = self._strategy(h, self._height)
return w, h
end
--- Set the widget that this layout adds a constraint on.
function constraint:set_widget(widget)
self.widget = widget
self:emit_signal("widget::layout_changed")
end
--- Get the number of children element
-- @treturn table The children
function constraint:get_children()
return {self.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function constraint:set_children(children)
self:set_widget(children[1])
end
--- Set the strategy to use for the constraining. Valid values are 'max',
-- 'min' or 'exact'. Throws an error on invalid values.
function constraint:set_strategy(val)
local func = {
min = function(real_size, limit)
return limit and math.max(limit, real_size) or real_size
end,
max = function(real_size, limit)
return limit and math.min(limit, real_size) or real_size
end,
exact = function(real_size, limit)
return limit or real_size
end
}
if not func[val] then
error("Invalid strategy for constraint layout: " .. tostring(val))
end
self._strategy = func[val]
self:emit_signal("widget::layout_changed")
end
--- Set the maximum width to val. nil for no width limit.
function constraint:set_width(val)
self._width = val
self:emit_signal("widget::layout_changed")
end
--- Set the maximum height to val. nil for no height limit.
function constraint:set_height(val)
self._height = val
self:emit_signal("widget::layout_changed")
end
--- Reset this layout. The widget will be unreferenced, strategy set to "max"
-- and the constraints set to nil.
function constraint:reset()
self._width = nil
self._height = nil
self:set_strategy("max")
self:set_widget(nil)
end
--- Returns a new constraint layout. This layout will constraint the size of a
-- widget according to the strategy. Note that this will only work for layouts
-- that respect the widget's size, eg. fixed layout. In layouts that don't
-- (fully) respect widget's requested size, the inner widget still might get
-- drawn with a size that does not fit the constraint, eg. in flex layout.
-- @param[opt] widget A widget to use.
-- @param[opt] strategy How to constraint the size. 'max' (default), 'min' or
-- 'exact'.
-- @param[opt] width The maximum width of the widget. nil for no limit.
-- @param[opt] height The maximum height of the widget. nil for no limit.
local function new(widget, strategy, width, height)
local ret = base.make_widget()
for k, v in pairs(constraint) do
if type(v) == "function" then
ret[k] = v
end
end
ret:set_strategy(strategy or "max")
ret:set_width(width)
ret:set_height(height)
if widget then
ret:set_widget(widget)
end
return ret
end
function constraint.mt:__call(...)
return new(...)
end
return setmetatable(constraint, constraint.mt)
return util.deprecate_class(
require("wibox.container.constraint"),
"wibox.layout.constraint",
"wibox.container.constraint"
)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,4 +1,6 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_layout_defaults_fixed_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
@ -21,28 +23,28 @@ local fixed = {}
-- @param height The available height.
function fixed:layout(context, width, height)
local result = {}
local pos,spacing = 0, self._spacing
local pos,spacing = 0, self._private.spacing
for k, v in pairs(self.widgets) do
for k, v in pairs(self._private.widgets) do
local x, y, w, h, _
if self.dir == "y" then
if self._private.dir == "y" then
x, y = 0, pos
w, h = width, height - pos
if k ~= #self.widgets or not self._fill_space then
if k ~= #self._private.widgets or not self._private.fill_space then
_, h = base.fit_widget(self, context, v, w, h);
end
pos = pos + h + spacing
else
x, y = pos, 0
w, h = width - pos, height
if k ~= #self.widgets or not self._fill_space then
if k ~= #self._private.widgets or not self._private.fill_space then
w, _ = base.fit_widget(self, context, v, w, h);
end
pos = pos + w + spacing
end
if (self.dir == "y" and pos-spacing > height) or
(self.dir ~= "y" and pos-spacing > width) then
if (self._private.dir == "y" and pos-spacing > height) or
(self._private.dir ~= "y" and pos-spacing > width) then
break
end
table.insert(result, base.place_widget_at(v, x, y, w, h))
@ -58,7 +60,7 @@ function fixed:add(...)
assert(args.n > 0, "need at least one widget to add")
for i=1, args.n do
base.check_widget(args[i])
table.insert(self.widgets, args[i])
table.insert(self._private.widgets, args[i])
end
self:emit_signal("widget::layout_changed")
end
@ -68,9 +70,9 @@ end
-- @tparam number index The widget index to remove
-- @treturn boolean index If the operation is successful
function fixed:remove(index)
if not index or index < 1 or index > #self.widgets then return false end
if not index or index < 1 or index > #self._private.widgets then return false end
table.remove(self.widgets, index)
table.remove(self._private.widgets, index)
self:emit_signal("widget::layout_changed")
@ -104,14 +106,10 @@ function fixed:remove_widgets(...)
return #args > (recursive and 1 or 0) and ret
end
--- Get all children of this layout
-- @treturn table a list of all widgets
function fixed:get_children()
return self.widgets
return self._private.widgets
end
--- Replace the layout children
-- @tparam table children A table composed of valid widgets
function fixed:set_children(children)
self:reset()
if #children > 0 then
@ -136,12 +134,12 @@ function fixed:replace_widget(widget, widget2, recursive)
end
function fixed:swap(index1, index2)
if not index1 or not index2 or index1 > #self.widgets
or index2 > #self.widgets then
if not index1 or not index2 or index1 > #self._private.widgets
or index2 > #self._private.widgets then
return false
end
local widget1, widget2 = self.widgets[index1], self.widgets[index2]
local widget1, widget2 = self._private.widgets[index1], self._private.widgets[index2]
self:set(index1, widget2)
self:set(index2, widget1)
@ -175,11 +173,11 @@ function fixed:swap_widgets(widget1, widget2, recursive)
end
function fixed:set(index, widget2)
if (not widget2) or (not self.widgets[index]) then return false end
if (not widget2) or (not self._private.widgets[index]) then return false end
base.check_widget(widget2)
self.widgets[index] = widget2
self._private.widgets[index] = widget2
self:emit_signal("widget::layout_changed")
@ -191,10 +189,10 @@ end
-- @param widget The widget
-- @treturn boolean If the operation is successful
function fixed:insert(index, widget)
if not index or index < 1 or index > #self.widgets + 1 then return false end
if not index or index < 1 or index > #self._private.widgets + 1 then return false end
base.check_widget(widget)
table.insert(self.widgets, index, widget)
table.insert(self._private.widgets, index, widget)
self:emit_signal("widget::layout_changed")
return true
@ -208,10 +206,10 @@ function fixed:fit(context, orig_width, orig_height)
local width, height = orig_width, orig_height
local used_in_dir, used_max = 0, 0
for _, v in pairs(self.widgets) do
for _, v in pairs(self._private.widgets) do
local w, h = base.fit_widget(self, context, v, width, height)
local in_dir, max
if self.dir == "y" then
if self._private.dir == "y" then
max, in_dir = w, h
height = height - in_dir
else
@ -224,7 +222,7 @@ function fixed:fit(context, orig_width, orig_height)
used_in_dir = used_in_dir + in_dir
if width <= 0 or height <= 0 then
if self.dir == "y" then
if self._private.dir == "y" then
used_in_dir = orig_height
else
used_in_dir = orig_width
@ -233,36 +231,38 @@ function fixed:fit(context, orig_width, orig_height)
end
end
local spacing = self._spacing * (#self.widgets-1)
local spacing = self._private.spacing * (#self._private.widgets-1)
if self.dir == "y" then
if self._private.dir == "y" then
return used_max, used_in_dir + spacing
end
return used_in_dir + spacing, used_max
end
function fixed:reset()
self.widgets = {}
self._private.widgets = {}
self:emit_signal("widget::layout_changed")
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.
-- @property fill_space
function fixed:fill_space(val)
if self._fill_space ~= val then
self._fill_space = not not val
if self._private.fill_space ~= val then
self._private.fill_space = not not val
self:emit_signal("widget::layout_changed")
end
end
local function get_layout(dir, widget1, ...)
local ret = base.make_widget()
local ret = base.make_widget(nil, nil, {enable_properties = true})
util.table.crush(ret, fixed)
util.table.crush(ret, fixed, true)
ret.dir = dir
ret.widgets = {}
ret._private.dir = dir
ret._private.widgets = {}
ret:set_spacing(0)
ret:fill_space(false)
@ -292,14 +292,24 @@ function fixed.vertical(...)
end
--- Add spacing between each layout widgets
-- @param spacing Spacing between widgets.
-- @property spacing
-- @tparam number spacing Spacing between widgets.
function fixed:set_spacing(spacing)
if self._spacing ~= spacing then
self._spacing = spacing
if self._private.spacing ~= spacing then
self._private.spacing = spacing
self:emit_signal("widget::layout_changed")
end
end
function fixed:get_spacing()
return self._private.spacing or 0
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return fixed
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,4 +1,6 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_layout_defaults_flex_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
@ -50,24 +52,24 @@ local flex = {}
function flex:layout(_, width, height)
local result = {}
local pos,spacing = 0, self._spacing
local num = #self.widgets
local pos,spacing = 0, self._private.spacing
local num = #self._private.widgets
local total_spacing = (spacing*(num-1))
local space_per_item
if self.dir == "y" then
if self._private.dir == "y" then
space_per_item = height / num - total_spacing/num
else
space_per_item = width / num - total_spacing/num
end
if self._max_widget_size then
space_per_item = math.min(space_per_item, self._max_widget_size)
if self._private.max_widget_size then
space_per_item = math.min(space_per_item, self._private.max_widget_size)
end
for _, v in pairs(self.widgets) do
for _, v in pairs(self._private.widgets) do
local x, y, w, h
if self.dir == "y" then
if self._private.dir == "y" then
x, y = 0, util.round(pos)
w, h = width, floor(space_per_item)
else
@ -79,8 +81,8 @@ function flex:layout(_, width, height)
pos = pos + space_per_item + spacing
if (self.dir == "y" and pos-spacing >= height) or
(self.dir ~= "y" and pos-spacing >= width) then
if (self._private.dir == "y" and pos-spacing >= height) or
(self._private.dir ~= "y" and pos-spacing >= width) then
break
end
end
@ -97,39 +99,41 @@ function flex:fit(context, orig_width, orig_height)
local used_in_other = 0
-- Figure out the maximum size we can give out to sub-widgets
local sub_height = self.dir == "x" and orig_height or orig_height / #self.widgets
local sub_width = self.dir == "y" and orig_width or orig_width / #self.widgets
local sub_height = self._private.dir == "x" and orig_height or orig_height / #self._private.widgets
local sub_width = self._private.dir == "y" and orig_width or orig_width / #self._private.widgets
for _, v in pairs(self.widgets) do
for _, v in pairs(self._private.widgets) do
local w, h = base.fit_widget(self, context, v, sub_width, sub_height)
local max = self.dir == "y" and w or h
local max = self._private.dir == "y" and w or h
if max > used_in_other then
used_in_other = max
end
used_in_dir = used_in_dir + (self.dir == "y" and h or w)
used_in_dir = used_in_dir + (self._private.dir == "y" and h or w)
end
if self._max_widget_size then
if self._private.max_widget_size then
used_in_dir = math.min(used_in_dir,
#self.widgets * self._max_widget_size)
#self._private.widgets * self._private.max_widget_size)
end
local spacing = self._spacing * (#self.widgets-1)
local spacing = self._private.spacing * (#self._private.widgets-1)
if self.dir == "y" then
if self._private.dir == "y" then
return used_in_other, used_in_dir + spacing
end
return used_in_dir + spacing, used_in_other
end
--- Set the maximum size the widgets in this layout will take (that is,
-- maximum width for horizontal and maximum height for vertical).
-- @param val The maximum size of the widget.
--- Set the maximum size the widgets in this layout will take.
--That is, maximum width for horizontal and maximum height for vertical.
-- @property max_widget_size
-- @param number
function flex:set_max_widget_size(val)
if self._max_widget_size ~= val then
self._max_widget_size = val
if self._private.max_widget_size ~= val then
self._private.max_widget_size = val
self:emit_signal("widget::layout_changed")
end
end
@ -137,9 +141,9 @@ end
local function get_layout(dir, widget1, ...)
local ret = fixed[dir](widget1, ...)
util.table.crush(ret, flex)
util.table.crush(ret, flex, true)
ret.fill_space = nil
ret._private.fill_space = nil
return ret
end
@ -160,6 +164,10 @@ function flex.vertical(...)
return get_layout("vertical", ...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return flex
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,198 +1,18 @@
---------------------------------------------------------------------------
-- This class has been moved to `wibox.container.margin`
--
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.layout.margin
---------------------------------------------------------------------------
local pairs = pairs
local type = type
local setmetatable = setmetatable
local base = require("wibox.widget.base")
local gcolor = require("gears.color")
local cairo = require("lgi").cairo
local util = require("awful.util")
local margin = { mt = {} }
-- Draw a margin layout
function margin:draw(_, cr, width, height)
local x = self.left
local y = self.top
local w = self.right
local h = self.bottom
local color = self.color
if not self.widget or width <= x + w or height <= y + h then
return
end
if color then
cr:set_source(color)
cr:rectangle(0, 0, width, height)
cr:rectangle(x, y, width - x - w, height - y - h)
cr:set_fill_rule(cairo.FillRule.EVEN_ODD)
cr:fill()
end
end
-- Layout a margin layout
function margin:layout(_, width, height)
if self.widget then
local x = self.left
local y = self.top
local w = self.right
local h = self.bottom
return { base.place_widget_at(self.widget, x, y, width - x - w, height - y - h) }
end
end
-- Fit a margin layout into the given space
function margin:fit(context, width, height)
local extra_w = self.left + self.right
local extra_h = self.top + self.bottom
local w, h = 0, 0
if self.widget then
w, h = base.fit_widget(self, context, self.widget, width - extra_w, height - extra_h)
end
if self._draw_empty == false and (w == 0 or h == 0) then
return 0, 0
end
return w + extra_w, h + extra_h
end
--- Set the widget that this layout adds a margin on.
function margin:set_widget(widget)
if widget then
base.check_widget(widget)
end
self.widget = widget
self:emit_signal("widget::layout_changed")
end
-- Get the number of children element
-- @treturn table The children
function margin:get_children()
return {self.widget}
end
-- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function margin:set_children(children)
self:set_widget(children[1])
end
--- Set all the margins to val.
-- @tparam number val The margin value
function margin:set_margins(val)
if self.left == val and
self.right == val and
self.top == val and
self.bottom == val then
return
end
self.left = val
self.right = val
self.top = val
self.bottom = val
self:emit_signal("widget::layout_changed")
end
--- Set the margins color to create a border.
-- @param color A color used to fill the margin.
function margin:set_color(color)
self.color = color and gcolor(color)
self:emit_signal("widget::redraw_needed")
end
--- Draw the margin even if the content size is 0x0 (default: true)
-- @tparam boolean draw_empty Draw nothing is content is 0x0 or draw the margin anyway
function margin:set_draw_empty(draw_empty)
self._draw_empty = draw_empty
self:emit_signal("widget::layout_changed")
end
--- Reset this layout. The widget will be unreferenced, the margins set to 0
-- and the color erased
function margin:reset()
self:set_widget(nil)
self:set_margins(0)
self:set_color(nil)
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 _, v in pairs({ "left", "right", "top", "bottom" }) do
margin["set_" .. v] = function(layout, val)
if layout[v] == val then return end
layout[v] = val
layout:emit_signal("widget::layout_changed")
end
end
--- Returns a new margin layout.
-- @param[opt] widget A widget to use.
-- @param[opt] left A margin to use on the left side of the widget.
-- @param[opt] right A margin to use on the right side of the widget.
-- @param[opt] top A margin to use on the top side of the widget.
-- @param[opt] bottom A margin to use on the bottom side of the widget.
-- @param[opt] color A color for the margins.
-- @param[opt] draw_empty whether or not to draw the margin when the content is empty
local function new(widget, left, right, top, bottom, color, draw_empty)
local ret = base.make_widget()
for k, v in pairs(margin) do
if type(v) == "function" then
ret[k] = v
end
end
ret:set_left(left or 0)
ret:set_right(right or 0)
ret:set_top(top or 0)
ret:set_bottom(bottom or 0)
ret:set_draw_empty(draw_empty)
ret:set_color(color)
if widget then
ret:set_widget(widget)
end
return ret
end
function margin.mt:__call(...)
return new(...)
end
return setmetatable(margin, margin.mt)
return util.deprecate_class(
require("wibox.container.margin"),
"wibox.layout.margin",
"wibox.container.margin"
)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,127 +1,18 @@
---------------------------------------------------------------------------
-- This class has been moved to `wibox.container.mirror`
--
-- @author dodo
-- @copyright 2012 dodo
-- @release @AWESOME_VERSION@
-- @classmod wibox.layout.mirror
---------------------------------------------------------------------------
local type = type
local error = error
local pairs = pairs
local ipairs = ipairs
local setmetatable = setmetatable
local base = require("wibox.widget.base")
local matrix = require("gears.matrix")
local util = require("awful.util")
local mirror = { mt = {} }
--- Layout this layout
function mirror:layout(_, width, height)
if not self.widget then return end
local m = matrix.identity
local t = { x = 0, y = 0 } -- translation
local s = { x = 1, y = 1 } -- scale
if self.horizontal then
t.x = width
s.x = -1
end
if self.vertical then
t.y = height
s.y = -1
end
m = m:translate(t.x, t.y)
m = m:scale(s.x, s.y)
return { base.place_widget_via_matrix(self.widget, m, width, height) }
end
--- Fit this layout into the given area
function mirror:fit(context, ...)
if not self.widget then
return 0, 0
end
return base.fit_widget(self, context, self.widget, ...)
end
--- Set the widget that this layout mirrors.
-- @param widget The widget to mirror
function mirror:set_widget(widget)
if widget then
base.check_widget(widget)
end
self.widget = widget
self:emit_signal("widget::layout_changed")
end
--- Get the number of children element
-- @treturn table The children
function mirror:get_children()
return {self.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function mirror:set_children(children)
self:set_widget(children[1])
end
--- Reset this layout. The widget will be removed and the axes reset.
function mirror:reset()
self.horizontal = false
self.vertical = false
self:set_widget(nil)
end
--- Set the reflection of this mirror layout.
-- @param reflection a table which contains new values for horizontal and/or vertical (booleans)
function mirror:set_reflection(reflection)
if type(reflection) ~= 'table' then
error("Invalid type of reflection for mirror layout: " ..
type(reflection) .. " (should be a table)")
end
for _, ref in ipairs({"horizontal", "vertical"}) do
if reflection[ref] ~= nil then
self[ref] = reflection[ref]
end
end
self:emit_signal("widget::layout_changed")
end
--- Get the reflection of this mirror layout.
-- @return a table of booleans with the keys "horizontal", "vertical".
function mirror:get_reflection()
return { horizontal = self.horizontal, vertical = self.vertical }
end
--- Returns a new mirror layout. A mirror layout mirrors a given widget. Use
-- :set_widget() to set the widget and
-- :set_horizontal() and :set_vertical() for the direction.
-- horizontal and vertical are by default false which doesn't change anything.
-- @param[opt] widget The widget to display.
-- @param[opt] reflection A table describing the reflection to apply.
local function new(widget, reflection)
local ret = base.make_widget()
ret.horizontal = false
ret.vertical = false
for k, v in pairs(mirror) do
if type(v) == "function" then
ret[k] = v
end
end
ret:set_widget(widget)
ret:set_reflection(reflection or {})
return ret
end
function mirror.mt:__call(...)
return new(...)
end
return setmetatable(mirror, mirror.mt)
return util.deprecate_class(
require("wibox.container.mirror"),
"wibox.layout.mirror",
"wibox.container.mirror"
)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -2,6 +2,8 @@
--- A layout filling all the available space. Each widget is assigned a
-- ratio (percentage) of the total space. Multiple methods are available to
-- ajust this ratio.
--
--@DOC_wibox_layout_defaults_ratio_EXAMPLE@
-- @author Emmanuel Lepage Vallee
-- @copyright 2016 Emmanuel Lepage Vallee
-- @release @AWESOME_VERSION@
@ -23,9 +25,9 @@ local ratio = {}
local function gen_sum(self, i_s, i_e)
local sum, new_w = 0,0
for i = i_s or 1, i_e or #self.widgets do
if self._ratios[i] then
sum = sum + self._ratios[i]
for i = i_s or 1, i_e or #self._private.widgets do
if self._private.ratios[i] then
sum = sum + self._private.ratios[i]
else
new_w = new_w + 1
end
@ -40,24 +42,24 @@ end
-- specific ratio must be enforced for a widget, it has to be done with the
-- `ajust_ratio` method after each insertion or deletion
local function normalize(self)
local count = #self.widgets
local count = #self._private.widgets
if count == 0 then return end
-- Instead of adding "if" everywhere, just handle this common case
if count == 1 then
self._ratios = { 1 }
self._private.ratios = { 1 }
return
end
local sum, new_w = gen_sum(self)
local old_count = #self.widgets - new_w
local old_count = #self._private.widgets - new_w
local to_add = (sum == 0) and 1 or (sum / old_count)
-- Make sure all widgets have a ratio
for i=1, #self.widgets do
if not self._ratios[i] then
self._ratios[i] = to_add
for i=1, #self._private.widgets do
if not self._private.ratios[i] then
self._private.ratios[i] = to_add
end
end
@ -66,9 +68,9 @@ local function normalize(self)
local delta, new_sum = (1 - sum) / count,0
-- Increase or decrease each ratio so it the sum become 1
for i=1, #self.widgets do
self._ratios[i] = self._ratios[i] + delta
new_sum = new_sum + self._ratios[i]
for i=1, #self._private.widgets do
self._private.ratios[i] = self._private.ratios[i] + delta
new_sum = new_sum + self._private.ratios[i]
end
-- Floating points is not an exact science, but it should still be close
@ -78,18 +80,18 @@ end
function ratio:layout(_, width, height)
local result = {}
local pos,spacing = 0, self._spacing
local pos,spacing = 0, self._private.spacing
for k, v in ipairs(self.widgets) do
for k, v in ipairs(self._private.widgets) do
local space
local x, y, w, h
if self.dir == "y" then
space = height * self._ratios[k]
if self._private.dir == "y" then
space = height * self._private.ratios[k]
x, y = 0, util.round(pos)
w, h = width, floor(space)
else
space = width * self._ratios[k]
space = width * self._private.ratios[k]
x, y = util.round(pos), 0
w, h = floor(space), height
end
@ -100,8 +102,8 @@ function ratio:layout(_, width, height)
-- Make sure all widgets fit in the layout, if they aren't, something
-- went wrong
if (self.dir == "y" and util.round(pos) >= height) or
(self.dir ~= "y" and util.round(pos) >= width) then
if (self._private.dir == "y" and util.round(pos) >= height) or
(self._private.dir ~= "y" and util.round(pos) >= width) then
break
end
end
@ -116,14 +118,14 @@ end
-- @tparam number increment An floating point value between -1 and 1 where the
-- end result is within 0 and 1
function ratio:inc_ratio(index, increment)
if #self.widgets == 1 or (not index) or (not self._ratios[index])
if #self._private.widgets == 1 or (not index) or (not self._private.ratios[index])
or increment < -1 or increment > 1 then
return
end
assert(self._ratios[index])
assert(self._private.ratios[index])
self:set_ratio(index, self._ratios[index] + increment)
self:set_ratio(index, self._private.ratios[index] + increment)
end
--- Increment the ratio of the first instance of `widget`
@ -144,22 +146,22 @@ end
-- @tparam number index The index of the widget to change
-- @tparam number percent An floating point value between 0 and 1
function ratio:set_ratio(index, percent)
if not percent or #self.widgets == 1 or not index or not self.widgets[index]
if not percent or #self._private.widgets == 1 or not index or not self._private.widgets[index]
or percent < 0 or percent > 1 then
return
end
local old = self._ratios[index]
local old = self._private.ratios[index]
-- Remove what has to be cleared from all widget
local delta = ( (percent-old) / (#self.widgets-1) )
local delta = ( (percent-old) / (#self._private.widgets-1) )
for k in pairs(self.widgets) do
self._ratios[k] = self._ratios[k] - delta
for k in pairs(self._private.widgets) do
self._private.ratios[k] = self._private.ratios[k] - delta
end
-- Set the new ratio
self._ratios[index] = percent
self._private.ratios[index] = percent
-- As some widgets may now have a slightly negative ratio, normalize again
normalize(self)
@ -172,7 +174,7 @@ end
-- @treturn number The index (between 0 and 1)
function ratio:get_ratio(index)
if not index then return end
return self._ratios[index]
return self._private.ratios[index]
end
--- Set the ratio of `widget` to `percent`.
@ -191,7 +193,7 @@ end
-- @tparam number itself The ratio for "widget"
-- @tparam number after The sum of the ratio after the widget
function ratio:ajust_ratio(index, before, itself, after)
if not self.widgets[index] or not before or not itself or not after then
if not self._private.widgets[index] or not before or not itself or not after then
return
end
@ -202,21 +204,21 @@ function ratio:ajust_ratio(index, before, itself, after)
if sum > 1.01 or sum < -0.99 then return end
-- Compute the before and after offset to be applied to each widgets
local before_count, after_count = index-1, #self.widgets - index
local before_count, after_count = index-1, #self._private.widgets - index
local b, a = gen_sum(self, 1, index-1), gen_sum(self, index+1)
local db, da = (before - b)/before_count, (after - a)/after_count
-- Apply the new ratio
self._ratios[index] = itself
self._private.ratios[index] = itself
-- Equality split the delta among widgets before and after
for i = 1, index -1 do
self._ratios[i] = self._ratios[i] + db
self._private.ratios[i] = self._private.ratios[i] + db
end
for i = index+1, #self.widgets do
self._ratios[i] = self._ratios[i] + da
for i = index+1, #self._private.widgets do
self._private.ratios[i] = self._private.ratios[i] + da
end
-- Remove potential negative ratio
@ -243,7 +245,7 @@ function ratio:add(...)
assert(args.n > 0, "need at least one widget to add")
for i=1, args.n do
base.check_widget(args[i])
table.insert(self.widgets, args[i])
table.insert(self._private.widgets, args[i])
end
normalize(self)
@ -254,10 +256,10 @@ end
-- @tparam number index The widget index to remove
-- @treturn boolean index If the operation is successful
function ratio:remove(index)
if not index or not self.widgets[index] then return false end
if not index or not self._private.widgets[index] then return false end
table.remove(self._ratios, index)
table.remove(self.widgets, index)
table.remove(self._private.ratios, index)
table.remove(self._private.widgets, index)
normalize(self)
@ -270,11 +272,11 @@ end
-- @tparam number index The position
-- @param widget The widget
function ratio:insert(index, widget)
if not index or index < 1 or index > #self.widgets + 1 then return false end
if not index or index < 1 or index > #self._private.widgets + 1 then return false end
base.check_widget(widget)
table.insert(self.widgets, index, widget)
table.insert(self._private.widgets, index, widget)
normalize(self)
@ -284,11 +286,11 @@ end
local function get_layout(dir, widget1, ...)
local ret = flex[dir](widget1, ...)
util.table.crush(ret, ratio)
util.table.crush(ret, ratio, true)
ret.fill_space = nil
ret._private.fill_space = nil
ret._ratios = {}
ret._private.ratios = {}
return ret
end
@ -307,6 +309,10 @@ function ratio.vertical(...)
return get_layout("vertical", ...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return ratio
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,137 +1,18 @@
---------------------------------------------------------------------------
-- This class has been moved to `wibox.container.rotate`
--
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.layout.rotate
---------------------------------------------------------------------------
local error = error
local pairs = pairs
local pi = math.pi
local type = type
local setmetatable = setmetatable
local tostring = tostring
local base = require("wibox.widget.base")
local matrix = require("gears.matrix")
local util = require("awful.util")
local rotate = { mt = {} }
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
--- Layout this layout
function rotate:layout(_, width, height)
if not self.widget or not self.widget.visible then
return
end
local dir = self:get_direction()
local m = matrix.identity
if dir == "west" then
m = m:rotate(pi / 2)
m = m:translate(0, -width)
elseif dir == "south" then
m = m:rotate(pi)
m = m:translate(-width, -height)
elseif dir == "east" then
m = m:rotate(3 * pi / 2)
m = m:translate(-height, 0)
end
-- Since we rotated, we might have to swap width and height.
-- transform() does that for us.
return { base.place_widget_via_matrix(self.widget, m, transform(self, width, height)) }
end
--- Fit this layout into the given area
function rotate:fit(context, width, height)
if not self.widget then
return 0, 0
end
return transform(self, base.fit_widget(self, context, self.widget, transform(self, width, height)))
end
--- Set the widget that this layout rotates.
function rotate:set_widget(widget)
if widget then
base.check_widget(widget)
end
self.widget = widget
self:emit_signal("widget::layout_changed")
end
--- Get the number of children element
-- @treturn table The children
function rotate:get_children()
return {self.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function rotate:set_children(children)
self:set_widget(children[1])
end
--- Reset this layout. The widget will be removed and the rotation reset.
function rotate:reset()
self.direction = nil
self: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 rotate:set_direction(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
self.direction = dir
self:emit_signal("widget::layout_changed")
end
--- Get the direction of this rotating layout
function rotate:get_direction()
return self.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.
-- @param[opt] widget The widget to display.
-- @param[opt] dir The direction to rotate to.
local function new(widget, dir)
local ret = base.make_widget()
for k, v in pairs(rotate) do
if type(v) == "function" then
ret[k] = v
end
end
ret:set_widget(widget)
ret:set_direction(dir or "north")
return ret
end
function rotate.mt:__call(...)
return new(...)
end
return setmetatable(rotate, rotate.mt)
return util.deprecate_class(
require("wibox.container.rotate"),
"wibox.layout.rotate",
"wibox.container.rotate"
)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,509 +1,17 @@
---------------------------------------------------------------------------
-- This class has been moved to `wibox.container.scroll`
--
-- @author Uli Schlachter (based on ideas from Saleur Geoffrey)
-- @copyright 2015 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.layout.scroll
---------------------------------------------------------------------------
local util = require("awful.util")
local cache = require("gears.cache")
local timer = require("gears.timer")
local hierarchy = require("wibox.hierarchy")
local base = require("wibox.widget.base")
local lgi = require("lgi")
local GLib = lgi.GLib
local scroll = {}
local scroll_mt = { __index = scroll }
local _need_scroll_redraw
-- "Strip" a context so that we can use it for our own drawing
local function cleanup_context(context)
local skip = { wibox = true, drawable = true, client = true, position = true }
local res = {}
for k, v in pairs(context) do
if not skip[k] then
res[k] = v
end
end
return res
end
-- Create a hierarchy (and some more stuff) for drawing the given widget. This
-- allows "some stuff" to be re-used instead of re-created all the time.
local hierarchy_cache = cache.new(function(context, widget, width, height)
context = cleanup_context(context)
local layouts = setmetatable({}, { __mode = "k" })
-- Create a widget hierarchy and update when needed
local hier
local function do_pending_updates(layout)
layouts[layout] = true
hier:update(context, widget, width, height, nil)
end
local function emit(signal)
-- Make the scroll layouts redraw
for w in pairs(layouts) do
w:emit_signal(signal)
end
end
local function redraw_callback()
emit("widget::redraw_needed")
end
local function layout_callback()
emit("widget::redraw_needed")
emit("widget::layout_changed")
end
hier = hierarchy.new(context, widget, width, height, redraw_callback, layout_callback, nil)
return hier, do_pending_updates, context
end)
--- Calculate all the information needed for scrolling.
-- @param self The instance of the scrolling layout.
-- @param context A widget context under which we are fit/drawn.
-- @param width The available width
-- @param height The available height
-- @return A table with the following entries
-- @field fit_width The width that should be returned from :fit
-- @field fit_height The height that should be returned from :fit
-- @field surface_width The width for showing the child widget
-- @field surface_height The height for showing the child widget
-- @field first_x The x offset for drawing the child the first time
-- @field first_y The y offset for drawing the child the first time
-- @field[opt] second_x The x offset for drawing the child the second time
-- @field[opt] second_y The y offset for drawing the child the second time
-- @field hierarchy The wibox.hierarchy instance representing "everything"
-- @field context The widget context for drawing the hierarchy
local function calculate_info(self, context, width, height)
local result = {}
assert(self.widget)
-- First, get the size of the widget (and the size of extra space)
local surface_width, surface_height = width, height
local extra_width, extra_height, extra = 0, 0, self.expand and self.extra_space or 0
local w, h
if self.dir == "h" then
w, h = base.fit_widget(self, context, self.widget, self.space_for_scrolling, height)
surface_width = w
extra_width = extra
else
w, h = base.fit_widget(self, context, self.widget, width, self.space_for_scrolling)
surface_height = h
extra_height = extra
end
result.fit_width, result.fit_height = w, h
if self.dir == "h" then
if self.max_size then
result.fit_width = math.min(w, self.max_size)
end
else
if self.max_size then
result.fit_height = math.min(h, self.max_size)
end
end
if w > width or h > height then
-- There is less space available than we need, we have to scroll
_need_scroll_redraw(self)
surface_width, surface_height = surface_width + extra_width, surface_height + extra_height
local x, y = 0, 0
local function get_scroll_offset(size, visible_size)
return self.step_function(self.timer:elapsed(), size, visible_size, self.speed, self.extra_space)
end
if self.dir == "h" then
x = -get_scroll_offset(surface_width - extra, width)
else
y = -get_scroll_offset(surface_height - extra, height)
end
result.first_x, result.first_y = x, y
-- Was the extra space already included elsewhere?
local extra_spacer = self.expand and 0 or self.extra_space
if self.dir == "h" then
x = x + surface_width + extra_spacer
else
y = y + surface_height + extra_spacer
end
result.second_x, result.second_y = x, y
else
result.first_x, result.first_y = 0, 0
end
result.surface_width, result.surface_height = surface_width, surface_height
-- Get the hierarchy and subscribe ourselves to updates
local hier, do_pending_updates, ctx = hierarchy_cache:get(context,
self.widget, surface_width, surface_height)
result.hierarchy = hier
result.context = ctx
do_pending_updates(self)
return result
end
--- Draw this scrolling layout.
-- @param context The context in which we are drawn.
-- @param cr The cairo context to draw to.
-- @param width The available width.
-- @param height The available height.
function scroll:draw(context, cr, width, height)
if not self.widget then
return
end
local info = calculate_info(self, context, width, height)
-- Draw the first instance of the child
cr:save()
cr:translate(info.first_x, info.first_y)
cr:rectangle(0, 0, info.surface_width, info.surface_height)
cr:clip()
info.hierarchy:draw(info.context, cr)
cr:restore()
-- If there is one, draw the second instance (same code as above, minus the
-- clip)
if info.second_x and info.second_y then
cr:translate(info.second_x, info.second_y)
cr:rectangle(0, 0, info.surface_width, info.surface_height)
cr:clip()
info.hierarchy:draw(info.context, cr)
end
end
--- Fit the scroll layout into the given space.
-- @param context The context in which we are fit.
-- @param width The available width.
-- @param height The available height.
function scroll:fit(context, width, height)
if not self.widget then
return 0, 0
end
local info = calculate_info(self, context, width, height)
return info.fit_width, info.fit_height
end
-- Internal function used for triggering redraws for scrolling.
-- The purpose is to start a timer for redrawing the widget for scrolling.
-- Redrawing works by simply emitting the `widget::redraw_needed` signal.
-- Pausing is implemented in this function: We just don't start a timer.
-- This function must be idempotent (calling it multiple times right after
-- another does not make a difference).
_need_scroll_redraw = function(self)
if not self.paused and not self.scroll_timer then
self.scroll_timer = timer.start_new(1 / self.fps, function()
self.scroll_timer = nil
self:emit_signal("widget::redraw_needed")
end)
end
end
--- Pause the scrolling animation.
-- @see continue
function scroll:pause()
if self.paused then
return
end
self.paused = true
self.timer:stop()
end
--- Continue the scrolling animation.
-- @see pause
function scroll:continue()
if not self.paused then
return
end
self.paused = false
self.timer:continue()
self:emit_signal("widget::redraw_needed")
end
--- Reset the scrolling state to its initial condition.
-- For must scroll step functions, the effect of this function should be to
-- display the widget without any scrolling applied.
-- This function does not undo the effect of @{pause}.
function scroll:reset_scrolling()
self.timer:start()
if self.paused then
self.timer:stop()
end
end
--- Set the direction in which this widget scroll.
-- @param dir Either "h" for horizontal scrolling or "v" for vertical scrolling
function scroll:set_direction(dir)
if dir == self.dir then
return
end
if dir ~= "h" and dir ~= "v" then
error("Invalid direction, can only be 'h' or 'v'")
end
self.dir = dir
self:emit_signal("widget::layout_changed")
self:emit_signal("widget::redraw_needed")
end
--- Set the widget which we scroll.
-- @tparam widget widget The widget that we should display
function scroll:set_widget(widget)
if widget == self.widget then
return
end
if widget then
base.check_widget(widget)
end
self.widget = widget
self:emit_signal("widget::layout_changed")
self:emit_signal("widget::redraw_needed")
end
--- Get the number of children element
-- @treturn table The children
function scroll:get_children()
return {self.widget}
end
--- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function scroll:set_children(children)
self:set_widget(children[1])
end
--- Specify the expand mode that is used for extra space.
-- @tparam boolean expand If true, the widget is expanded to include the extra
-- space. If false, the extra space is simply left empty.
-- @see set_extra_space
function scroll:set_expand(expand)
if expand == self.expand then
return
end
self.expand = expand
self:emit_signal("widget::redraw_needed")
end
--- Set the number of frames per second that this widget should draw.
-- @tparam number fps The number of frames per second
function scroll:set_fps(fps)
if fps == self.fps then
return
end
self.fps = fps
-- No signal needed: If we are scrolling, the next redraw will apply the new
-- FPS, else it obviously doesn't make a difference.
end
--- Set the amount of extra space that should be included in the scrolling. This
-- extra space will likely be left empty between repetitions of the widgets.
-- @tparam number extra_space The amount of extra space
-- @see set_expand
function scroll:set_extra_space(extra_space)
if extra_space == self.extra_space then
return
end
self.extra_space = extra_space
self:emit_signal("widget::redraw_needed")
end
--- Set the speed of the scrolling animation. The exact meaning depends on the
-- step function that is used, but for the simplest step functions, this will be
-- in pixels per second.
-- @tparam number speed The speed for the animation
function scroll:set_speed(speed)
if speed == self.speed then
return
end
self.speed = speed
self:emit_signal("widget::redraw_needed")
end
--- Set the maximum size of this widget in the direction set by
-- @{set_direction}. If the child widget is smaller than this size, no scrolling
-- is done. If the child widget is larger, then only this size will be visible
-- and the rest is made visible via scrolling.
-- @tparam number max_size The maximum size of this widget or nil for unlimited.
function scroll:set_max_size(max_size)
if max_size == self.max_size then
return
end
self.max_size = max_size
self:emit_signal("widget::layout_changed")
end
--- Set the step function that determines the exact behaviour of the scrolling
-- animation.
-- The step function is called with five arguments:
--
-- * The time in seconds since the state of the animation
-- * The size of the child widget
-- * The size of the visible part of the widget
-- * The speed of the animation. This should have a linear effect on this
-- function's behaviour.
-- * The extra space configured by @{set_extra_space}. This was not yet added to
-- the size of the child widget, but should likely be added to it in most
-- cases.
--
-- The step function should return a single number. This number is the offset at
-- which the widget is drawn and should be between 0 and `size+extra_space`.
-- @tparam function step_function A step function.
-- @see step_functions
function scroll:set_step_function(step_function)
-- Call the step functions once to see if it works
step_function(0, 42, 10, 10, 5)
if step_function == self.step_function then
return
end
self.step_function = step_function
self:emit_signal("widget::redraw_needed")
end
--- Set an upper limit for the space for scrolling.
-- This restricts the child widget's maximal size.
-- @tparam number space_for_scrolling The space for scrolling
function scroll:set_space_for_scrolling(space_for_scrolling)
if space_for_scrolling == self.space_for_scrolling then
return
end
self.space_for_scrolling = space_for_scrolling
self:emit_signal("widget::layout_changed")
end
local function get_layout(dir, widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
local ret = base.make_widget()
ret.paused = false
ret.timer = GLib.Timer()
ret.scroll_timer = nil
setmetatable(ret, scroll_mt)
ret:set_direction(dir)
ret:set_widget(widget)
ret:set_fps(fps or 20)
ret:set_speed(speed or 10)
ret:set_extra_space(extra_space or 0)
ret:set_expand(expand)
ret:set_max_size(max_size)
ret:set_step_function(step_function or scroll.step_functions.linear_increase)
ret:set_space_for_scrolling(space_for_scrolling or 2^1024)
return ret
end
--- Get a new horizontal scrolling layout.
-- @param[opt] widget The widget that should be scrolled
-- @param[opt=20] fps The number of frames per second
-- @param[opt=10] speed The speed of the animation
-- @param[opt=0] extra_space The amount of extra space to include
-- @tparam[opt=false] boolean expand Should the widget be expanded to include the
-- extra space?
-- @param[opt] max_size The maximum size of the child widget
-- @param[opt=step_functions.linear_increase] step_function The step function to be used
-- @param[opt=2^1024] space_for_scrolling The space for scrolling
function scroll.horizontal(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
return get_layout("h", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
end
--- Get a new vertical scrolling layout.
-- @param[opt] widget The widget that should be scrolled
-- @param[opt=20] fps The number of frames per second
-- @param[opt=10] speed The speed of the animation
-- @param[opt=0] extra_space The amount of extra space to include
-- @tparam[opt=false] boolean expand Should the widget be expanded to include the
-- extra space?
-- @param[opt] max_size The maximum size of the child widget
-- @param[opt=step_functions.linear_increase] step_function The step function to be used
-- @param[opt=2^1024] space_for_scrolling The space for scrolling
function scroll.vertical(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
return get_layout("v", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling)
end
--- A selection of step functions
-- @see set_step_function
scroll.step_functions = {}
--- A step function that scrolls the widget in an increasing direction with
-- constant speed.
function scroll.step_functions.linear_increase(elapsed, size, _, speed, extra_space)
return (elapsed * speed) % (size + extra_space)
end
--- A step function that scrolls the widget in an decreasing direction with
-- constant speed.
function scroll.step_functions.linear_decrease(elapsed, size, _, speed, extra_space)
return (-elapsed * speed) % (size + extra_space)
end
--- A step function that scrolls the widget to its end and back to its
-- beginning, then back to its end, etc. The speed is constant.
function scroll.step_functions.linear_back_and_forth(elapsed, size, visible_size, speed)
local state = ((elapsed * speed) % (2 * size)) / size
state = state <= 1 and state or 2 - state
return (size - visible_size) * state
end
--- A step function that scrolls the widget to its end and back to its
-- beginning, then back to its end, etc. The speed is null at the ends and
-- maximal in the middle.
function scroll.step_functions.nonlinear_back_and_forth(elapsed, size, visible_size, speed)
local state = ((elapsed * speed) % (2 * size)) / size
local negate = false
if state > 1 then
negate = true
state = state - 1
end
if state < 1/3 then
-- In the first 1/3rd of time, do a quadratic increase in speed
state = 2 * state * state
elseif state < 2/3 then
-- In the center, do a linear increase. That means we need:
-- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3
-- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3)
state = 5/3*state - 3/9
else
-- In the last 1/3rd of time, do a quadratic decrease in speed
state = 1 - 2 * (1 - state) * (1 - state)
end
if negate then
state = 1 - state
end
return (size - visible_size) * state
end
--- A step function that scrolls the widget to its end and back to its
-- beginning, then back to its end, etc. The speed is null at the ends and
-- maximal in the middle. At both ends the widget stands still for a moment.
function scroll.step_functions.waiting_nonlinear_back_and_forth(elapsed, size, visible_size, speed)
local state = ((elapsed * speed) % (2 * size)) / size
local negate = false
if state > 1 then
negate = true
state = state - 1
end
if state < 1/5 or state > 4/5 then
-- One fifth of time, nothing moves
state = state < 1/5 and 0 or 1
else
state = (state - 1/5) * 5/3
if state < 1/3 then
-- In the first 1/3rd of time, do a quadratic increase in speed
state = 2 * state * state
elseif state < 2/3 then
-- In the center, do a linear increase. That means we need:
-- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3
-- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3)
state = 5/3*state - 3/9
else
-- In the last 1/3rd of time, do a quadratic decrease in speed
state = 1 - 2 * (1 - state) * (1 - state)
end
end
if negate then
state = 1 - state
end
return (size - visible_size) * state
end
return scroll
return util.deprecate_class(
require("wibox.container.scroll"),
"wibox.layout.scroll",
"wibox.container.scroll"
)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -8,6 +8,7 @@
-- The indices are going from 1 (the bottom of the stack) up to the top of
-- the stack. The order can be changed either using `:swap` or `:raise`.
--
--@DOC_wibox_layout_defaults_stack_EXAMPLE@
-- @author Emmanuel Lepage Vallee
-- @copyright 2016 Emmanuel Lepage Vallee
-- @release @AWESOME_VERSION@
@ -52,17 +53,16 @@ local stack = {mt={}}
-- @class function
--- Add spacing between each layout widgets
-- @param spacing Spacing between widgets.
-- @name set_spacing
-- @class function
-- @property spacing
-- @tparam number spacing Spacing between widgets.
function stack:layout(_, width, height)
local result = {}
local spacing = self._spacing
local spacing = self._private.spacing
for _, v in pairs(self.widgets) do
for _, v in pairs(self._private.widgets) do
table.insert(result, base.place_widget_at(v, spacing, spacing, width - 2*spacing, height - 2*spacing))
if self._top_only then break end
if self._private.top_only then break end
end
return result
@ -70,9 +70,9 @@ end
function stack:fit(context, orig_width, orig_height)
local max_w, max_h = 0,0
local spacing = self._spacing
local spacing = self._private.spacing
for _, v in pairs(self.widgets) do
for _, v in pairs(self._private.widgets) do
local w, h = base.fit_widget(self, context, v, orig_width, orig_height)
max_w, max_h = math.max(max_w, w+2*spacing), math.max(max_h, h+2*spacing)
end
@ -80,26 +80,25 @@ function stack:fit(context, orig_width, orig_height)
return math.min(max_w, orig_width), math.min(max_h, orig_height)
end
--- Get if only the first stack widget is drawn
-- @return If the only the first stack widget is drawn
function stack:get_display_top_only()
return self._top_only
--- If only the first stack widget is drawn
-- @property top_only
function stack:get_top_only()
return self._private.top_only
end
--- Only draw the first widget of the stack, ignore others
-- @tparam boolean top_only Only draw the top stack widget
function stack:set_display_top_only(top_only)
self._top_only = top_only
function stack:set_top_only(top_only)
self._private.top_only = top_only
end
--- Raise a widget at `index` to the top of the stack
-- @tparam number index the widget index to raise
function stack:raise(index)
if (not index) or self.widgets[index] then return end
if (not index) or self._private.widgets[index] then return end
local w = self.widgets[index]
table.remove(self.widgets, index)
table.insert(self.widgets, w)
local w = self._private.widgets[index]
table.remove(self._private.widgets, index)
table.insert(self._private.widgets, w)
self:emit_signal("widget::layout_changed")
end
@ -130,7 +129,7 @@ end
local function new(...)
local ret = fixed.horizontal(...)
util.table.crush(ret, stack)
util.table.crush(ret, stack, true)
return ret
end
@ -139,5 +138,9 @@ function stack.mt:__call(_, ...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(stack, stack.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,224 +1,17 @@
---------------------------------------------------------------------------
-- A container capable of changing the background color, foreground color
-- widget shape.
-- This class has been moved to `wibox.container.background`
--
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @classmod wibox.widget.background
---------------------------------------------------------------------------
local util = require("awful.util")
local base = require("wibox.widget.base")
local color = require("gears.color")
local surface = require("gears.surface")
local beautiful = require("beautiful")
local cairo = require("lgi").cairo
local setmetatable = setmetatable
local pairs = pairs
local type = type
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local background = { mt = {} }
-- Draw this widget
function background:draw(context, cr, width, height)
if not self.widget or not self.widget.visible then
return
end
-- Keep the shape path in case there is a border
self._path = nil
if self._shape then
-- Only add the offset if there is something to draw
local offset = ((self._shape_border_width and self._shape_border_color)
and self._shape_border_width or 0) / 2
cr:translate(offset, offset)
self._shape(cr, width - 2*offset, height - 2*offset, unpack(self._shape_args or {}))
cr:translate(-offset, -offset)
self._path = cr:copy_path()
cr:clip()
end
if self.background then
cr:set_source(self.background)
cr:paint()
end
if self.bgimage then
if type(self.bgimage) == "function" then
self.bgimage(context, cr, width, height,unpack(self.bgimage_args))
else
local pattern = cairo.Pattern.create_for_surface(self.bgimage)
cr:set_source(pattern)
cr:paint()
end
end
end
-- Draw the border
function background:after_draw_children(_, cr)
-- Draw the border
if self._path and self._shape_border_width and self._shape_border_width > 0 then
cr:append_path(self._path)
cr:set_source(color(self._shape_border_color or self.foreground or beautiful.fg_normal))
cr:set_line_width(self._shape_border_width)
cr:stroke()
self._path = nil
end
end
-- Prepare drawing the children of this widget
function background:before_draw_children(_, cr)
if self.foreground then
cr:set_source(self.foreground)
end
-- Clip the shape
if self._path and self._shape_clip then
cr:append_path(self._path)
cr:clip()
end
end
-- Layout this widget
function background:layout(_, width, height)
if self.widget then
return { base.place_widget_at(self.widget, 0, 0, width, height) }
end
end
-- Fit this widget into the given area
function background:fit(context, width, height)
if not self.widget then
return 0, 0
end
return base.fit_widget(self, context, self.widget, width, height)
end
--- Set the widget that is drawn on top of the background
-- @tparam widget widget The widget to be disaplayed inside of the background
-- area
function background:set_widget(widget)
if widget then
base.check_widget(widget)
end
self.widget = widget
self:emit_signal("widget::layout_changed")
end
-- Get children element
-- @treturn table The children
function background:get_children()
return {self.widget}
end
-- Replace the layout children
-- This layout only accept one children, all others will be ignored
-- @tparam table children A table composed of valid widgets
function background:set_children(children)
self:set_widget(children[1])
end
--- Set the background to use.
--@DOC_wibox_widget_background_bg_EXAMPLE@
-- @param bg A color string, pattern or gradient (see `gears.color`)
function background:set_bg(bg)
if bg then
self.background = color(bg)
else
self.background = nil
end
self:emit_signal("widget::redraw_needed")
end
--- Set the foreground to use.
--@DOC_wibox_widget_background_fg_EXAMPLE@
-- @param fg A color string, pattern or gradient (see `gears.color`)
function background:set_fg(fg)
if fg then
self.foreground = color(fg)
else
self.foreground = nil
end
self:emit_signal("widget::redraw_needed")
end
--- Set the background shape.
--
-- Any other arguments will be passed to the shape function
--@DOC_wibox_widget_background_shape_EXAMPLE@
-- @param shape A function taking a context, width and height as arguments
function background:set_shape(shape, ...)
self._shape = shape
self._shape_args = {...}
self:emit_signal("widget::redraw_needed")
end
--- When a `shape` is set, also draw a border.
--
-- See `wibox.widget.background.set_shape` for an usage example.
-- @tparam number width The border width
function background:set_shape_border_width(width)
self._shape_border_width = width
self:emit_signal("widget::redraw_needed")
end
--- When a `shape` is set, also draw a border.
--
-- See `wibox.widget.background.set_shape` for an usage example.
-- @param[opt=self.foreground] fg The border color, pattern or gradient
function background:set_shape_border_color(fg)
self._shape_border_color = fg
self:emit_signal("widget::redraw_needed")
end
--- When a `shape` is set, make sure nothing is drawn outside of it.
--@DOC_wibox_widget_background_clip_EXAMPLE@
-- @tparam boolean value If the shape clip is enable
function background:set_shape_clip(value)
self._shape_clip = value
self:emit_signal("widget::redraw_needed")
end
--- Set the background image to use
-- If `image` is a function, it will be called with `(context, cr, width, height)`
-- as arguments. Any other arguments passed to this method will be appended.
-- @param image A background image or a function
function background:set_bgimage(image, ...)
self.bgimage = type(image) == "function" and image or surface.load(image)
self.bgimage_args = {...}
self:emit_signal("widget::redraw_needed")
end
--- Returns a new background layout. A background layout applies a background
-- and foreground color to another widget.
-- @param[opt] widget The widget to display.
-- @param[opt] bg The background to use for that widget.
-- @param[opt] shape A `gears.shape` compatible shape function
local function new(widget, bg, shape)
local ret = base.make_widget()
for k, v in pairs(background) do
if type(v) == "function" then
ret[k] = v
end
end
ret._shape = shape
ret:set_widget(widget)
ret:set_bg(bg)
return ret
end
function background.mt:__call(...)
return new(...)
end
return setmetatable(background, background.mt)
return util.deprecate_class(
require("wibox.container.background"),
"wibox.widget.background",
"wibox.container.background"
)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -2,7 +2,7 @@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
-- @module wibox.widget.base
-- @classmod wibox.widget.base
---------------------------------------------------------------------------
local object = require("gears.object")
@ -24,60 +24,102 @@ base.widget = {}
--- Set/get a widget's buttons.
-- @param _buttons The table of buttons that should bind to the widget.
-- @function buttons
function base.widget:buttons(_buttons)
if _buttons then
self.widget_buttons = _buttons
self._private.widget_buttons = _buttons
end
return self.widget_buttons
return self._private.widget_buttons
end
--- Set a widget's visible property
-- @tparam boolean b Wether the widget is visible at all
-- @function set_visible
function base.widget:set_visible(b)
if b ~= self.visible then
self.visible = b
if b ~= self._private.visible then
self._private.visible = b
self:emit_signal("widget::layout_changed")
-- In case something ignored fit and drew the widget anyway
self:emit_signal("widget::redraw_needed")
end
end
--- Get if the widget is visible.
-- @treturn boolean If the widget is visible
-- @function get_visible
function base.widget:get_visible()
return self._private.visible or false
end
--- Set a widget's opacity
-- @tparam number o The opacity to use (a number from 0 to 1). 0 is fully
-- transparent while 1 is fully opaque.
-- @function set_opacity
function base.widget:set_opacity(o)
if o ~= self.opacity then
self.opacity = o
if o ~= self._private.opacity then
self._private.opacity = o
self:emit_signal("widget::redraw")
end
end
--- Get the widget opacity.
-- @treturn number The opacity (between 0 and 1)
-- @function get_opacity
function base.widget:get_opacity()
return self._private.opacity
end
--- Set the widget's width
-- @tparam number|nil s The width that the widget has. `nil` means to apply the
-- default mechanism of calling the `:fit` method. A number overrides the result
-- from `:fit`.
function base.widget:set_width(s)
if s ~= self._forced_width then
self._forced_width = s
-- @function set_width
function base.widget:set_forced_width(s)
if s ~= self._private.forced_width then
self._private.forced_width = s
self:emit_signal("widget::layout_changed")
end
end
--- Get the widget forced width.
-- Note that widgets instances can be placed at different places simultaneously,
-- therefore, they can have multiple width and width simultaneously. If there
-- is no forced size, then the only way to get the widget actual size is when
-- there is a `mouse::enter`, `mouse::leave` or button events.
-- @treturn nil|number The forced width (nil if automatic)
-- @function get_forced_widget
function base.widget:get_forced_width()
return self._private.forced_width
end
--- Set the widget's height
-- @tparam number|nil s The height that the widget has. `nil` means to apply the
-- default mechanism of calling the `:fit` method. A number overrides the result
-- from `:fit`.
function base.widget:set_height(s)
if s ~= self._forced_height then
self._forced_height = s
-- @function set_height
function base.widget:set_forced_height(s)
if s ~= self._private.forced_height then
self._private.forced_height = s
self:emit_signal("widget::layout_changed")
end
end
--- Get the widget forced height.
-- Note that widgets instances can be placed at different places simultaneously,
-- therefore, they can have multiple width and height simultaneously. If there
-- is no forced size, then the only way to get the widget actual size is when
-- there is a `mouse::enter`, `mouse::leave` or button events.
-- @treturn nil|number The forced height (nil if automatic)
-- @function get_forced_height
function base.widget:get_forced_height()
return self._private.forced_height
end
--- Get all direct children widgets
-- This method should be re-implemented by the relevant widgets
-- @treturn table The children
-- @function get_children
function base.widget:get_children()
return {}
end
@ -86,6 +128,7 @@ end
-- The default implementation does nothing, this must be re-implemented by
-- all layout and container widgets.
-- @tparam table children A table composed of valid widgets
-- @function set_children
function base.widget:set_children(children) -- luacheck: no unused
-- Nothing on purpose
end
@ -98,24 +141,26 @@ local function digg_children(ret, tlw)
end
end
--- Get all direct and indirect children widgets
--- 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
function base.widget:get_all_children()
local ret = {}
digg_children(ret, self)
return ret
end
--- Get a widex index
--- 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
function base.widget:index(widget, recursive, ...)
local widgets = self:get_children()
@ -143,12 +188,12 @@ local widget_dependencies = setmetatable({}, { __mode = "kv" })
-- Get the cache of the given kind for this widget. This returns a gears.cache
-- that calls the callback of kind `kind` on the widget.
local function get_cache(widget, kind)
if not widget._widget_caches[kind] then
widget._widget_caches[kind] = cache.new(function(...)
if not widget._private.widget_caches[kind] then
widget._private.widget_caches[kind] = cache.new(function(...)
return protected_call(widget[kind], widget, ...)
end)
end
return widget._widget_caches[kind]
return widget._private.widget_caches[kind]
end
-- Special value to skip the dependency recording that is normally done by
@ -177,7 +222,7 @@ local clear_caches
function clear_caches(widget)
local deps = widget_dependencies[widget] or {}
widget_dependencies[widget] = {}
widget._widget_caches = {}
widget._private.widget_caches = {}
for w in pairs(deps) do
clear_caches(w)
end
@ -187,6 +232,7 @@ end
--- Figure out the geometry in device coordinate space. This gives only tight
-- bounds if no rotations by non-multiples of 90° are used.
-- @function wibox.widget.base.rect_to_device_geometry
function base.rect_to_device_geometry(cr, x, y, width, height)
return matrix.transform_rectangle(cr.matrix, x, y, width, height)
end
@ -200,10 +246,11 @@ end
-- @param width The available width for the widget
-- @param height The available height for the widget
-- @return The width and height that the widget wants to use
-- @function wibox.widget.base.fit_widget
function base.fit_widget(parent, context, widget, width, height)
record_dependency(parent, widget)
if not widget.visible then
if not widget._private.visible then
return 0, 0
end
@ -225,8 +272,8 @@ function base.fit_widget(parent, context, widget, width, height)
end
-- Apply forced size and handle nil's
w = widget._forced_width or w or 0
h = widget._forced_height or h or 0
w = widget._private.forced_width or w or 0
h = widget._private.forced_height or h or 0
-- Also sanitize the output.
w = math.max(0, math.min(w, width))
@ -244,10 +291,11 @@ end
-- @param width The available width for the widget
-- @param height The available height for the widget
-- @return The result from the widget's `:layout` callback.
-- @function wibox.widget.base.layout_widget
function base.layout_widget(parent, context, widget, width, height)
record_dependency(parent, widget)
if not widget.visible then
if not widget._private.visible then
return
end
@ -262,6 +310,7 @@ end
-- Handle a button event on a widget. This is used internally and should not be
-- called directly.
-- @function wibox.widget.base.handle_button
function base.handle_button(event, widget, x, y, button, modifiers, geometry)
x = x or y -- luacheck: no unused
local function is_any(mod)
@ -282,7 +331,7 @@ function base.handle_button(event, widget, x, y, button, modifiers, geometry)
-- Find all matching button objects
local matches = {}
for _, v in pairs(widget.widget_buttons) do
for _, v in pairs(widget._private.widget_buttons) do
local match = true
-- Is it the right button?
if v.button ~= 0 and v.button ~= button then match = false end
@ -310,6 +359,7 @@ end
-- @param height The height of the widget in its own coordinate system. That is,
-- after applying the transformation matrix.
-- @return An opaque object that can be returned from :layout()
-- @function wibox.widget.base.place_widget_via_matrix
function base.place_widget_via_matrix(widget, mat, width, height)
return {
_widget = widget,
@ -329,6 +379,7 @@ end
-- @param height The height of the widget in its own coordinate system. That is,
-- after applying the transformation matrix.
-- @return An opaque object that can be returned from :layout()
-- @function wibox.widget.base.place_widget_at
function base.place_widget_at(widget, x, y, width, height)
return base.place_widget_via_matrix(widget, matrix.create_translate(x, y), width, height)
end
@ -424,12 +475,17 @@ end
-- Only available when the declarative system is used
local function get_children_by_id(self, name)
return self._by_id[name] or {}
if rawget(self, "_private") then
return self._private.by_id[name] or {}
else
return rawget(self, "_by_id")[name] or {}
end
end
--- 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
function base.widget:setup(args)
local f,ids = self.set_widget or self.add or self.set_first,{}
local w, id = drill(ids, args)
@ -438,13 +494,20 @@ function base.widget:setup(args)
-- Avoid being dropped by wibox metatable -> drawin
rawset(self, id, w)
end
rawset(self, "_by_id", ids)
if rawget(self, "_private") then
self._private.by_id = ids
else
rawset(self, "_by_id", ids)
end
rawset(self, "get_children_by_id", get_children_by_id)
end
--- Create a widget from a declarative description
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html)
-- @param args An array containing the widgets disposition
-- @function wibox.widget.base.make_widget_declarative
function base.make_widget_declarative(args)
local ids = {}
@ -454,10 +517,16 @@ function base.make_widget_declarative(args)
local w, id = drill(ids, args)
local mt = {}
local mt = getmetatable(w) or {}
local orig_string = tostring(w)
rawset(w, "_by_id", ids)
if rawget(w, "_private") then
w._private.by_id = ids
else
rawset(w, "_by_id", ids)
end
rawset(w, "get_children_by_id", get_children_by_id)
mt.__tostring = function()
@ -474,9 +543,18 @@ end
-- looks the same on the screen.
-- @tparam[opt] string widget_name Name of the widget. If not set, it will be
-- set automatically via `gears.object.modulename`.
-- @tparam[opt={}] table args Widget settings
-- @tparam[opt=false] boolean args.enable_properties Enable automatic getters and
-- setters calls.
-- @tparam[opt=nil] table args.class The widget class
-- @see fit_widget
function base.make_widget(proxy, widget_name)
local ret = object()
-- @function wibox.widget.base.make_widget
function base.make_widget(proxy, widget_name, args)
args = args or {}
local ret = object {
enable_properties = args.enable_properties,
class = args.class,
}
-- This signal is used by layouts to find out when they have to update.
ret:add_signal("widget::layout_changed")
@ -495,21 +573,24 @@ function base.make_widget(proxy, widget_name)
ret:emit_signal("widget::redraw_needed")
end)
-- Create a table used to store the widgets internal data
rawset(ret, "_private", {})
-- No buttons yet
ret.widget_buttons = {}
ret._private.widget_buttons = {}
-- Widget is visible
ret.visible = true
ret._private.visible = true
-- Widget is fully opaque
ret.opacity = 1
ret._private.opacity = 1
-- Differentiate tables from widgets
ret.is_widget = true
rawset(ret, "is_widget", true)
-- Size is not restricted/forced
ret._forced_width = nil
ret._forced_height = nil
ret._private.forced_width = nil
ret._private.forced_height = nil
-- Make buttons work
ret:connect_signal("button::press", function(...)
@ -520,12 +601,12 @@ function base.make_widget(proxy, widget_name)
end)
if proxy then
ret.fit = function(_, context, width, height)
rawset(ret, "fit", function(_, context, width, height)
return base.fit_widget(ret, context, proxy, width, height)
end
ret.layout = function(_, _, width, height)
end)
rawset(ret, "layout", function(_, _, width, height)
return { base.place_widget_at(proxy, 0, 0, width, height) }
end
end)
proxy:connect_signal("widget::layout_changed", function()
ret:emit_signal("widget::layout_changed")
end)
@ -542,12 +623,12 @@ function base.make_widget(proxy, widget_name)
-- Add functions
for k, v in pairs(base.widget) do
ret[k] = v
rawset(ret, k, v)
end
-- Add __tostring method to metatable.
ret.widget_name = widget_name or object.modulename(3)
local mt = {}
rawset(ret, "widget_name", widget_name or object.modulename(3))
local mt = getmetatable(ret) or {}
local orig_string = tostring(ret)
mt.__tostring = function()
return string.format("%s (%s)", ret.widget_name, orig_string)
@ -556,12 +637,14 @@ function base.make_widget(proxy, widget_name)
end
--- Generate an empty widget which takes no space and displays nothing
-- @function wibox.widget.base.empty_widget
function base.empty_widget()
return base.make_widget()
end
--- Do some sanity checking on widget. This function raises a lua error if
-- widget is not a valid widget.
-- @function wibox.widget.base.check_widget
function base.check_widget(widget)
assert(type(widget) == "table", "Type should be table, but is " .. tostring(type(widget)))
assert(widget.is_widget, "Argument is not a widget!")

291
lib/wibox/widget/graph.lua Normal file
View File

@ -0,0 +1,291 @@
---------------------------------------------------------------------------
--- A graph widget.
--
--@DOC_wibox_widget_defaults_graph_EXAMPLE@
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release @AWESOME_VERSION@
-- @classmod wibox.widget.graph
---------------------------------------------------------------------------
local setmetatable = setmetatable
local ipairs = ipairs
local math = math
local table = table
local type = type
local color = require("gears.color")
local base = require("wibox.widget.base")
local beautiful = require("beautiful")
local graph = { mt = {} }
--- Set the graph border color.
-- If the value is nil, no border will be drawn.
--
-- @property border_color
-- @tparam geats.color border_color The border color to set.
--- Set the graph foreground color.
--
-- @property color
-- @tparam color color The graph color.
-- @see gears.color.create_pattern
--- Set the graph background color.
--
-- @property background_color
-- @tparam gears.color background_color The graph background color.
--- Set the maximum value the graph should handle.
-- If "scale" is also set, the graph never scales up below this value, but it
-- automatically scales down to make all data fit.
--
-- @property max_value
-- @param number
--- Set the graph to automatically scale its values. Default is false.
--
-- @property scale
-- @param boolean
--- Set the graph to draw stacks. Default is false.
--
-- @property stack
-- @param boolean
--- Set the graph stacking colors. Order matters.
--
-- @property stack_colors
-- @param stack_colors A table with stacking colors.
--- The graph background color.
-- @beautiful beautiful.graph_bg
--- The graph foreground color.
-- @beautiful beautiful.graph_fg
--- The graph border color.
-- @beautiful beautiful.graph_border_color
local properties = { "width", "height", "border_color", "stack",
"stack_colors", "color", "background_color",
"max_value", "scale" }
function graph.draw(_graph, _, cr, width, height)
local max_value = _graph._private.max_value
local values = _graph._private.values
cr:set_line_width(1)
-- Draw the background first
cr:set_source(color(_graph._private.background_color or beautiful.graph_bg or "#000000aa"))
cr:paint()
-- Account for the border width
cr:save()
if _graph._private.border_color then
cr:translate(1, 1)
width, height = width - 2, height - 2
end
-- Draw a stacked graph
if _graph._private.stack then
if _graph._private.scale then
for _, v in ipairs(values) do
for _, sv in ipairs(v) do
if sv > max_value then
max_value = sv
end
end
end
end
for i = 0, width do
local rel_i = 0
local rel_x = i + 0.5
if _graph._private.stack_colors then
for idx, col in ipairs(_graph._private.stack_colors) do
local stack_values = values[idx]
if stack_values and i < #stack_values then
local value = stack_values[#stack_values - i] + rel_i
cr:move_to(rel_x, height * (1 - (rel_i / max_value)))
cr:line_to(rel_x, height * (1 - (value / max_value)))
cr:set_source(color(col or beautiful.graph_fg or "#ff0000"))
cr:stroke()
rel_i = value
end
end
end
end
else
if _graph._private.scale then
for _, v in ipairs(values) do
if v > max_value then
max_value = v
end
end
end
-- Draw the background on no value
if #values ~= 0 then
-- Draw reverse
for i = 0, #values - 1 do
local value = values[#values - i]
if value >= 0 then
value = value / max_value
cr:move_to(i + 0.5, height * (1 - value))
cr:line_to(i + 0.5, height)
end
end
cr:set_source(color(_graph._private.color or beautiful.graph_fg or "#ff0000"))
cr:stroke()
end
end
-- Undo the cr:translate() for the border
cr:restore()
-- Draw the border last so that it overlaps already drawn values
if _graph._private.border_color then
-- We decremented these by two above
width, height = width + 2, height + 2
-- Draw the border
cr:rectangle(0.5, 0.5, width - 1, height - 1)
cr:set_source(color(_graph._private.border_color or beautiful.graph_border_color or "#ffffff"))
cr:stroke()
end
end
function graph.fit(_graph)
return _graph._private.width, _graph._private.height
end
--- Add a value to the graph
--
-- @param value The value to be added to the graph
-- @param group The stack color group index.
function graph:add_value(value, group)
value = value or 0
local values = self._private.values
local max_value = self._private.max_value
value = math.max(0, value)
if not self._private.scale then
value = math.min(max_value, value)
end
if self._private.stack and group then
if not self._private.values[group]
or type(self._private.values[group]) ~= "table"
then
self._private.values[group] = {}
end
values = self._private.values[group]
end
table.insert(values, value)
local border_width = 0
if self._private.border_color then border_width = 2 end
-- Ensure we never have more data than we can draw
while #values > self._private.width - border_width do
table.remove(values, 1)
end
self:emit_signal("widget::redraw_needed")
return self
end
--- Clear the graph.
function graph:clear()
self._private.values = {}
self:emit_signal("widget::redraw_needed")
return self
end
--- Set the graph height.
-- @param height The height to set.
function graph:set_height(height)
if height >= 5 then
self._private.height = height
self:emit_signal("widget::layout_changed")
end
return self
end
--- Set the graph width.
-- @param width The width to set.
function graph:set_width(width)
if width >= 5 then
self._private.width = width
self:emit_signal("widget::layout_changed")
end
return self
end
-- Build properties function
for _, prop in ipairs(properties) do
if not graph["set_" .. prop] then
graph["set_" .. prop] = function(_graph, value)
if _graph._private[prop] ~= value then
_graph._private[prop] = value
_graph:emit_signal("widget::redraw_needed")
end
return _graph
end
end
if not graph["get_" .. prop] then
graph["get_" .. prop] = function(_graph)
return _graph._private[prop]
end
end
end
--- Create a graph widget.
-- @param args Standard widget() arguments. You should add width and height
-- key to set graph geometry.
-- @return A new graph widget.
-- @function wibox.widget.graph
function graph.new(args)
args = args or {}
local width = args.width or 100
local height = args.height or 20
if width < 5 or height < 5 then return end
local _graph = base.make_widget(nil, nil, {enable_properties = true})
_graph._private.width = width
_graph._private.height = height
_graph._private.values = {}
_graph._private.max_value = 1
-- Set methods
_graph.add_value = graph["add_value"]
_graph.clear = graph["clear"]
_graph.draw = graph.draw
_graph.fit = graph.fit
for _, prop in ipairs(properties) do
_graph["set_" .. prop] = graph["set_" .. prop]
_graph["get_" .. prop] = graph["get_" .. prop]
end
return _graph
end
function graph.mt:__call(...)
return graph.new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(graph, graph.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -1,4 +1,6 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_widget_defaults_imagebox_EXAMPLE@
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @release @AWESOME_VERSION@
@ -7,23 +9,23 @@
local base = require("wibox.widget.base")
local surface = require("gears.surface")
local util = require("awful.util")
local setmetatable = setmetatable
local pairs = pairs
local type = type
local print = print
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local imagebox = { mt = {} }
--- Draw an imagebox with the given cairo context in the given geometry.
-- Draw an imagebox with the given cairo context in the given geometry.
function imagebox:draw(_, cr, width, height)
if not self._image then return end
if not self._private.image then return end
if width == 0 or height == 0 then return end
if not self.resize_forbidden then
if not self._private.resize_forbidden then
-- Let's scale the image so that it fits into (width, height)
local w = self._image:get_width()
local h = self._image:get_height()
local w = self._private.image:get_width()
local h = self._private.image:get_height()
local aspect = width / w
local aspect_h = height / h
if aspect > aspect_h then aspect = aspect_h end
@ -32,22 +34,22 @@ function imagebox:draw(_, cr, width, height)
end
-- Set the clip
if self._clip_shape then
cr:clip(self._clip_shape(cr, width, height, unpack(self._clip_args)))
if self._private.clip_shape then
cr:clip(self._private.clip_shape(cr, width, height, unpack(self._private.clip_args)))
end
cr:set_source_surface(self._image, 0, 0)
cr:set_source_surface(self._private.image, 0, 0)
cr:paint()
end
--- Fit the imagebox into the given geometry
-- Fit the imagebox into the given geometry
function imagebox:fit(_, width, height)
if not self._image then
if not self._private.image then
return 0, 0
end
local w = self._image:get_width()
local h = self._image:get_height()
local w = self._private.image:get_width()
local h = self._private.image:get_height()
if w > width then
h = h * width / w
@ -62,7 +64,7 @@ function imagebox:fit(_, width, height)
return 0, 0
end
if not self.resize_forbidden then
if not self._private.resize_forbidden then
local aspect = width / w
local aspect_h = height / h
@ -76,9 +78,11 @@ function imagebox:fit(_, width, height)
end
--- Set an imagebox' image
-- @property image
-- @param image Either a string or a cairo image surface. A string is
-- interpreted as the path to a png image file.
-- @return true on success, false if the image cannot be used
function imagebox:set_image(image)
if type(image) == "string" then
image = surface.load(image)
@ -98,19 +102,28 @@ function imagebox:set_image(image)
end
end
if self._image == image then
if self._private.image == image then
-- The image could have been modified, so better redraw
self:emit_signal("widget::redraw_needed")
return
end
self._image = image
self._private.image = image
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
return true
end
--- Set a clip shape for this imagebox
-- A clip shape define an area where the content is displayed and one where it
-- is trimmed.
--
-- @property clip_shape
-- @param clip_shape A `gears_shape` compatible shape function
-- @see gears.shape
-- @see set_clip_shape
--- Set a clip shape for this imagebox
-- A clip shape define an area where the content is displayed and one where it
-- is trimmed.
@ -118,35 +131,37 @@ end
-- Any other parameters will be passed to the clip shape function
--
-- @param clip_shape A `gears_shape` compatible shape function
-- @see gears.shape
-- @see clip_shape
function imagebox:set_clip_shape(clip_shape, ...)
self._clip_shape = clip_shape
self._clip_args = {...}
self._private.clip_shape = clip_shape
self._private.clip_args = {...}
self:emit_signal("widget::redraw_needed")
end
--- Should the image be resized to fit into the available space?
-- @property resize
-- @param allowed If false, the image will be clipped, else it will be resized
-- to fit into the available space.
function imagebox:set_resize(allowed)
self.resize_forbidden = not allowed
self._private.resize_forbidden = not allowed
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
--- Returns a new imagebox
--- Returns a new imagebox.
-- Any other arguments will be passed to the clip shape function
-- @param image the image to display, may be nil
-- @param resize_allowed If false, the image will be clipped, else it will be resized
-- to fit into the available space.
-- @param clip_shape A `gears.shape` compatible function
-- @treturn table A new `imagebox`
-- @function wibox.widget.imagebox
local function new(image, resize_allowed, clip_shape)
local ret = base.make_widget()
local ret = base.make_widget(nil, nil, {enable_properties = true})
for k, v in pairs(imagebox) do
if type(v) == "function" then
ret[k] = v
end
end
util.table.crush(ret, imagebox, true)
if image then
ret:set_image(image)
@ -155,8 +170,8 @@ local function new(image, resize_allowed, clip_shape)
ret:set_resize(resize_allowed)
end
ret._clip_shape = clip_shape
ret._clip_args = {}
ret._private.clip_shape = clip_shape
ret._private.clip_args = {}
return ret
end
@ -165,6 +180,10 @@ function imagebox.mt:__call(...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(imagebox, imagebox.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -12,6 +12,9 @@ return setmetatable({
imagebox = require("wibox.widget.imagebox");
background = require("wibox.widget.background");
systray = require("wibox.widget.systray");
textclock = require("wibox.widget.textclock");
progressbar = require("wibox.widget.progressbar");
graph = require("wibox.widget.graph");
}, {__call = function(_, args) return base.make_widget_declarative(args) end})
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,243 @@
---------------------------------------------------------------------------
--- A progressbar widget.
--
--@DOC_wibox_widget_defaults_progressbar_EXAMPLE@
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release @AWESOME_VERSION@
-- @classmod wibox.widget.progressbar
---------------------------------------------------------------------------
local setmetatable = setmetatable
local ipairs = ipairs
local math = math
local base = require("wibox.widget.base")
local color = require("gears.color")
local beautiful = require("beautiful")
local progressbar = { mt = {} }
local data = setmetatable({}, { __mode = "k" })
--- Set the progressbar border color.
-- If the value is nil, no border will be drawn.
--
-- @function set_border_color
-- @param progressbar The progressbar.
-- @param color The border color to set.
--- Set the progressbar foreground color.
--
-- @function set_color
-- @param progressbar The progressbar.
-- @param color The progressbar color.
--- Set the progressbar background color.
--
-- @function set_background_color
-- @param progressbar The progressbar.
-- @param color The progressbar background color.
--- Set the progressbar to draw vertically. Default is false.
--
-- @function set_vertical
-- @param progressbar The progressbar.
-- @param vertical A boolean value.
--- Set the progressbar to draw ticks. Default is false.
--
-- @function set_ticks
-- @param progressbar The progressbar.
-- @param ticks A boolean value.
--- Set the progressbar ticks gap.
--
-- @function set_ticks_gap
-- @param progressbar The progressbar.
-- @param value The value.
--- Set the progressbar ticks size.
--
-- @function set_ticks_size
-- @param progressbar The progressbar.
-- @param value The value.
--- Set the maximum value the progressbar should handle.
--
-- @function set_max_value
-- @param progressbar The progressbar.
-- @param value The value.
--- The progressbar background color.
-- @beautiful beautiful.graph_bg
--- The progressbar foreground color.
-- @beautiful beautiful.graph_fg
--- The progressbar border color.
-- @beautiful beautiful.graph_border_color
local properties = { "width", "height", "border_color",
"color", "background_color",
"vertical", "value", "max_value",
"ticks", "ticks_gap", "ticks_size" }
function progressbar.draw(pbar, _, cr, width, height)
local ticks_gap = data[pbar].ticks_gap or 1
local ticks_size = data[pbar].ticks_size or 4
-- We want one pixel wide lines
cr:set_line_width(1)
local value = data[pbar].value
local max_value = data[pbar].max_value
if value >= 0 then
value = value / max_value
end
local over_drawn_width = width
local over_drawn_height = height
local border_width = 0
local col = data[pbar].border_color or beautiful.progressbar_border_color
if col then
-- Draw border
cr:rectangle(0.5, 0.5, width - 1, height - 1)
cr:set_source(color(col))
cr:stroke()
over_drawn_width = width - 2 -- remove 2 for borders
over_drawn_height = height - 2 -- remove 2 for borders
border_width = 1
end
cr:rectangle(border_width, border_width,
over_drawn_width, over_drawn_height)
cr:set_source(color(data[pbar].color or beautiful.progressbar_fg or "#ff0000"))
cr:fill()
-- Cover the part that is not set with a rectangle
if data[pbar].vertical then
local rel_height = over_drawn_height * (1 - value)
cr:rectangle(border_width,
border_width,
over_drawn_width,
rel_height)
cr:set_source(color(data[pbar].background_color or beautiful.progressbar_bg or "#000000aa"))
cr:fill()
-- Place smaller pieces over the gradient if ticks are enabled
if data[pbar].ticks then
for i=0, height / (ticks_size+ticks_gap)-border_width do
local rel_offset = over_drawn_height / 1 - (ticks_size+ticks_gap) * i
if rel_offset >= rel_height then
cr:rectangle(border_width,
rel_offset,
over_drawn_width,
ticks_gap)
end
end
cr:set_source(color(data[pbar].background_color or beautiful.progressbar_bg or "#000000aa"))
cr:fill()
end
else
local rel_x = over_drawn_width * value
cr:rectangle(border_width + rel_x,
border_width,
over_drawn_width - rel_x,
over_drawn_height)
cr:set_source(color(data[pbar].background_color or "#000000aa"))
cr:fill()
if data[pbar].ticks then
for i=0, width / (ticks_size+ticks_gap)-border_width do
local rel_offset = over_drawn_width / 1 - (ticks_size+ticks_gap) * i
if rel_offset <= rel_x then
cr:rectangle(rel_offset,
border_width,
ticks_gap,
over_drawn_height)
end
end
cr:set_source(color(data[pbar].background_color or "#000000aa"))
cr:fill()
end
end
end
function progressbar.fit(pbar)
return data[pbar].width, data[pbar].height
end
--- Set the progressbar value.
-- @param value The progress bar value between 0 and 1.
function progressbar:set_value(value)
value = value or 0
local max_value = data[self].max_value
data[self].value = math.min(max_value, math.max(0, value))
self:emit_signal("widget::redraw_needed")
return self
end
--- Set the progressbar height.
-- @param height The height to set.
function progressbar:set_height(height)
data[self].height = height
self:emit_signal("widget::layout_changed")
return self
end
--- Set the progressbar width.
-- @param width The width to set.
function progressbar:set_width(width)
data[self].width = width
self:emit_signal("widget::layout_changed")
return self
end
-- Build properties function
for _, prop in ipairs(properties) do
if not progressbar["set_" .. prop] then
progressbar["set_" .. prop] = function(pbar, value)
data[pbar][prop] = value
pbar:emit_signal("widget::redraw_needed")
return pbar
end
end
end
--- Create a progressbar widget.
-- @param args Standard widget() arguments. You should add width and height
-- key to set progressbar geometry.
-- @return A progressbar widget.
-- @function wibox.widget.progressbar
function progressbar.new(args)
args = args or {}
local width = args.width or 100
local height = args.height or 20
args.type = "imagebox"
local pbar = base.make_widget()
data[pbar] = { width = width, height = height, value = 0, max_value = 1 }
-- Set methods
for _, prop in ipairs(properties) do
pbar["set_" .. prop] = progressbar["set_" .. prop]
end
pbar.draw = progressbar.draw
pbar.fit = progressbar.fit
return pbar
end
function progressbar.mt:__call(...)
return progressbar.new(...)
end
return setmetatable(progressbar, progressbar.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -7,6 +7,7 @@
local wbase = require("wibox.widget.base")
local beautiful = require("beautiful")
local util = require("awful.util")
local capi = {
awesome = awesome,
screen = screen
@ -23,6 +24,14 @@ local base_size = nil
local reverse = false
local display_on_screen = "primary"
--- The systray background color.
-- @beautiful beautiful.bg_systray
-- @param string The color (string like "#ff0000" only)
--- The systray icon spacing.
-- @beautiful beautiful.systray_icon_spacing
-- @tparam[opt=0] integer The icon spacing
local function should_display_on(s)
if display_on_screen == "primary" then
return s == capi.screen.primary
@ -138,12 +147,16 @@ function systray:set_screen(s)
end
end
--- Create the systray widget.
-- Note that this widget can only exist once.
-- @tparam boolean revers Show in the opposite direction
-- @treturn table The new `systray` widget
-- @function wibox.widget.systray
local function new(revers)
local ret = wbase.make_widget()
for k, v in pairs(systray) do
ret[k] = v
end
util.table.crush(ret, systray, true)
if revers then
ret:set_reverse(true)

View File

@ -1,4 +1,6 @@
---------------------------------------------------------------------------
--
--@DOC_wibox_widget_defaults_textbox_EXAMPLE@
-- @author Uli Schlachter
-- @author dodo
-- @copyright 2010, 2011 Uli Schlachter, dodo
@ -10,54 +12,56 @@ local base = require("wibox.widget.base")
local gdebug = require("gears.debug")
local beautiful = require("beautiful")
local lgi = require("lgi")
local util = require("awful.util")
local Pango = lgi.Pango
local PangoCairo = lgi.PangoCairo
local type = type
local setmetatable = setmetatable
local pairs = pairs
local textbox = { mt = {} }
--- The textbox font.
-- @beautiful beautiful.font
--- Set the DPI of a Pango layout
local function setup_dpi(box, dpi)
if box.dpi ~= dpi then
box.dpi = dpi
box._ctx:set_resolution(dpi)
box._layout:context_changed()
if box._private.dpi ~= dpi then
box._private.dpi = dpi
box._private.ctx:set_resolution(dpi)
box._private.layout:context_changed()
end
end
--- Setup a pango layout for the given textbox and dpi
local function setup_layout(box, width, height, dpi)
box._layout.width = Pango.units_from_double(width)
box._layout.height = Pango.units_from_double(height)
box._private.layout.width = Pango.units_from_double(width)
box._private.layout.height = Pango.units_from_double(height)
setup_dpi(box, dpi)
end
--- Draw the given textbox on the given cairo context in the given geometry
-- Draw the given textbox on the given cairo context in the given geometry
function textbox:draw(context, cr, width, height)
setup_layout(self, width, height, context.dpi)
cr:update_layout(self._layout)
local _, logical = self._layout:get_pixel_extents()
cr:update_layout(self._private.layout)
local _, logical = self._private.layout:get_pixel_extents()
local offset = 0
if self._valign == "center" then
if self._private.valign == "center" then
offset = (height - logical.height) / 2
elseif self._valign == "bottom" then
elseif self._private.valign == "bottom" then
offset = height - logical.height
end
cr:move_to(0, offset)
cr:show_layout(self._layout)
cr:show_layout(self._private.layout)
end
local function do_fit_return(self)
local _, logical = self._layout:get_pixel_extents()
local _, logical = self._private.layout:get_pixel_extents()
if logical.width == 0 or logical.height == 0 then
return 0, 0
end
return logical.width, logical.height
end
--- Fit the given textbox
-- Fit the given textbox
function textbox:fit(context, width, height)
setup_layout(self, width, height, context.dpi)
return do_fit_return(self)
@ -92,8 +96,8 @@ end
function textbox:get_preferred_size_at_dpi(dpi)
local max_lines = 2^20
setup_dpi(self, dpi)
self._layout.width = -1 -- no width set
self._layout.height = -max_lines -- show this many lines per paragraph
self._private.layout.width = -1 -- no width set
self._private.layout.height = -max_lines -- show this many lines per paragraph
return do_fit_return(self)
end
@ -106,8 +110,8 @@ end
function textbox:get_height_for_width_at_dpi(width, dpi)
local max_lines = 2^20
setup_dpi(self, dpi)
self._layout.width = Pango.units_from_double(width)
self._layout.height = -max_lines -- show this many lines per paragraph
self._private.layout.width = Pango.units_from_double(width)
self._private.layout.height = -max_lines -- show this many lines per paragraph
local _, h = do_fit_return(self)
return h
end
@ -121,7 +125,7 @@ end
-- @treturn[2] boolean false
-- @treturn[2] string Error message explaining why the markup was invalid.
function textbox:set_markup_silently(text)
if self._markup == text then
if self._private.markup == text then
return true
end
@ -131,9 +135,9 @@ function textbox:set_markup_silently(text)
return false, parsed.message or tostring(parsed)
end
self._markup = text
self._layout.text = parsed
self._layout.attributes = attr
self._private.markup = text
self._private.layout.text = parsed
self._private.layout.attributes = attr
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
return true
@ -141,9 +145,12 @@ end
--- Set the text of the textbox (with
-- [Pango markup](https://developer.gnome.org/pango/stable/PangoMarkupFormat.html)).
-- @property markup
-- @tparam string text The text to set. This can contain pango markup (e.g.
-- `<b>bold</b>`). You can use `awful.util.escape` to escape
-- parts of it.
-- @see text
function textbox:set_markup(text)
local success, message = self:set_markup_silently(text)
if not success then
@ -151,96 +158,117 @@ function textbox:set_markup(text)
end
end
function textbox:get_markup()
return self._private.markup
end
--- Set a textbox' text.
-- @property text
-- @param text The text to display. Pango markup is ignored and shown as-is.
-- @see markup
function textbox:set_text(text)
if self._layout.text == text and self._layout.attributes == nil then
if self._private.layout.text == text and self._private.layout.attributes == nil then
return
end
self._markup = nil
self._layout.text = text
self._layout.attributes = nil
self._private.markup = nil
self._private.layout.text = text
self._private.layout.attributes = nil
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
function textbox:get_text()
return self._private.layout.text
end
--- Set a textbox' ellipsize mode.
-- @property ellipsize
-- @param mode Where should long lines be shortened? "start", "middle" or "end"
function textbox:set_ellipsize(mode)
local allowed = { none = "NONE", start = "START", middle = "MIDDLE", ["end"] = "END" }
if allowed[mode] then
if self._layout:get_ellipsize() == allowed[mode] then
if self._private.layout:get_ellipsize() == allowed[mode] then
return
end
self._layout:set_ellipsize(allowed[mode])
self._private.layout:set_ellipsize(allowed[mode])
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
end
--- Set a textbox' wrap mode.
-- @property wrap
-- @param mode Where to wrap? After "word", "char" or "word_char"
function textbox:set_wrap(mode)
local allowed = { word = "WORD", char = "CHAR", word_char = "WORD_CHAR" }
if allowed[mode] then
if self._layout:get_wrap() == allowed[mode] then
if self._private.layout:get_wrap() == allowed[mode] then
return
end
self._layout:set_wrap(allowed[mode])
self._private.layout:set_wrap(allowed[mode])
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
end
--- Set a textbox' vertical alignment
--- The textbox' vertical alignment
-- @property valign
-- @param mode Where should the textbox be drawn? "top", "center" or "bottom"
function textbox:set_valign(mode)
local allowed = { top = true, center = true, bottom = true }
if allowed[mode] then
if self._valign == mode then
if self._private.valign == mode then
return
end
self._valign = mode
self._private.valign = mode
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
end
--- Set a textbox' horizontal alignment
--- Set a textbox' horizontal alignment.
-- @property align
-- @param mode Where should the textbox be drawn? "left", "center" or "right"
function textbox:set_align(mode)
local allowed = { left = "LEFT", center = "CENTER", right = "RIGHT" }
if allowed[mode] then
if self._layout:get_alignment() == allowed[mode] then
if self._private.layout:get_alignment() == allowed[mode] then
return
end
self._layout:set_alignment(allowed[mode])
self._private.layout:set_alignment(allowed[mode])
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
end
--- Set a textbox' font
-- @property font
-- @param font The font description as string
function textbox:set_font(font)
self._layout:set_font_description(beautiful.get_font(font))
self._private.layout:set_font_description(beautiful.get_font(font))
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end
-- Returns a new textbox
--- Create a new textbox.
-- @tparam[opt=""] string text The textbox content
-- @tparam[opt=false] boolean ignore_markup Ignore the pango/HTML markup
-- @treturn table A new textbox widget
-- @function wibox.widget.textbox
local function new(text, ignore_markup)
local ret = base.make_widget()
local ret = base.make_widget(nil, nil, {enable_properties = true})
for k, v in pairs(textbox) do
if type(v) == "function" then
ret[k] = v
end
end
util.table.crush(ret, textbox, true)
ret._dpi = -1
ret._ctx = PangoCairo.font_map_get_default():create_context()
ret._layout = Pango.Layout.new(ret._ctx)
ret._private.dpi = -1
ret._private.ctx = PangoCairo.font_map_get_default():create_context()
ret._private.layout = Pango.Layout.new(ret._private.ctx)
ret:set_ellipsize("end")
ret:set_wrap("word_char")
@ -263,6 +291,10 @@ function textbox.mt.__call(_, ...)
return new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(textbox, textbox.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,57 @@
---------------------------------------------------------------------------
--- Text clock widget.
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release @AWESOME_VERSION@
-- @classmod wibox.widget.textclock
---------------------------------------------------------------------------
local setmetatable = setmetatable
local os = os
local textbox = require("wibox.widget.textbox")
local timer = require("gears.timer")
local DateTime = require("lgi").GLib.DateTime
local textclock = { mt = {} }
--- This lowers the timeout so that it occurs "correctly". For example, a timeout
-- of 60 is rounded so that it occurs the next time the clock reads ":00 seconds".
local function calc_timeout(real_timeout)
return real_timeout - os.time() % real_timeout
end
--- Create a textclock widget. It draws the time it is in a textbox.
--
-- @tparam[opt=" %a %b %d, %H:%M "] string format The time format.
-- @tparam[opt=60] number timeout How often update the time (in seconds).
-- @treturn table A textbox widget.
-- @function wibox.widget.textclock
function textclock.new(format, timeout)
format = format or " %a %b %d, %H:%M "
timeout = timeout or 60
local w = textbox()
local t
function w._private.textclock_update_cb()
w:set_markup(DateTime.new_now_local():format(format))
t.timeout = calc_timeout(timeout)
t:again()
return true -- Continue the timer
end
t = timer.weak_start_new(timeout, w._private.textclock_update_cb)
t:emit_signal("timeout")
return w
end
function textclock.mt:__call(...)
return textclock.new(...)
end
--@DOC_widget_COMMON@
--@DOC_object_COMMON@
return setmetatable(textclock, textclock.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -82,17 +82,18 @@ assert:register("assertion", "widget_layout", widget_layout, "assertion.widget_l
return {
widget_stub = function(width, height)
local w = object()
w._private = {}
w:add_signal("widget::redraw_needed")
w:add_signal("widget::layout_changed")
w.is_widget = true
w.visible = true
w.opacity = 1
w._private.visible = true
w._private.opacity = 1
if width or height then
w.fit = function()
return width or 10, height or 10
end
end
w._widget_caches = {}
w._private.widget_caches = {}
return w
end,

View File

@ -13,7 +13,7 @@ return { create_wibox = function()
-- Widgets that are aligned to the right
local right_layout = wibox.layout.fixed.horizontal()
local textclock = awful.widget.textclock()
local textclock = wibox.widget.textclock()
right_layout:add(textclock)
right_layout:add(awful.widget.layoutbox(1))

View File

@ -144,11 +144,16 @@ function(run_test test_path namespace template escaped_content)
get_filename_component(${test_path} TEST_FILE_NAME NAME)
set(IMAGE_PATH "${IMAGE_DIR}/AUTOGEN${namespace}_${TEST_FILE_NAME}")
# Use the example testing as coverage source
if (USE_LCOV)
set(LCOV_PATH ${SOURCE_DIR}/.luacov)
endif()
# Execute the script, leave the image extension decision to the test
# SVG is preferred, but PNG is better suited for some tests, like bitmap
# patterns.
execute_process(
COMMAND lua ${template} ${test_path} ${IMAGE_PATH} ${SOURCE_DIR}/.luacov
COMMAND lua ${template} ${test_path} ${IMAGE_PATH} ${LCOV_PATH}
OUTPUT_VARIABLE TEST_OUTPUT
ERROR_VARIABLE TEST_ERROR
)

View File

@ -30,7 +30,7 @@ local function show(cr, skip_fill)
cr:clip()
end
local cr = get_surface(svgpath)
local cr = get_surface(svgpath..".svg")
cr:translate(3,3)
loadfile(filepath)(shape, cr, show)

View File

@ -1,5 +1,29 @@
local lgi = require("lgi")
local Pango = lgi.Pango
local cairo = lgi.cairo
-- A simple Awesome logo
local function logo()
local img = cairo.ImageSurface.create(cairo.Format.ARGB32, 22, 22)
local cr = cairo.Context(img)
-- Awesome default #555555
cr:set_source_rgb(0.21568627451, 0.21568627451, 0.21568627451)
cr:paint()
cr:set_source_rgb(1,1,1)
cr:rectangle(0, 7, 15, 1)
cr:fill()
cr:rectangle(15, 15, 1, 7)
cr:fill()
cr:rectangle(8, 15, 7, 1)
cr:fill()
return img
end
-- Default theme for the documentation examples
local module = {
@ -10,9 +34,17 @@ local module = {
border_width = 1.5 ,
-- Fake resources handling
xresources = require("beautiful.xresources")
xresources = require("beautiful.xresources"),
awesome_icon = logo()
}
module.graph_bg = module.bg_normal
module.graph_fg = module.bg_highlight
module.progressbar_bg = module.bg_normal
module.progressbar_fg = module.bg_highlight
local f = Pango.FontDescription.from_string("sans 8")
function module.get_font()

View File

@ -10,17 +10,17 @@ parent : setup {
{
text_widget,
bg = '#ff0000',
widget = wibox.widget.background
widget = wibox.container.background
},
{
text_widget,
bg = '#00ff00',
widget = wibox.widget.background
widget = wibox.container.background
},
{
text_widget,
bg = '#0000ff',
widget = wibox.widget.background
widget = wibox.container.background
},
spacing = 10,
layout = wibox.layout.fixed.vertical

View File

@ -13,7 +13,7 @@ parent : setup {
shape = gears.shape.circle,
bg = beautiful.bg_normal,
shape_border_color = beautiful.border_color,
widget = wibox.widget.background
widget = wibox.container.background
},
{
-- To solve this, clip the content
@ -25,7 +25,7 @@ parent : setup {
shape = gears.shape.circle,
bg = beautiful.bg_normal,
shape_border_color = beautiful.border_color,
widget = wibox.widget.background
widget = wibox.container.background
},
spacing = 10,
layout = wibox.layout.fixed.vertical

View File

@ -10,17 +10,17 @@ parent : setup {
{
text_widget,
fg = '#ff0000',
widget = wibox.widget.background
widget = wibox.container.background
},
{
text_widget,
fg = '#00ff00',
widget = wibox.widget.background
widget = wibox.container.background
},
{
text_widget,
fg = '#0000ff',
widget = wibox.widget.background
widget = wibox.container.background
},
spacing = 10,
layout = wibox.layout.fixed.vertical

View File

@ -14,7 +14,7 @@ parent : setup {
bg = beautiful.bg_normal,
shape_border_color = beautiful.border_color,
shape_border_width = beautiful.border_width,
widget = wibox.widget.background
widget = wibox.container.background
},
{
-- To solve this, use a margin
@ -27,13 +27,13 @@ parent : setup {
right = 10,
top = 3,
bottom = 3,
widget = wibox.layout.margin
widget = wibox.container.margin
},
shape = gears.shape.hexagon,
bg = beautiful.bg_normal,
shape_border_color = beautiful.border_color,
shape_border_width = beautiful.border_width,
widget = wibox.widget.background
widget = wibox.container.background
},
spacing = 10,
layout = wibox.layout.fixed.vertical

View File

@ -0,0 +1,24 @@
--DOC_HIDE_ALL
local wibox = require("wibox")
local gears = {shape = require("gears.shape")}
local beautiful = require("beautiful")
return {
text = "Before",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
{
{
text = "After",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
shape = gears.shape.circle,
shape_border_width = 5,
shape_border_color = "#ff0000",
bg = beautiful.bg_highlight,
widget = wibox.container.background
}

View File

@ -0,0 +1,27 @@
--DOC_HIDE_ALL
local wibox = require("wibox")
return {
text = "Some long text",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
{
{
{
{
text = "Some long text",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
strategy = "max",
height = 8,
width = 50,
widget = wibox.container.constraint
},
layout = wibox.layout.fixed.horizontal,
},
layout = wibox.layout.fixed.vertical,
}

View File

@ -0,0 +1,54 @@
--DOC_HIDE_ALL
local wibox = require("wibox")
local beautiful = require("beautiful")
return {
nil,
{
nil,
{
{
text = "Before",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
bg = beautiful.bg_highlight,
widget = wibox.container.background
},
nil,
expand = "none",
layout = wibox.layout.align.horizontal,
},
nil,
expand = "none",
layout = wibox.layout.align.vertical,
},
{
nil,
{
nil,
{
{
{
text = "After",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
bg = beautiful.bg_highlight,
widget = wibox.container.background
},
top = 5,
left = 20,
color = "#ff0000",
widget = wibox.container.margin,
},
nil,
expand = "none",
layout = wibox.layout.align.horizontal,
},
nil,
expand = "none",
layout = wibox.layout.align.vertical,
}

View File

@ -0,0 +1,19 @@
--DOC_HIDE_ALL
local wibox = require("wibox")
return {
text = "Before",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
{
{
text = "After",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
reflection = {horizontal = true},
widget = wibox.container.mirror
}

View File

@ -0,0 +1,19 @@
--DOC_HIDE_ALL
local wibox = require("wibox")
return {
text = "Before",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
{
{
text = "After",
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
direction = "east",
widget = wibox.container.rotate
}

View File

@ -0,0 +1,92 @@
local file_path, image_path, luacovpath = ...
-- Set the global shims
-- luacheck: globals awesome client tag drawin screen
awesome = require( "awesome" )
client = require( "client" )
tag = require( "tag" )
drawin = require( "drawin" )
screen = require( "screen" )
-- Force luacheck to be silent about setting those as unused globals
assert(awesome and client and tag)
local beautiful = require( "beautiful" )
local wibox = require( "wibox" )
local surface = require( "gears.surface" )
local shape = require( "gears.shape" )
-- If luacov is available, use it. Else, do nothing.
pcall(function()
require("luacov.runner")(luacovpath)
end)
-- Let the test request a size and file format
local before, after = loadfile(file_path)()
local container = wibox.widget {
{
{
{
before,
shape_border_color = beautiful.border_color,
shape_border_width = beautiful.border_width,
shape = shape.rounded_rect,
widget = wibox.container.background,
},
strategy = 'exact',
width = 70,
height = 40,
widget = wibox.container.constraint
},
{
{
{
text = " ",
widget = wibox.widget.textbox,
},
bg = beautiful.bg_normal,
shape_border_color = beautiful.border_color,
shape_border_width = beautiful.border_width,
widget = wibox.container.background,
shape = shape.transform(shape.arrow)
: rotate_at(15,15,math.pi/2)
: translate(0,-8)
: scale(0.9, 0.9),
},
strategy = 'exact',
width = 42,
height = 40,
widget = wibox.container.constraint
},
{
{
after,
shape_border_color = beautiful.border_color,
shape_border_width = beautiful.border_width,
shape = shape.rounded_rect,
widget = wibox.container.background,
},
strategy = 'exact',
width = 70,
height = 40,
widget = wibox.container.constraint
},
layout = wibox.layout.align.horizontal
},
margins = 10,
widget = wibox.container.margin,
}
-- Emulate the event loop for 10 iterations
for _ = 1, 10 do
awesome:emit_signal("refresh")
end
-- Get the example fallback size (the tests can return a size if the want)
local f_w, f_h = container:fit({dpi=96}, 9999, 9999)
-- Save to the output file
local img = surface.widget_to_svg(container, image_path..".svg", f_w, f_h)
img:finish()

View File

@ -0,0 +1,66 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local gears = {shape = require("gears.shape")} --DOC_HIDE
local beautiful = require("beautiful") --DOC_HIDE
local function create_arrow(text) --DOC_HIDE
return { --DOC_HIDE
{ --DOC_HIDE
{ --DOC_HIDE
text = text, --DOC_HIDE
align = "center", --DOC_HIDE
valign = "center", --DOC_HIDE
widget = wibox.widget.textbox, --DOC_HIDE
}, --DOC_HIDE
shape = gears.shape.arrow, --DOC_HIDE
bg = beautiful.bg_normal, --DOC_HIDE
shape_border_color = beautiful.border_color, --DOC_HIDE
shape_border_width = beautiful.border_width, --DOC_HIDE
widget = wibox.container.background --DOC_HIDE
}, --DOC_HIDE
strategy = 'exact', --DOC_HIDE
width = 70, --DOC_HIDE
height = 70, --DOC_HIDE
widget = wibox.container.constraint --DOC_HIDE
} --DOC_HIDE
end --DOC_HIDE
local normal = create_arrow("Normal")
local north = wibox.container {
create_arrow("North"),
direction = "north",
widget = wibox.container.rotate
}
local south = wibox.container {
create_arrow("South"),
direction = "south",
widget = wibox.container.rotate
}
local east = wibox.container {
create_arrow("East"),
direction = "east",
widget = wibox.container.rotate
}
local west = wibox.container {
create_arrow("West"),
direction = "west",
widget = wibox.container.rotate
}
parent : setup { --DOC_HIDE
normal, --DOC_HIDE
north, --DOC_HIDE
south, --DOC_HIDE
east, --DOC_HIDE
west, --DOC_HIDE
spacing = 10, --DOC_HIDE
layout = wibox.layout.fixed.horizontal --DOC_HIDE
} --DOC_HIDE

View File

@ -0,0 +1,10 @@
local generic_widget = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
return --DOC_HIDE
wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
layout = wibox.layout.align.horizontal
}

View File

@ -0,0 +1,10 @@
local generic_widget = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
return --DOC_HIDE
wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
layout = wibox.layout.fixed.horizontal
}

View File

@ -0,0 +1,10 @@
local generic_widget = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
return --DOC_HIDE
wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
layout = wibox.layout.flex.horizontal
}

View File

@ -0,0 +1,13 @@
local generic_widget = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local w = wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
layout = wibox.layout.ratio.horizontal
}
w:ajust_ratio(2, 0.44, 0.33, 0.22)
return w --DOC_HIDE

View File

@ -0,0 +1,10 @@
local generic_widget = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
return --DOC_HIDE
wibox.widget {
generic_widget( "first" ),
generic_widget( "second" ),
generic_widget( "third" ),
layout = wibox.layout.stack
}

View File

@ -0,0 +1,62 @@
local file_path, image_path, luacovpath = ...
-- Set the global shims
-- luacheck: globals awesome client tag drawin screen
awesome = require( "awesome" )
client = require( "client" )
tag = require( "tag" )
drawin = require( "drawin" )
screen = require( "screen" )
-- Force luacheck to be silent about setting those as unused globals
assert(awesome and client and tag)
local wibox = require( "wibox" )
local surface = require( "gears.surface" )
local color = require( "gears.color" )
local beautiful = require( "beautiful" )
-- If luacov is available, use it. Else, do nothing.
pcall(function()
require("luacov.runner")(luacovpath)
end)
-- Create a generic rectangle widget to show layout disposition
local function generic_widget(text)
return {
{
{
draw = function(_, _, cr, width, height)
cr:set_source(color(beautiful.bg_normal))
cr:set_line_width(3)
cr:rectangle(0, 0, width, height)
cr:fill_preserve()
cr:set_source(color(beautiful.border_color))
cr:stroke()
end,
widget = wibox.widget.base.make_widget
},
text and {
align = "center",
valign = "center",
text = text,
widget = wibox.widget.textbox
} or nil,
widget = wibox.layout.stack
},
margins = 5,
widget = wibox.container.margin,
}
end
-- Let the test request a size and file format
local widget, w, h = loadfile(file_path)(generic_widget)
-- Emulate the event loop for 10 iterations
for _ = 1, 10 do
awesome:emit_signal("refresh")
end
-- Save to the output file
local img = surface["widget_to_svg"](widget, image_path..".svg", w or 200, h or 30)
img:finish()

View File

@ -0,0 +1,22 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local data = { --DOC_HIDE
3, 5, 6,4, 11,15,19,29,17,17,14,0,0,3,1,0,0, 22, 17,7, 1,0,0,5, --DOC_HIDE
3, 5, 6,4, 11,15,19,29,17,17,14,0,0,3,1,0,0, 22, 17,7, 1,0,0,5, --DOC_HIDE
3, 5, 6,4, 11,15,19,29,17,17,14,0,0,3,1,0,0, 22, 17,7, 1,0,0,5, --DOC_HIDE
3, 5, 6,4, 11,15,19,29,17,17,14,0,0,3,1,0,0, 22, 17,7, 1,0,0,5, --DOC_HIDE
3, 5, 6,4, 11,15,19,29,17,17,14,0,0,3,1,0,0, 22, 17,7, 1,0,0,5, --DOC_HIDE
} --DOC_HIDE
local w = --DOC_HIDE
wibox.widget {
max_value = 29,
widget = wibox.widget.graph
}
parent:add( w ) --DOC_HIDE
for _, v in ipairs(data) do --DOC_HIDE
w:add_value(v) --DOC_HIDE
end --DOC_HIDE

View File

@ -0,0 +1,13 @@
local parent = ... --DOC_HIDE
local wibox = require( "wibox" ) --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
parent:add( --DOC_HIDE
wibox.widget {
image = beautiful.awesome_icon,
resize = false,
widget = wibox.widget.imagebox
}
) --DOC_HIDE

View File

@ -0,0 +1,12 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
parent:add( --DOC_HIDE
wibox.widget {
max_value = 1,
value = 0.33,
widget = wibox.widget.progressbar
}
) --DOC_HIDE

View File

@ -0,0 +1,13 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
parent:add( --DOC_HIDE
wibox.widget{
markup = "This <i>is</i> a <b>textbox</b>!!!",
align = "center",
valign = "center",
widget = wibox.widget.textbox
}
) --DOC_HIDE

View File

@ -30,14 +30,14 @@ table.insert(steps, function()
widget = wibox.widget.textbox,
},
bg = "#ff0000",
widget = wibox.widget.background
widget = wibox.container.background
},
{
{
widget = button,
},
bg = "#ff00ff",
widget = wibox.widget.background
widget = wibox.container.background
},
{
{
@ -45,14 +45,14 @@ table.insert(steps, function()
widget = wibox.widget.textbox,
},
bg = "#0000ff",
widget = wibox.widget.background
widget = wibox.container.background
},
layout = wibox.layout.flex.vertical
}
awful.placement.centered(w)
img = button._image
img = button._private.image
assert(img)
return true
@ -68,7 +68,7 @@ table.insert(steps, function()
end)
table.insert(steps, function()
assert(button._image ~= img)
assert(button._private.image ~= img)
return true
end)
@ -77,14 +77,14 @@ end)
table.insert(steps, function()
root.fake_input("button_release", 1)
assert(button._image ~= img)
assert(button._private.image ~= img)
return true
end)
-- Test a button press/release outside of the widget
table.insert(steps, function()
assert(button._image == img)
assert(button._private.image == img)
root.fake_input("button_press", 1)
@ -92,14 +92,14 @@ table.insert(steps, function()
end)
table.insert(steps, function()
assert(button._image ~= img)
assert(button._private.image ~= img)
return true
end)
table.insert(steps, function()
-- just make sure the button is not released for nothing
assert(button._image ~= img)
assert(button._private.image ~= img)
-- test if the button is released when the mouse move out
awful.placement.right(mouse--[[, {parent = w}]])
@ -109,7 +109,7 @@ table.insert(steps, function()
end)
table.insert(steps, function()
assert(button._image == img)
assert(button._private.image == img)
return true
end)

View File

@ -63,7 +63,7 @@ collectable(wibox.layout.align.horizontal())
-- Then some random widgets from awful
collectable(awful.widget.launcher({ image = cairo.ImageSurface(cairo.Format.ARGB32, 20, 20), command = "bash" }))
collectable(awful.widget.prompt())
collectable(awful.widget.textclock())
collectable(wibox.widget.textclock())
collectable(awful.widget.layoutbox(1))
-- Some widgets do things via timer.delayed_call