From 22d656903563f75678f3634964731ccf93355dfd Mon Sep 17 00:00:00 2001 From: ache Date: Mon, 13 Mar 2017 23:17:19 +0100 Subject: Init commit --- lib/wibox/widget/slider.lua | 709 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 709 insertions(+) create mode 100644 lib/wibox/widget/slider.lua (limited to 'lib/wibox/widget/slider.lua') diff --git a/lib/wibox/widget/slider.lua b/lib/wibox/widget/slider.lua new file mode 100644 index 0000000..fd590c6 --- /dev/null +++ b/lib/wibox/widget/slider.lua @@ -0,0 +1,709 @@ +--------------------------------------------------------------------------- +-- An interactive mouse based slider widget. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_slider.svg) +-- +-- @usage +--wibox.widget { +-- bar_shape = gears.shape.rounded_rect, +-- bar_height = 3, +-- bar_color = beautiful.border_color, +-- handle_color = beautiful.bg_normal, +-- handle_shape = gears.shape.circle, +-- handle_border_color = beautiful.border_color, +-- handle_border_width = 1, +-- value = 25, +-- widget = wibox.widget.slider, +--} +-- +-- @author Grigory Mishchenko <grishkokot@gmail.com> +-- @author Emmanuel Lepage Vallee <elv1313@gmail.com> +-- @copyright 2015 Grigory Mishchenko, 2016 Emmanuel Lepage Vallee +-- @classmod wibox.widget.slider +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local type = type +local color = require("gears.color") +local util = require("awful.util") +local beautiful = require("beautiful") +local base = require("wibox.widget.base") +local shape = require("gears.shape") +local capi = { + mouse = mouse, + mousegrabber = mousegrabber, + root = root, +} + +local slider = {mt={}} + +--- The slider handle shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_shape.svg) +-- +-- +-- @property handle_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The slider handle color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_color.svg) +-- +-- +-- @property handle_color +-- @param color + +--- The slider handle margins. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_margins.svg) +-- +-- +-- @property handle_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The slider handle width. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_width.svg) +-- +-- +-- @property handle_width +-- @param number + +--- The handle border_color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_border.svg) +-- +-- +-- @property handle_border_color +-- @param color + +--- The handle border width. +-- @property handle_border_width +-- @param[opt=0] number + +--- The bar (background) shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_shape.svg) +-- +-- +-- @property bar_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The bar (background) height. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_height.svg) +-- +-- +-- @property bar_height +-- @param number + +--- The bar (background) color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_color.svg) +-- +-- +-- @property bar_color +-- @param color + +--- The bar (background) margins. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_margins.svg) +-- +-- +-- @property bar_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The bar (background) border width. +-- @property bar_border_width +-- @param[opt=0] numbergb + +--- The bar (background) border_color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_border.svg) +-- +-- +-- @property bar_border_color +-- @param color + +--- The slider value. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_value.svg) +-- +-- +-- @property value +-- @param[opt=0] number + +--- The slider minimum value. +-- @property minimum +-- @param[opt=0] number + +--- The slider maximum value. +-- @property maximum +-- @param[opt=100] number + +--- The bar (background) border width. +-- @beautiful beautiful.slider_bar_border_width +-- @param number + +--- The bar (background) border color. +-- @beautiful beautiful.slider_bar_border_color +-- @param color + +--- The handle border_color. +-- @beautiful beautiful.slider_handle_border_color +-- @param color + +--- The handle border width. +-- @beautiful beautiful.slider_handle_border_width +-- @param number + +--- The handle . +-- @beautiful beautiful.slider_handle_width +-- @param number + +-- @beautiful beautiful.slider_handle_color +-- @param color + +--- The handle shape. +-- @beautiful beautiful.slider_handle_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The bar (background) shape. +-- @beautiful beautiful.slider_bar_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The bar (background) height. +-- @beautiful beautiful.slider_bar_height +-- @param number + +--- The bar (background) margins. +-- @beautiful beautiful.slider_bar_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The slider handle margins. +-- @beautiful beautiful.slider_handle_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The bar (background) color. +-- @beautiful beautiful.slider_bar_color +-- @param color + +local properties = { + -- Handle + handle_shape = shape.rectangle, + handle_color = false, + handle_margins = {}, + handle_width = false, + handle_border_width = 0, + handle_border_color = false, + + -- Bar + bar_shape = shape.rectangle, + bar_height = false, + bar_color = false, + bar_margins = {}, + bar_border_width = 0, + bar_border_color = false, + + -- Content + value = 0, + minimum = 0, + maximum = 100, +} + +-- Create the accessors +for prop in pairs(properties) do + slider["set_"..prop] = function(self, value) + local changed = self._private[prop] ~= value + self._private[prop] = value + + if changed then + self:emit_signal("property::"..prop) + self:emit_signal("widget::redraw_needed") + end + end + + slider["get_"..prop] = function(self) + -- Ignoring the false's is on purpose + return self._private[prop] == nil + and properties[prop] + or self._private[prop] + end +end + +-- Add some validation to set_value +function slider:set_value(value) + value = math.min(value, self:get_maximum()) + value = math.max(value, self:get_minimum()) + local changed = self._private.value ~= value + + self._private.value = value + + if changed then + self:emit_signal( "property::value" ) + self:emit_signal( "widget::redraw_needed" ) + end +end + +local function get_extremums(self) + local min = self._private.minimum or properties.minimum + local max = self._private.maximum or properties.maximum + local interval = max - min + + return min, max, interval +end + +function slider:draw(_, cr, width, height) + local bar_height = self._private.bar_height + + -- If there is no background, then skip this + local bar_color = self._private.bar_color + or beautiful.slider_bar_color + + if bar_color then + cr:set_source(color(bar_color)) + end + + local margins = self._private.bar_margins + or beautiful.slider_bar_margins + + local x_offset, right_margin, y_offset = 0, 0 + + if margins then + if type(margins) == "number" then + bar_height = bar_height or (height - 2*margins) + x_offset, y_offset = margins, margins + right_margin = margins + else + bar_height = bar_height or ( + height - (margins.top or 0) - (margins.bottom or 0) + ) + x_offset, y_offset = margins.left or 0, margins.top or 0 + right_margin = margins.right or 0 + end + else + bar_height = bar_height or beautiful.slider_bar_height or height + y_offset = (height - bar_height)/2 + end + + + cr:translate(x_offset, y_offset) + + local bar_shape = self._private.bar_shape + or beautiful.slider_bar_shape + or properties.bar_shape + + local bar_border_width = self._private.bar_border_width + or beautiful.slider_bar_border_width + or properties.bar_border_width + + bar_shape(cr, width - x_offset - right_margin, bar_height or height) + + if bar_color then + if bar_border_width == 0 then + cr:fill() + else + cr:fill_preserve() + end + end + + -- Draw the bar border + if bar_border_width > 0 then + local bar_border_color = self._private.bar_border_color + or beautiful.slider_bar_border_color + or properties.bar_border_color + + cr:set_line_width(bar_border_width) + + if bar_border_color then + cr:save() + cr:set_source(color(bar_border_color)) + cr:stroke() + cr:restore() + else + cr:stroke() + end + end + + cr:translate(-x_offset, -y_offset) + + -- Paint the handle + local handle_color = self._private.handle_color + or beautiful.slider_handle_color + + -- It is ok if there is no color, it will be inherited + if handle_color then + cr:set_source(color(handle_color)) + end + + local handle_height, handle_width = height, self._private.handle_width + or beautiful.slider_handle_width + or height/2 + + local handle_shape = self._private.handle_shape + or beautiful.slider_handle_shape + or properties.handle_shape + + -- Lets get the margins for the handle + margins = self._private.handle_margins + or beautiful.slider_handle_margins + + x_offset, y_offset = 0, 0 + + if margins then + if type(margins) == "number" then + x_offset, y_offset = margins, margins + handle_width = handle_width - 2*margins + handle_height = handle_height - 2*margins + else + x_offset, y_offset = margins.left or 0, margins.top or 0 + handle_width = handle_width - + (margins.left or 0) - (margins.right or 0) + handle_height = handle_height - + (margins.top or 0) - (margins.bottom or 0) + end + end + + local value = self._private.value or self._private.min or 0 + + -- Get the widget size back to it's non-transfored value + local min, _, interval = get_extremums(self) + local rel_value = ((value-min)/interval) * (width-handle_width) + + cr:translate(x_offset + rel_value, y_offset) + + local handle_border_width = self._private.handle_border_width + or beautiful.slider_handle_border_width + or properties.handle_border_width or 0 + + handle_shape(cr, handle_width, handle_height) + + if handle_border_width > 0 then + cr:fill_preserve() + else + cr:fill() + end + + -- Draw the handle border + if handle_border_width > 0 then + local handle_border_color = self._private.handle_border_color + or beautiful.slider_handle_border_color + or properties.handle_border_color + + if handle_border_color then + cr:set_source(color(handle_border_color)) + end + + cr:set_line_width(handle_border_width) + cr:stroke() + end +end + +function slider:fit(_, width, height) + -- Use all the space, this should be used with a constraint widget + return width, height +end + +-- Move the handle to the correct location +local function move_handle(self, width, x, _) + local _, _, interval = get_extremums(self) + self:set_value(math.floor((x*interval)/width)) +end + +local function mouse_press(self, x, y, button_id, _, geo) + if button_id ~= 1 then return end + + local matrix_from_device = geo.hierarchy:get_matrix_from_device() + + -- Sigh. geo.width/geo.height is in device space. We need it in our own + -- coordinate system + local width = geo.widget_width + + move_handle(self, width, x, y) + + -- Calculate a matrix transforming from screen coordinates into widget coordinates + local wgeo = geo.drawable.drawable:geometry() + local matrix = matrix_from_device:translate(-wgeo.x, -wgeo.y) + + capi.mousegrabber.run(function(mouse) + if not mouse.buttons[1] then + return false + end + + -- Calculate the point relative to the widget + move_handle(self, width, matrix:transform_point(mouse.x, mouse.y)) + + return true + end,"fleur") +end + +--- Create a slider widget. +-- @tparam[opt={}] table args +-- @function wibox.widget.slider +local function new(args) + local ret = base.make_widget(nil, nil, { + enable_properties = true, + }) + + util.table.crush(ret._private, args or {}) + + util.table.crush(ret, slider, true) + + ret:connect_signal("button::press", mouse_press) + + return ret +end + +function slider.mt:__call(_, ...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(slider, slider.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 -- cgit v1.2.3-54-g00ecf