aboutsummaryrefslogtreecommitdiff
path: root/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua
diff options
context:
space:
mode:
Diffstat (limited to '.config/awesome/ui/statusbar/panel/widgets/imagebox.lua')
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/imagebox.lua720
1 files changed, 0 insertions, 720 deletions
diff --git a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua b/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua
deleted file mode 100644
index 5caadc5..0000000
--- a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua
+++ /dev/null
@@ -1,720 +0,0 @@
----------------------------------------------------------------------------
--- A widget to display an image.
---
--- The `wibox.widget.imagebox` is part of the Awesome WM's widget system
--- (see @{03-declarative-layout.md}).
---
--- This widget displays an image. The image can be a file,
--- a cairo image surface, or an rsvg handle object (see the
--- [image property](#image)).
---
--- Examples using a `wibox.widget.imagebox`:
--- ---
---
--- @DOC_wibox_widget_defaults_imagebox_EXAMPLE@
---
--- Alternatively, you can declare the `imagebox` widget using the
--- declarative pattern (both variants are strictly equivalent):
---
--- @DOC_wibox_widget_declarative-pattern_imagebox_EXAMPLE@
---
--- @author Uli Schlachter
--- @copyright 2010 Uli Schlachter
--- @widgetmod wibox.widget.imagebox
--- @supermodule wibox.widget.base
----------------------------------------------------------------------------
-
-local lgi = require "lgi"
-local cairo = lgi.cairo
-
-local base = require "wibox.widget.base"
-local gdebug = require "gears.debug"
-local gtable = require "gears.table"
-local surface = require "gears.surface"
-local setmetatable = setmetatable
-local type = type
-local math = math
-
-local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
-
--- Safe load for optional Rsvg module
-local Rsvg = nil
-do
- local success, err = pcall(function()
- Rsvg = lgi.Rsvg
- end)
- if not success then
- gdebug.print_warning(debug.traceback("Could not load Rsvg: " .. tostring(err)))
- end
-end
-
-local imagebox = { mt = {} }
-
-local rsvg_handle_cache = setmetatable({}, { __mode = "k" })
-
----Load rsvg handle form image file
--- @tparam string file Path to svg file.
--- @return Rsvg handle
--- @treturn table A table where cached data can be stored.
-local function load_rsvg_handle(file)
- if not Rsvg then
- return
- end
-
- local cache = (rsvg_handle_cache[file] or {})["handle"]
-
- if cache then
- return cache, rsvg_handle_cache[file]
- end
-
- local handle, err
-
- if file:match "<[?]?xml" or file:match "<svg" then
- handle, err = Rsvg.Handle.new_from_data(file)
- else
- handle, err = Rsvg.Handle.new_from_file(file)
- end
-
- if not err then
- rsvg_handle_cache[file] = rsvg_handle_cache[file] or {}
- rsvg_handle_cache[file]["handle"] = handle
- return handle, rsvg_handle_cache[file]
- end
-end
-
----Apply cairo surface for given imagebox widget
-local function set_surface(ib, surf)
- local is_surf_valid = surf.width > 0 and surf.height > 0
- if not is_surf_valid then
- return false
- end
-
- ib._private.default = { width = surf.width, height = surf.height }
- ib._private.handle = nil
- ib._private.image = surf
- return true
-end
-
----Apply RsvgHandle for given imagebox widget
-local function set_handle(ib, handle, cache)
- local dim = handle:get_dimensions()
- local is_handle_valid = dim.width > 0 and dim.height > 0
- if not is_handle_valid then
- return false
- end
-
- ib._private.default = { width = dim.width, height = dim.height }
- ib._private.handle = handle
- ib._private.cache = cache
- ib._private.image = nil
-
- return true
-end
-
----Try to load some image object from file then apply it to imagebox.
----@tparam table ib Imagebox
----@tparam string file Image file name
----@tparam function image_loader Function to load image object from file
----@tparam function image_setter Function to set image object to imagebox
----@treturn boolean True if image was successfully applied
-local function load_and_apply(ib, file, image_loader, image_setter)
- local image_applied
- local object, cache = image_loader(file)
-
- if object then
- image_applied = image_setter(ib, object, cache)
- end
- return image_applied
-end
-
----Update the cached size depending on the stylesheet and dpi.
---
--- It's necessary because a single RSVG handle can be used by
--- many imageboxes. So DPI and Stylesheet need to be set each time.
-local function update_dpi(self, ctx)
- if not self._private.handle then
- return
- end
-
- local dpi = self._private.auto_dpi and ctx.dpi or self._private.dpi or nil
-
- local need_dpi = dpi and self._private.last_dpi ~= dpi
-
- local need_style = self._private.handle.set_stylesheet and self._private.stylesheet
-
- local old_size = self._private.default and self._private.default.width
-
- if dpi and dpi ~= self._private.cache.dpi then
- if type(dpi) == "table" then
- self._private.handle:set_dpi_x_y(dpi.x, dpi.y)
- else
- self._private.handle:set_dpi(dpi)
- end
- end
-
- if need_style and self._private.cache.stylesheet ~= self._private.stylesheet then
- self._private.handle:set_stylesheet(self._private.stylesheet)
- end
-
- -- Reload the size.
- if need_dpi or (need_style and self._private.stylesheet ~= self._private.last_stylesheet) then
- set_handle(self, self._private.handle, self._private.cache)
- end
-
- self._private.last_dpi = dpi
- self._private.cache.dpi = dpi
- self._private.last_stylesheet = self._private.stylesheet
- self._private.cache.stylesheet = self._private.stylesheet
-
- -- This can happen in the constructor when `dpi` is set after `image`.
- if old_size and old_size ~= self._private.default.width then
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- end
-end
-
--- Draw an imagebox with the given cairo context in the given geometry.
-function imagebox:draw(ctx, cr, width, height)
- if width == 0 or height == 0 or not self._private.default then
- return
- end
-
- -- For valign = "top" and halign = "left"
- local translate = {
- x = 0,
- y = 0,
- }
-
- update_dpi(self, ctx)
-
- local w, h = self._private.default.width, self._private.default.height
-
- if self._private.resize then
- -- That's for the "fit" policy.
- local aspects = {
- w = width / w,
- h = height / h,
- }
-
- local policy = {
- w = self._private.horizontal_fit_policy or "auto",
- h = self._private.vertical_fit_policy or "auto",
- }
-
- for _, aspect in ipairs { "w", "h" } do
- if self._private.upscale == false and (w < width and h < height) then
- aspects[aspect] = 1
- elseif self._private.downscale == false and (w >= width and h >= height) then
- aspects[aspect] = 1
- elseif policy[aspect] == "none" then
- aspects[aspect] = 1
- elseif policy[aspect] == "auto" then
- aspects[aspect] = math.min(width / w, height / h)
- elseif policy[aspect] == "cover" then
- aspects[aspect] = math.max(width / w, height / h)
- end
- end
-
- if self._private.halign == "center" then
- translate.x = math.floor((width - w * aspects.w) / 2)
- elseif self._private.halign == "right" then
- translate.x = math.floor(width - (w * aspects.w))
- end
-
- if self._private.valign == "center" then
- translate.y = math.floor((height - h * aspects.h) / 2)
- elseif self._private.valign == "bottom" then
- translate.y = math.floor(height - (h * aspects.h))
- end
-
- cr:translate(translate.x, translate.y)
-
- -- Before using the scale, make sure it is below the threshold.
- local threshold, max_factor = self._private.max_scaling_factor, math.max(aspects.w, aspects.h)
-
- if threshold and threshold > 0 and threshold < max_factor then
- aspects.w = (aspects.w * threshold) / max_factor
- aspects.h = (aspects.h * threshold) / max_factor
- end
-
- -- Set the clip
- if self._private.clip_shape then
- cr:clip(self._private.clip_shape(cr, w * aspects.w, h * aspects.h, unpack(self._private.clip_args)))
- end
-
- cr:scale(aspects.w, aspects.h)
- else
- if self._private.halign == "center" then
- translate.x = math.floor((width - w) / 2)
- elseif self._private.halign == "right" then
- translate.x = math.floor(width - w)
- end
-
- if self._private.valign == "center" then
- translate.y = math.floor((height - h) / 2)
- elseif self._private.valign == "bottom" then
- translate.y = math.floor(height - h)
- end
-
- cr:translate(translate.x, translate.y)
-
- -- Set the clip
- if self._private.clip_shape then
- cr:clip(self._private.clip_shape(cr, w, h, unpack(self._private.clip_args)))
- end
- end
-
- if self._private.handle then
- self._private.handle:render_cairo(cr)
- else
- cr:set_source_surface(self._private.image, 0, 0)
-
- local filter = self._private.scaling_quality
-
- if filter then
- cr:get_source():set_filter(cairo.Filter[filter:upper()])
- end
-
- cr:paint()
- end
-end
-
--- Fit the imagebox into the given geometry
-function imagebox:fit(ctx, width, height)
- if not self._private.default then
- return 0, 0
- end
-
- update_dpi(self, ctx)
-
- local w, h = self._private.default.width, self._private.default.height
-
- if w <= width and h <= height and self._private.upscale == false then
- return w, h
- end
-
- if (w < width or h < height) and self._private.downscale == false then
- return w, h
- end
-
- if self._private.resize or w > width or h > height then
- local aspect = math.min(width / w, height / h)
- return w * aspect, h * aspect
- end
-
- return w, h
-end
-
---- The image rendered by the `imagebox`.
---
--- @property image
--- @tparam[opt=nil] image|nil image
--- @propemits false false
-
---- Set the `imagebox` image.
---
--- The image can be a file, a cairo image surface, or an rsvg handle object
--- (see the [image property](#image)).
--- @method set_image
--- @hidden
--- @tparam image image The image to render.
--- @treturn boolean `true` on success, `false` if the image cannot be used.
--- @usage my_imagebox:set_image(beautiful.awesome_icon)
--- @usage my_imagebox:set_image('/usr/share/icons/theme/my_icon.png')
--- @see image
-function imagebox:set_image(image)
- local setup_succeed
-
- -- Keep the original to prevent the cache from being GCed.
- self._private.original_image = image
-
- if type(image) == "userdata" and not (Rsvg and Rsvg.Handle:is_type_of(image)) then
- -- This function is not documented to handle userdata objects, but
- -- historically it did, and it did by just assuming they refer to a
- -- cairo surface.
- image = surface.load(image)
- end
-
- if type(image) == "string" then
- -- try to load rsvg handle from file
- setup_succeed = load_and_apply(self, image, load_rsvg_handle, set_handle)
-
- if not setup_succeed then
- -- rsvg handle failed, try to load cairo surface with pixbuf
- setup_succeed = load_and_apply(self, image, surface.load, set_surface)
- end
- elseif Rsvg and Rsvg.Handle:is_type_of(image) then
- -- try to apply given rsvg handle
- rsvg_handle_cache[image] = rsvg_handle_cache[image] or {}
- setup_succeed = set_handle(self, image, rsvg_handle_cache[image])
- elseif cairo.Surface:is_type_of(image) then
- -- try to apply given cairo surface
- setup_succeed = set_surface(self, image)
- elseif not image then
- -- nil as argument mean full imagebox reset
- setup_succeed = true
- self._private.handle = nil
- self._private.image = nil
- self._private.default = nil
- end
-
- if not setup_succeed then
- return false
- end
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal "property::image"
- return true
-end
-
---- Set a clip shape for this imagebox.
---
--- A clip shape defines an area and dimension to which the content should be
--- trimmed.
---
--- @DOC_wibox_widget_imagebox_clip_shape_EXAMPLE@
---
--- @property clip_shape
--- @tparam[opt=gears.shape.rectangle] shape clip_shape A `gears.shape` compatible shape function.
--- @propemits true false
--- @see gears.shape
-
---- Set a clip shape for this imagebox.
---
--- A clip shape defines an area and dimensions to which the content should be
--- trimmed.
---
--- Additional parameters will be passed to the clip shape function.
---
--- @tparam function|gears.shape clip_shape A `gears_shape` compatible shape function.
--- @method set_clip_shape
--- @hidden
--- @see gears.shape
--- @see clip_shape
-function imagebox:set_clip_shape(clip_shape, ...)
- self._private.clip_shape = clip_shape
- self._private.clip_args = { ... }
- self:emit_signal "widget::redraw_needed"
- self:emit_signal("property::clip_shape", clip_shape)
-end
-
---- Should the image be resized to fit into the available space?
---
--- Note that `upscale` and `downscale` can affect the value of `resize`.
--- If conflicting values are passed to the constructor, then the result
--- is undefined.
---
--- @DOC_wibox_widget_imagebox_resize_EXAMPLE@
--- @property resize
--- @propemits true false
--- @tparam[opt=true] boolean resize
-
---- Allow the image to be upscaled (made bigger).
---
--- Note that `upscale` and `downscale` can affect the value of `resize`.
--- If conflicting values are passed to the constructor, then the result
--- is undefined.
---
--- @DOC_wibox_widget_imagebox_upscale_EXAMPLE@
--- @property upscale
--- @tparam[opt=self.resize] boolean upscale
--- @see downscale
--- @see resize
-
---- Allow the image to be downscaled (made smaller).
---
--- Note that `upscale` and `downscale` can affect the value of `resize`.
--- If conflicting values are passed to the constructor, then the result
--- is undefined.
---
--- @DOC_wibox_widget_imagebox_downscale_EXAMPLE@
--- @property downscale
--- @tparam[opt=self.resize] boolean downscale
--- @see upscale
--- @see resize
-
---- Set the SVG CSS stylesheet.
---
--- If the image is an SVG (vector graphics), this property allows to set
--- a CSS stylesheet. It can be used to set colors and much more.
---
--- Note that this property is a string, not a path. If the stylesheet is
--- stored on disk, read the content first.
---
---@DOC_wibox_widget_imagebox_stylesheet_EXAMPLE@
---
--- @property stylesheet
--- @tparam[opt=""] string stylesheet
--- @propemits true false
-
---- Set the SVG DPI (dot per inch).
---
--- Force a specific DPI when rendering the `.svg`. For other file formats,
--- this does nothing.
---
--- It can either be a number of a table containing the `x` and `y` keys.
---
--- Please note that DPI and `resize` can "fight" each other and end up
--- making the image smaller instead of bigger.
---
---@DOC_wibox_widget_imagebox_dpi_EXAMPLE@
---
--- @property dpi
--- @tparam[opt=96] number|table dpi
--- @negativeallowed false
--- @propemits true false
--- @see auto_dpi
-
---- Use the object DPI when rendering the SVG.
---
--- By default, the SVG are interpreted as-is. When this property is set,
--- the screen DPI will be passed to the SVG renderer. Depending on which
--- tool was used to create the `.svg`, this may do nothing at all. However,
--- for example, if the `.svg` uses `<text>` elements and doesn't have an
--- hardcoded stylesheet, the result will differ.
---
--- @property auto_dpi
--- @tparam[opt=false] boolean auto_dpi
--- @propemits true false
--- @see dpi
-
-for _, prop in ipairs { "stylesheet", "dpi", "auto_dpi" } do
- imagebox["set_" .. prop] = function(self, value)
- -- It will be set in :fit and :draw. The handle is shared
- -- by multiple imagebox, so it cannot be set just once.
- self._private[prop] = value
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal("property::" .. prop)
- end
-end
-
-function imagebox:set_resize(allowed)
- self._private.resize = allowed
-
- if allowed then
- self._private.downscale = true
- self._private.upscale = true
- self:emit_signal("property::downscale", allowed)
- self:emit_signal("property::upscale", allowed)
- end
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal("property::resize", allowed)
-end
-
-for _, prop in ipairs { "downscale", "upscale" } do
- imagebox["set_" .. prop] = function(self, allowed)
- self._private[prop] = allowed
-
- if self._private.resize ~= (self._private.upscale or self._private.downscale) then
- self._private.resize = self._private.upscale or self._private.downscale
- self:emit_signal("property::resize", self._private.resize)
- end
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal("property::" .. prop, allowed)
- end
-end
-
---- Set the horizontal fit policy.
---
--- Here is the result for a 22x32 image:
---
--- @DOC_wibox_widget_imagebox_horizontal_fit_policy_EXAMPLE@
---
--- @property horizontal_fit_policy
--- @tparam[opt="auto"] string horizontal_fit_policy
--- @propertyvalue "auto" Honor the `resize` variable and preserve the aspect ratio.
--- @propertyvalue "none" Do not resize at all.
--- @propertyvalue "fit" Resize to the widget width.
--- @propertyvalue "cover" Resize to fill widget and preserve the aspect ratio.
--- @propemits true false
--- @see vertical_fit_policy
--- @see resize
-
---- Set the vertical fit policy.
---
--- Here is the result for a 32x22 image:
---
--- @DOC_wibox_widget_imagebox_vertical_fit_policy_EXAMPLE@
---
--- @property vertical_fit_policy
--- @tparam[opt="auto"] string vertical_fit_policy
--- @propertyvalue "auto" Honor the `resize` variable and preserve the aspect ratio.
--- @propertyvalue "none" Do not resize at all.
--- @propertyvalue "fit" Resize to the widget height.
--- @propertyvalue "cover" Resize to fill widget and preserve the aspect ratio.
--- @propemits true false
--- @see horizontal_fit_policy
--- @see resize
-
---- The vertical alignment.
---
--- @DOC_wibox_widget_imagebox_valign_EXAMPLE@
---
--- @property valign
--- @tparam[opt="center"] string valign
--- @propertyvalue "top"
--- @propertyvalue "center"
--- @propertyvalue "bottom"
--- @propemits true false
--- @see wibox.container.place
--- @see halign
-
---- The horizontal alignment.
---
--- @DOC_wibox_widget_imagebox_halign_EXAMPLE@
---
--- @property halign
--- @tparam[opt="center"] string halign
--- @propertyvalue "left"
--- @propertyvalue "center"
--- @propertyvalue "right"
--- @propemits true false
--- @see wibox.container.place
--- @see valign
-
---- The maximum scaling factor.
---
--- If an image is scaled too much, it gets very blurry. This
--- property allows to limit the scaling.
--- Use the properties `valign` and `halign` to control how the image will be
--- aligned.
---
--- In the example below, the original size is 22x22
---
--- @DOC_wibox_widget_imagebox_max_scaling_factor_EXAMPLE@
---
--- @property max_scaling_factor
--- @tparam[opt=0] number max_scaling_factor Use `0` for "no limit".
--- @negativeallowed false
--- @propemits true false
--- @see valign
--- @see halign
--- @see scaling_quality
-
---- Set the scaling aligorithm.
---
--- Depending on how the image is used, what is the "correct" way to
--- scale can change. For example, upscaling a pixel art image should
--- not make it blurry. However, scaling up a photo should not make it
--- blocky.
---
---<table class='widget_list' border=1>
--- <tr style='font-weight: bold;'>
--- <th align='center'>Value</th>
--- <th align='center'>Description</th>
--- </tr>
--- <tr><td>fast</td><td>A high-performance filter</td></tr>
--- <tr><td>good</td><td>A reasonable-performance filter</td></tr>
--- <tr><td>best</td><td>The highest-quality available</td></tr>
--- <tr><td>nearest</td><td>Nearest-neighbor filtering (blocky)</td></tr>
--- <tr><td>bilinear</td><td>Linear interpolation in two dimensions</td></tr>
---</table>
---
--- The image used in the example below has a resolution of 32x22 and is
--- intentionally blocky to highlight the difference.
--- It is zoomed by a factor of 3.
---
--- @DOC_wibox_widget_imagebox_scaling_quality_EXAMPLE@
---
--- @property scaling_quality
--- @tparam[opt="good"] string scaling_quality
--- @propertyvalue "fast" A high-performance filter.
--- @propertyvalue "good" A reasonable-performance filter.
--- @propertyvalue "best" The highest-quality available.
--- @propertyvalue "nearest" Nearest-neighbor filtering (blocky).
--- @propertyvalue "bilinear" Linear interpolation in two dimensions.
--- @propemits true false
--- @see resize
--- @see horizontal_fit_policy
--- @see vertical_fit_policy
--- @see max_scaling_factor
-
-local defaults = {
- halign = "left",
- valign = "top",
- horizontal_fit_policy = "auto",
- vertical_fit_policy = "auto",
- max_scaling_factor = 0,
- scaling_quality = "good",
-}
-
-local function get_default(prop, value)
- if value == nil then
- return defaults[prop]
- end
-
- return value
-end
-
-for prop in pairs(defaults) do
- imagebox["set_" .. prop] = function(self, value)
- if value == self._private[prop] then
- return
- end
-
- self._private[prop] = get_default(prop, value)
- self:emit_signal "widget::redraw_needed"
- self:emit_signal("property::" .. prop, self._private[prop])
- end
-
- imagebox["get_" .. prop] = function(self)
- if self._private[prop] == nil then
- return defaults[prop]
- end
-
- return self._private[prop]
- end
-end
-
---- Returns a new `wibox.widget.imagebox` instance.
---
--- This is the constructor of `wibox.widget.imagebox`. It creates a new
--- instance of imagebox widget.
---
--- Alternatively, the declarative layout syntax can handle
--- `wibox.widget.imagebox` instanciation.
---
--- The image can be a file, a cairo image surface, or an rsvg handle object
--- (see the [image property](#image)).
---
--- Any additional arguments will be passed to the clip shape function.
--- @tparam[opt] image image The image to display (may be `nil`).
--- @tparam[opt] boolean resize_allowed If `false`, the image will be
--- clipped, else it will be resized to fit into the available space.
--- @tparam[opt] function clip_shape A `gears.shape` compatible function.
--- @treturn wibox.widget.imagebox A new `wibox.widget.imagebox` widget instance.
--- @constructorfct wibox.widget.imagebox
-local function new(image, resize_allowed, clip_shape, ...)
- local ret = base.make_widget(nil, nil, { enable_properties = true })
-
- gtable.crush(ret, imagebox, true)
- ret._private.resize = true
-
- if image then
- ret:set_image(image)
- end
-
- if resize_allowed ~= nil then
- ret.resize = resize_allowed
- end
-
- ret._private.clip_shape = clip_shape
- ret._private.clip_args = { ... }
-
- return ret
-end
-
-function imagebox.mt:__call(...)
- return new(...)
-end
-
-return setmetatable(imagebox, imagebox.mt)
-
--- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80