---------------------------------------------------------------------------
-- Place widgets or images on the sides, corner and back of another widget.
--
-- This widget main use case is to surround another widget with an uniform
-- border. It can also be used to create soft shadows. It support CSS style
-- image slicing or widgets within the border. The `awful.layout.align` layout
-- an also be used to achieve border, but make it very hard to make them
-- symmetric on both axis.
--
-- Note that because of legacy reasons, `wibox.container.background` also has
-- good support for borders. If you only need simple shaped strokes, the
-- `background` container is a much better choice. This module is better
-- suited for background images and border widgets.
--
-- Advanced usage
-- ==============
--
-- In this first example, 2 `wibox.container.borders` are combined in a
-- `wibox.layout.stack`. The bottom one is the "real" border and the top
-- layer is used to place some widgets. This is useful to built titlebars.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_dual1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- 
--     local w = wibox.widget {
--         -- This is the background border.
--         {
--             paddings      = {
--                 left   = 5,
--                 top    = 3,
--                 right  = 10,
--                 bottom = 10,
--             },
--             borders       = 20,
--             border_image  = image_path,
--             honor_borders = false,
--             widget        = wibox.container.border
--         },
--         -- This border container is used top place widgets.
--         {
--             {
--                 text          = &#34Center widget&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 widget        = wibox.widget.textbox
--             },
--             border_widgets = {
--                 top_left     = generic_widget(&#34&#34),
--                 top          = generic_widget(&#34top&#34),
--                 top_right    = generic_widget(&#34&#34),
--                 right        = generic_widget(&#34right&#34),
--                 bottom_right = generic_widget(&#34&#34),
--                 bottom       = generic_widget(&#34bottom&#34),
--                 bottom_left  = generic_widget(&#34&#34),
--                 left         = generic_widget(&#34left&#34),
--             },
--             widget = wibox.container.border
--         },
--         layout = wibox.layout.stack
--     }
--
-- This example demonstrates how to use this module to create a client
-- border with a top titlebar and borders. It does so by embedding a
-- `wibox.container.border` into another `wibox.container.border`. The outer
-- container acts as the border around the central area while the inner one
-- spans the top area and contains the titlebar itself. The outer border
-- widgets area can be used to implement border resize.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_titlebar1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- 
--     local w = wibox.widget {
--         {
--             {
--                 text    = &#34Content&#34,
--                 align   = &#34center&#34,
--                 valign  = &#34center&#34,
--                 widget  = wibox.widget.textbox
--             },
--             border_widgets = {
--                 top = {
--                     {
--                         {
--                             {
--                                 stylesheet = &#34#bg1 {stop-color:#ca2b2b&#59} #bg2 {stop-color:#f8b9b9&#59}&#34,
--                                 image      = btn,
--                                 widget     = wibox.widget.imagebox
--                             },
--                             {
--                                 stylesheet = &#34#bg1 {stop-color:#ec9527&#59} #bg2 {stop-color:#ffff9c&#59}&#34,
--                                 image      = btn,
--                                 widget     = wibox.widget.imagebox
--                             },
--                             {
--                                 stylesheet = &#34#bg1 {stop-color:#75b525&#59} #bg2 {stop-color:#e0fda9&#59}&#34,
--                                 image      = btn,
--                                 widget     = wibox.widget.imagebox
--                             },
--                             spacing = 3,
--                             layout  = wibox.layout.fixed.horizontal
--                         },
--                         {
--                             align  = &#34center&#34,
--                             text   = &#34Shameless macOS ripoff&#34,
--                             widget = wibox.widget.textbox
--                         },
--                         layout = wibox.layout.align.horizontal
--                     },
--                     paddings      = 6,
--                     borders       = 14,
--                     border_image  = bg,
--                     honor_borders = false,
--                     fill          = true,
--                     widget        = wibox.container.border
--                 },
--                 right        = generic_widget(&#34&#34),
--                 bottom_right = generic_widget(&#34&#34),
--                 bottom       = generic_widget(&#34&#34),
--                 bottom_left  = generic_widget(&#34&#34),
--                 left         = generic_widget(&#34&#34),
--             },
--             borders = {
--                 top    = 28,
--                 left   = 22,
--                 right  = 22,
--                 bottom = 22,
--             },
--             border_merging = {
--                 top = true
--             },
--             widget = wibox.container.border
--         },
--         bg     = &#34#d9d9d9&#34,
--         shape  = gears.shape.rounded_rect,
--         widget = wibox.container.background
--     }
--
-- This container is like every other widgets. It is possible to override the
-- Cairo drawing functions. This is very useful here to create custom borders
-- using cairo while still re-using the internal geometry handling code.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_custom_draw1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- 
--     local w = wibox.widget {
--         {
--             text   = &#34Center widget&#34,
--             valign = &#34center&#34,
--             align  = &#34center&#34,
--             widget = wibox.widget.textbox
--         },
--         after_draw_children = function(_, _, cr, width, height)
--             cr:set_source_rgba(1,0,0,1)
--             cr:set_dash({1,1},1)
--             cr:rectangle(1, 1, width-2, height-2)
--             cr:rectangle(5, 5, width-10, height-10)
--             cr:stroke()
--         end,
--         borders       = 20,
--         honor_borders = false,
--         widget        = wibox.container.border
--     }
--
--
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_defaults_border.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
--
-- @author Emmanuel Lepage-Vallee
-- @copyright 2021 Emmanuel Lepage-Vallee
-- @containermod wibox.container.border
-- @supermodule wibox.widget.base
local gtable   = require("gears.table")
local imagebox = require("wibox.widget.imagebox")
local base     = require("wibox.widget.base")
local gsurface = require("gears.surface")
local cairo    = require("lgi").cairo

local components = {
    "top_left", "top", "top_right", "right", "bottom_right", "bottom",
    "bottom_left", "left", "fill"
}

local slice_modes = {
    top_left     = "corners",
    top          = "sides",
    top_right    = "corners",
    right        = "sides",
    bottom_right = "corners",
    bottom       = "sides",
    bottom_left  = "corners",
    left         = "sides",
    fill         = "filling"
}

local fit_types = { "corners", "sides", "filling" }

local module = {}

local function imagebox_fit(self, _, w, h)
    return math.min(w, self.source_width or 0), math.min(h, self.source_height or 0)
end

local function fit_common(widget, ctx, max_w, max_h)
    if not widget then return 0, 0 end

    local w, h = widget:fit(ctx, max_w or math.huge, max_h or math.huge)

    return w, h
end

local function uses_slice(self)
    return not (self._private.border_widgets or self._private.border_image_widgets)
end

local function get_widget(self, ctx, component)
    local wids, imgs = self._private.border_widgets or {}, self._private.border_image_widgets or {}
    local slices = self._private.slice_cache and self._private.slice_cache[ctx.dpi] or {}

    return wids  [component]
        or imgs  [component]
        or slices[component]
end

local function get_all_widgets(self, partial, ctx)
    for _, position in ipairs(components) do
        partial[position] = partial[position] or get_widget(self, ctx, position)
    end

    return partial
end

--- Common code to load the `border_image`.
local function setup_origin_common(self, ctx)
    if self._private.original_md then return self._private.original_md end

    local origin_w, origin_h, origin

    local img = self._private.border_image

    -- Try SVG first.
    local style  = imagebox._get_stylesheet(self, self._private.border_image_stylesheet)
    local handle = type(img) == "string" and imagebox._load_rsvg_handle(img, style) or nil

    if handle then
        if style then
            handle:set_stylesheet(style)
        end
        handle:set_dpi(self.border_image_dpi or ctx.dpi)
        local dim = handle:get_dimensions()

        if dim.width > 0 and dim.height > 0 then
            origin_w, origin_h = dim.width, dim.height

            origin = cairo.ImageSurface(cairo.Format.ARGB32, origin_w, origin_h)
            local cr = cairo.Context(origin)
            handle:render_cairo(cr)
        end
    end

    if not origin then
        origin = gsurface(self._private.border_image)

        local err1, _origin_w = pcall(function() return origin:get_width() end)
        local err2, _origin_h = pcall(function() return origin:get_height() end)

        if err1 and err2 then
            origin_w, origin_h = _origin_w, _origin_h
        end
    end

    return {
        width   = origin_w,
        height  = origin_h,
        surface = origin,
        handle  = handle
    }
end

local function compute_side_borders(self, ctx, max_w, max_h)
    local override = self._private.borders or {}
    local mer      = self._private.border_merging or {}

    self._private.original_md = setup_origin_common(self, ctx)

    local m = {
        left   = override.left,
        right  = override.right,
        top    = override.top,
        bottom = override.bottom,
    }

    -- Limit the border to what the surface can provide.
    if uses_slice(self) and (m.left or 0) + (m.right or 0) > self._private.original_md.width then
        local max = math.floor((self._private.original_md.width-1)/2)
        m.left  = math.min(m.left, max)
        m.right = math.min(m.right, max)
    end

    -- Limit the border to what the surface can provide.
    if uses_slice(self) and (m.top or 0) + (m.bottom or 0) > self._private.original_md.height then
        local max = math.floor((self._private.original_md.height-1)/2)
        m.top    = math.min(m.top, max)
        m.bottom = math.min(m.bottom, max)
    end

    local wdgs = {
        left   = get_widget(self, ctx, "left"  ) or false,
        right  = get_widget(self, ctx, "right" ) or false,
        top    = get_widget(self, ctx, "top"   ) or false,
        bottom = get_widget(self, ctx, "bottom") or false,
    }

    -- Call `:fit()` on the sides.
    local l_w, l_h = fit_common(wdgs.left  , ctx, max_w, max_h)
    local r_w, r_h = fit_common(wdgs.right , ctx, max_w, max_h)
    local t_w, t_h = fit_common(wdgs.top   , ctx, max_w, max_h)
    local b_w, b_h = fit_common(wdgs.bottom, ctx, max_w, max_h)

    -- Either use the provided borders of the `:fit()` results.
    m.left   = m.left   or l_w
    m.right  = m.right  or r_w
    m.top    = m.top    or t_h
    m.bottom = m.bottom or b_h

    -- Allow the corner widgets to affect the border size.
    if self._private.expand_corners then
        wdgs.top_left     = get_widget(self, ctx, "top_left"    ) or false
        wdgs.top_right    = get_widget(self, ctx, "top_right"   ) or false
        wdgs.bottom_left  = get_widget(self, ctx, "bottom_left" ) or false
        wdgs.bottom_right = get_widget(self, ctx, "bottom_right") or false

        local tl_w, tl_h = fit_common(wdgs.top_left    , ctx, max_w, max_h)
        local tr_w, tr_h = fit_common(wdgs.top_right   , ctx, max_w, max_h)
        local bl_w, bl_h = fit_common(wdgs.bottom_left , ctx, max_w, max_h)
        local br_w, br_h = fit_common(wdgs.bottom_right, ctx, max_w, max_h)

        m.left   = math.max(m.left  , tl_w, bl_w)
        m.right  = math.max(m.right , tr_w, br_w)
        m.top    = math.max(m.top   , tl_h, tr_h)
        m.bottom = math.max(m.bottom, bl_h, br_h)
    end

    -- The sides should have matching size unless merging is enabled.
    local minimums = {
        left   = mer.right  and l_h or math.max(l_h, r_h),
        right  = mer.left   and r_h or math.max(l_h, r_h),
        top    = mer.bottom and t_w or math.max(t_w, b_w),
        bottom = mer.top    and b_w or math.max(t_w, b_w),
    }

    return m, wdgs, minimums
end

local function compute_borders(self, ctx, width, height, fit_width, fit_height)
    local m, widgets, mins = compute_side_borders(self, ctx, width, height)
    local ret = {}

    -- Add some fallback values.
    m.left   = m.left   or 0
    m.right  = m.right  or 0
    m.top    = m.top    or 0
    m.bottom = m.bottom or 0

    -- Take the center widget size into account.
    width  = math.min(width , (fit_width  or 9999) + m.left + m.right)
    height = math.min(height, (fit_height or 9999) + m.top  + m.right)

    -- Corners (w,h,x,y).
    ret.top_left     = {m.left , m.top   , 0              , 0                }
    ret.top_right    = {m.right, m.top   , width - m.right, 0                }
    ret.bottom_left  = {m.left , m.bottom, 0              , height - m.bottom}
    ret.bottom_right = {m.right, m.bottom, width - m.right, height - m.bottom}

    -- Sides (w,h,x,y).
    ret.left   = {m.left                  , height - m.top - m.bottom, 0            , m.top            }
    ret.right  = {m.right                 , height - m.top - m.bottom, width-m.right, m.top            }
    ret.top    = {width - m.left - m.right, m.top                    , m.left       , 0                }
    ret.bottom = {width - m.left - m.right, m.bottom                 , m.left       , height - m.bottom}

    -- Honor the border_widgets `:fit()`
    ret.left  [2] = math.max(mins.left  , ret.left  [2])
    ret.right [2] = math.max(mins.right , ret.right [2])
    ret.top   [1] = math.max(mins.top   , ret.top   [1])
    ret.bottom[1] = math.max(mins.bottom, ret.bottom[1])

    -- Center / fill
    ret.fill = {width - m.left - m.right, height - m.top - m.bottom, m.left, m.top}

    -- Undo what we just did to merge the widgets.
    if self._private.border_merging then
        local to_del = {}

        for _, side in ipairs { "top", "bottom" } do
            if self._private.border_merging[side] then
                ret[side][1] = width
                ret[side][3] = 0

                to_del[side.."_left" ] = true
                to_del[side.."_right"] = true
            end
        end

        for _, side in ipairs { "left", "right" } do
            if self._private.border_merging[side] then
                ret[side][2] = height
                ret[side][4] = 0

                to_del["top_"   ..side] = true
                to_del["bottom_"..side] = true
            end
        end

        for corner in pairs(to_del) do
            ret[corner], widgets[corner] = nil, nil
        end
    end

    return ret, widgets
end

local function init_imagebox(self, ib, border_type)
    ib.horizontal_fit_policy = border_type
    ib.vertical_fit_policy   = border_type
    ib.upscale               = true
    ib.valign                = "center"
    ib.halign                = "center"
    ib.resize                = true
    ib.scaling_quality       = self._private.image_scaling_quality or "nearest"
end

local function slice(self, ctx, borders)
    if not self._private.border_image then
        self._private.sliced_surface = nil
        return
    end

    if not self._private.slice then return end

    -- key: dpi, value: widgets
    self._private.slice_cache = self._private.slice_cache or {}

    if self._private.slice_cache[ctx.dpi] then
        return self._private.slice_cache[ctx.dpi]
    end

    self._private.slice_cache[ctx.dpi] = {}

    local ibs, crs = {}, {}

    local md = setup_origin_common(self, ctx)
    self._private.original_md = md

    if not md.surface then return end

    local scale_w, scale_h = 1, 1

    -- This feature allows to provide very large texture while still sharing
    -- the same assets between standard DPI and HiDPI computers.
    if self._private.border_image_dpi and not md.handle then
        local scale = ctx.dpi/self._private.border_image_dpi
        scale_w, scale_h = scale, scale
    end

    local ARGB32 = cairo.Format.ARGB32

    ibs.top_left     = cairo.ImageSurface(ARGB32, borders.left[1], borders.top[2])
    ibs.top          = cairo.ImageSurface(ARGB32, md.width - borders.left[1] - borders.right[1], borders.top[2])
    ibs.top_right    = cairo.ImageSurface(ARGB32, borders.right[1], borders.top[2])
    ibs.right        = cairo.ImageSurface(ARGB32, borders.right[1], md.height - borders.top[2] - borders.bottom[2])
    ibs.bottom_right = cairo.ImageSurface(ARGB32, borders.right[1], borders.bottom[2])
    ibs.bottom_left  = cairo.ImageSurface(ARGB32, borders.left[1], borders.bottom[2])
    ibs.left         = cairo.ImageSurface(ARGB32, borders.left[1], md.height - borders.top[2] - borders.bottom[2])
    ibs.bottom = cairo.ImageSurface(
        ARGB32,
        md.width - borders.left[1] - borders.right[1],
        borders.bottom[2]
    )
    ibs.fill = cairo.ImageSurface(
        ARGB32,
        md.width - borders.left[1] - borders.right[1],
        md.height - borders.top[2]  - borders.bottom[2]
    )

    for _, position in ipairs(components) do
        crs[position] = cairo.Context(ibs[position])
    end

    if scale_w > 1 or scale_h > 1 then
        md.width, md.height = math.ceil(md.width * scale_w), math.ceil(md.height * scale_h)

        local new_origin = cairo.ImageSurface(ARGB32, md.width, md.height)
        local slice_cr = cairo.Context(new_origin)

        slice_cr:set_source_surface(md.surface)
        slice_cr:scale(scale_w, scale_h)
        slice_cr:paint()

        md.surface = new_origin
    end

    -- Top
    crs.top:translate(-borders.left[1], 0)

    -- Top right
    crs.top_right:translate(-md.width + borders.right[1], 0)

    -- Right
    crs.right:translate(-md.width + borders.right[1], - borders.right[4])

    -- Bottom right
    crs.bottom_right:translate(-md.width + borders.right[1], -md.height + borders.bottom[2])

    -- Bottom
    crs.bottom:translate(-borders.left[1], -md.height + borders.bottom[2])

    -- Bottom left
    crs.bottom_left:translate(0, -md.height + borders.bottom[2])

    -- Left
    crs.left:translate(0, - borders.top_left[2])

    -- Center
    crs.fill:translate(-borders.left[1], -borders.top_left[2])

    for _, position in ipairs(components) do
        crs[position]:set_source_surface(md.surface)
        crs[position]:paint()

        -- Scaling was attempted, but there is still some corner cases like the
        -- `fill` :fit()` causing a negative texture size.
        local ib = imagebox(ibs[position])
        init_imagebox(self, ib, self._private[slice_modes[position].."_fit_policy"])
        self._private.slice_cache[ctx.dpi][position] = ib
    end
end

local function setup_background(self, ctx)
    if not self._private.border_image then
        self._private.sliced_surface = nil
        return
    end

    if self._private.background_widget then
        return self._private.background_widget
    end

    self._private.original_md = setup_origin_common(self, ctx)

    if not self._private.original_md.surface then return end

    local ib = imagebox(self._private.original_md.surface)
    self._private.background_widget = ib
    self._private.background_widget.resize = true
    ib.horizontal_fit_policy = self._private.filling_fit_policy
    ib.vertical_fit_policy   = self._private.filling_fit_policy

    return self._private.background_widget
end

local function children_layout(self, borders, width, height, wdg_w, wdg_h)
    assert(wdg_w > 0 and wdg_h > 0)

    -- This need to be after the other because it has to be on top.
    if self._private.widget then
        local p = self._private.paddings or {}
        if not self._private.honor_borders then
            wdg_w, wdg_h = width - (p.right or 0) - (p.left or 0), height - (p.top or 0) - (p.bottom or 0)
            wdg_w, wdg_h = math.max(0, wdg_w), math.max(0, wdg_h)

            return base.place_widget_at(self._private.widget, 0 + (p.left or 0), 0 + (p.top or 0), wdg_w, wdg_h)
        else
            wdg_w, wdg_h = wdg_w - (p.right or 0) - (p.left or 0), wdg_h - (p.top or 0) - (p.bottom or 0)
            wdg_w, wdg_h = math.max(0, wdg_w), math.max(0, wdg_h)

            return base.place_widget_at(
                self._private.widget, borders.left[1] + (p.left or 0), borders.top[2] + (p.top or 0), wdg_w, wdg_h
            )
        end
    end
end

-- Delayed initialization of the border_images.
local function init_border_images(self)
    self._private.pending_border_images = false
    local value = self._private.border_images

    if not value then return end

    if type(value) ~= "table" then
        local ibs = {}

        for _, t in ipairs(fit_types) do
            local ib = imagebox(value)
            rawset(ib, "fit", imagebox_fit)
            init_imagebox(self, ib, self._private[t.."_fit_policy"])
            ibs[t] = ib
        end

        self._private.border_image_widgets = {}

        for component, fit_type in pairs(slice_modes) do
            self._private.border_image_widgets[component] = ibs[fit_type]
        end
    else
        -- Salvage the old objects.
        local old_widgets = self._private.border_image_widgets

        self._private.border_image_widgets = {}

        for component, img in pairs(value) do
            local k, v = next(old_widgets)

            if v then
                old_widgets[k] = nil
            end

            local ib = v or imagebox()
            rawset(ib, "fit", imagebox_fit)
            local mode = slice_modes[component].."_fit_policy"
            init_imagebox(self, ib, self._private[mode])
            ib.image = img
            self._private.border_image_widgets[component] = ib
        end
    end
end

function module:fit(ctx, width, height)
    if self._private.pending_border_images then
        init_border_images(self)
    end

    local w, h, borders = 0, 0

    if self._private.widget then
        w, h = base.fit_widget(self, ctx, self._private.widget, width, height)

        -- Add padding.
        if w > 0 and h > 0 and self._private.paddings then
            w = w + (self._private.paddings.left or 0) + (self._private.paddings.right  or 0)
            h = h + (self._private.paddings.top  or 0) + (self._private.paddings.bottom or 0)
        end

        assert(w >= 0 and h>=0)

        borders = compute_borders(self, ctx, width, height, w, h)
    else
        borders = compute_borders(self, ctx, width, height, width, height)
    end

    -- Make sure the border `fit` are taken into account.
    w = math.max(borders.top [1], w)
    h = math.max(borders.left[2], h)

    -- Add the borders around the central widget.
    w, h = w + borders.left[1] + borders.right[1], h + borders.top[2] + borders.bottom[2]

    assert(w >= 0 and h >= 0)

    return math.min(width, w), math.min(height, h)
end

function module:layout(ctx, width, height)
    local positioned = {}
    local borders, widgets = compute_borders(self, ctx, width, height)

    local wdg_w = width  - borders.left[1] - borders.right[1]
    local wdg_h = height - borders.top[2]  - borders.bottom[2]

    if self._private.ontop == false and self._private.widget then
        table.insert(positioned, children_layout(self, borders, width, height, wdg_w, wdg_h))
    end

    if self._private.slice then
        slice(self, ctx, borders)
        get_all_widgets(self, widgets, ctx)

        -- Use `ipairs` rather than `pairs` on the widget to keep the order stable.
        for _, position in ipairs(components) do
            local geo = borders[position]
            --TODO use unpack
            if geo and widgets[position] and not (position == "fill" and not self._private.fill) then
                local place = base.place_widget_at(widgets[position], geo[3], geo[4], geo[1], geo[2])
                table.insert(positioned, place)
            end
        end
    else
        local bg = setup_background(self, ctx)

        if bg then
            table.insert(
                positioned,
                base.place_widget_at(bg, 0, 0, width, height)
            )
        end
    end

    if self._private.ontop ~= false and self._private.widget then
        table.insert(positioned, children_layout(self, borders, width, height, wdg_w, wdg_h))
    end

    return positioned
end

--- The widget to display inside of the border.
--
-- @property widget
-- @tparam[opt=nil] widget|nil widget

module.set_widget = base.set_widget_common

function module:get_widget()
    return self._private.widget
end

function module:get_children()
    return {self._private.widget}
end

function module:set_children(children)
    self:set_widget(children[1])
end

--- Reset this layout. The widget will be removed and the rotation reset.
-- @method reset
-- @noreturn
-- @interface container
function module:reset()
    self:set_widget(nil)
end

--- A single border image for the border.
--
-- When using this property, the `borders` also **needs** to be specified.
--
-- @property border_image
-- @tparam[opt=nil] string|image|nil border_image
-- @see borders
-- @see border_images

function module:set_border_image(value)
    if self._private.border_image == value then return end

    self._private.slice_cache = nil
    self._private.original_md = nil
    self._private.background_widget = nil
    self._private.border_image = value
    self:emit_signal("widget::redraw_needed")
end

--- Sice the `border_image` to fit the content.
--
-- This applies a CSS-like modifier to the image. It will use the
-- values of the `borders` property to split the original image into
-- multiple smaller images and use the value of `filling_fit_policy`,
-- `sides_fit_policy` and `corners_fit_policy` to resize the content.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_slice1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
--     local w = wibox.widget {
--         {
--             text          = &#34Central widget&#34,
--             valign        = &#34center&#34,
--             align         = &#34center&#34,
--             forced_height = 30,
--             forced_width  = 30,
--             widget        = wibox.widget.textbox
--         },
--         fill         = true,
--         borders      = 10,
--         border_image = image_path,
--         slice        = i,
--         widget       = wibox.container.border
--     }
--
-- @property slice
-- @tparam[opt=true] boolean slice
-- @propemits true false

function module:set_slice(value)
    if self._private.slice == value then return end

    self._private.slice = value
    self:emit_signal("property::slice", value)
    self:emit_signal("widget::layout_changed")
    self:emit_signal("widget::redraw_needed")
end

--FIXME DPI support remains undocumented because it has unfixed corner
-- cases.
-- Set the DPI of the slice surface.
--
-- If this is set, the slicer will enable HiDPI mode. It will
-- resize the surface using the ratio created by the effective
-- DPI and the source DPI.
--
-- If the `border_image` is a `.svg` file, this will override the
-- SVG default DPI.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_dpi1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- for k, dpi in ipairs {72, 96, 220} do
--     for idx, image in ipairs {svg_image_path, png_image_path} do
--         local w = wibox.widget {
--             {
--                 text          = &#34Central widget&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 forced_height = 30,
--                 forced_width  = 30,
--                 widget        = wibox.widget.textbox
--             },
--             fill               = false,
--             border_image_dpi   = dpi,
--             borders            = 10,
--             border_image       = image,
--             widget             = wibox.container.border
--         }
--         l:add_widget_at(w, r+1, idx, 1, 1)
--     end
-- end
--
-- @property border_image_dpi
-- @tparam[opt=nil] number border_image_dpi

function module:set_border_image_dpi(value)
    self._private.original_md = nil
    self._private.border_image_dpi = value
    self:emit_signal("widget::redraw_needed")
end

--- Set a stylesheet for the slice surface.
--
-- This only affect `.svg` based assets. It does nothing for `.png`/`.jpg`
-- and already loaded `gears.surfaces` based assets.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_stylesheet1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- local image_path = '<?xml version=&#341.0&#34?>'..
--     '<svg xmlns:xlink=&#34http://www.w3.org/1999/xlink&#34 width=&#3430&#34 height=&#3430&#34 viewBox=&#340 0 7.937 7.937&#34>'..
--     '  <defs>'..
--     '    <linearGradient id=&#34a&#34>'..
--     '      <stop style=&#34stop-opacity:1&#34 offset=&#340&#34 id=&#34first&#34/>'..
--     '      <stop offset=&#34.5&#34 style=&#34stop-opacity:1&#34 id=&#34second&#34/>'..
--     '      <stop style=&#34stop-opacity:1&#34 offset=&#341&#34 id=&#34third&#34/>'..
--     '    </linearGradient>' ..
--     '    <linearGradient xlink:href=&#34#a&#34 id=&#34b&#34 gradientUnits=&#34userSpaceOnUse&#34 x1=&#3428.726&#34 y1=&#3464.923&#34 x2=&#34182.185&#34 y2=&#34201.75&#34 gradientTransform=&#34matrix(.04726 0 0 .053 83.075 141.528)&#34/>'..
--     '  </defs>'..
--     '  <path d=&#34M84.732 144.627c-.372 0-.642.329-.642.679v6.58c0 .35.27.678.642.678h6.653c.372 0 .642-.328.642-.679v-6.579c0-.35-.27-.68-.642-.68zm.043.685h6.567v6.568h-6.567z&#34 style=&#34fill:url(#b)&#59&#34 transform=&#34translate(-84.09 -144.627)&#34/>'..
--     '</svg>'
--  
-- local style = &#34&#34..
--     &#34#first  {stop-color: magenta&#59}&#34 ..
--     &#34#second {stop-color: cyan&#59}&#34 ..
--     &#34#third  {stop-color: yellow&#59}&#34
--  
-- local w = wibox.widget {
--     {
--         text   = &#34Center widget&#34,
--         valign = &#34center&#34,
--         align  = &#34center&#34,
--         widget = wibox.widget.textbox
--     },
--     borders                 = 50,
--     border_image_stylesheet = style,
--     border_image            = image_path,
--     honor_borders           = false,
--     widget                  = wibox.container.border
-- }
--
-- @property border_image_stylesheet
-- @tparam[opt=""] string border_image_stylesheet CSS data or file path.
-- @see wibox.widget.imagebox.stylesheet

function module:set_border_image_stylesheet(value)
    self._private.original_md = nil
    self._private.border_image_stylesheet = value
    self:emit_signal("widget::redraw_needed")
end

--- How the border_image(s) are scaled.
--
-- Note that `nearest` and `best` are the most sensible choices. Other
-- modes may introduce anti-aliasing artifacts at the junction of the various images.
--
--<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>
--
-- <object data="../images/AUTOGEN_wibox_widget_imagebox_scaling_quality.svg" type="image/svg+xml"></object>
--
-- @property image_scaling_quality
-- @tparam[opt="nearest"] string image_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.

--- Use images for each of the side/corner/filling sections.
--
-- This property is for using different images for each component
-- of the border. If you want to use a single image, use `border_image`.
--
-- Please note that this is mutually exclusive for each corner or side with
-- `border_widgets`. It has priority over `border_image`.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_border_images1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- local w1 = wibox.widget {
--     {
--         text          = &#34Single image&#34,
--         valign        = &#34center&#34,
--         align         = &#34center&#34,
--         widget        = wibox.widget.textbox
--     },
--     borders          = 20,
--     sides_fit_policy = &#34repeat&#34,
--     border_images    = blue_logo,
--     widget           = wibox.container.border
-- }
--  
-- local w2 = wibox.widget {
--     {
--         text          = &#34Multiple images&#34,
--         valign        = &#34center&#34,
--         align         = &#34center&#34,
--         widget        = wibox.widget.textbox
--     },
--     sides_fit_policy = &#34repeat&#34,
--     border_images    = {
--         top_left     = red_logo,
--         top          = green_logo,
--         top_right    = blue_logo,
--         right        = yellow_logo,
--         bottom_right = orange_logo,
--         bottom       = purple_logo,
--         bottom_left  = cyan_logo,
--         left         = black_logo,
--     },
--     widget = wibox.container.border
-- }
--
-- @property border_images
-- @tparam[opt=nil] table|image|nil border_images
-- @tparam[opt=nil] string|image|nil border_images.top_left
-- @tparam[opt=nil] string|image|nil border_images.top
-- @tparam[opt=nil] string|image|nil border_images.top_right
-- @tparam[opt=nil] string|image|nil border_images.right
-- @tparam[opt=nil] string|image|nil border_images.bottom_right
-- @tparam[opt=nil] string|image|nil border_images.bottom
-- @tparam[opt=nil] string|image|nil border_images.bottom_left
-- @tparam[opt=nil] string|image|nil border_images.left
-- @propemits true false
-- @see border_image

function module:set_border_images(value)
    self._private.border_image_widgets = {}
    self._private.original_md = nil
    self._private.border_images = value

    -- Delay the initialization because the fit policy might not be
    -- set yet.
    self._private.pending_border_images = true

    self:emit_signal("property::border_images", value)
    self:emit_signal("widget::redraw_needed")
    self:emit_signal("widget::layout_changed")
end

--- The size of the border on each side.
--
-- Ideally, the value should be smaller than half of the original
-- surface size. For example, if the source is 48x48, then the
-- maximum size should be 23 pixels. If the value exceed this, it
-- will be lowered to the maximum value.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_borders1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- for k, borders in ipairs {0, 10, 30, 64} do
--     for idx, image in ipairs {svg_image_path, png_image_path} do
--         local w = wibox.widget {
--             {
--                 text          = &#34Central widget&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 forced_height = 30,
--                 forced_width  = 30,
--                 widget        = wibox.widget.textbox
--             },
--             fill               = false,
--             borders            = borders,
--             border_image       = image,
--             forced_height      = 100,
--             widget             = wibox.container.border
--         }
--         l:add_widget_at(w, r+1, idx, 1, 1)
--     end
-- end
--
-- @property borders
-- @tparam[opt=0] table|number borders
-- @tparam[opt=0] number borders.top
-- @tparam[opt=0] number borders.left
-- @tparam[opt=0] number borders.right
-- @tparam[opt=0] number borders.bottom
-- @negativeallowed false

function module:set_borders(value)

    if type(value) == "number" then
        value = {
            top    = value,
            left   = value,
            right  = value,
            bottom = value,
        }
    end

    self._private.borders = value

    self:emit_signal("widget::layout_changed")
end

--- How the sliced image is resized for the border sides.
--
-- In the following example, the gradient based border works
-- will with `fit` and `pad`. The repeated dot works well with
-- `repeat` and `reflect`. The soft shadow one works regardless
-- of the mode because the borders are identical.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_sides_fit_policy1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- local images = {
--     {
--         path     = image_path1,
--         borders = 10,
--     },
--     {
--         path    = image_path2,
--         borders = 30,
--     },
--     {
--         path    = image_path3,
--         borders = {
--             top    = 20,
--             left   = 20,
--             bottom = 20,
--             right  = 20,
--         },
--     },
-- }
--  
-- for k, mode in ipairs {&#34fit&#34, &#34repeat&#34, &#34reflect&#34, &#34pad&#34} do
--     for idx, image in ipairs(images) do
--         local w = wibox.widget {
--             {
--                 text          = &#34Central widget&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 widget        = wibox.widget.textbox
--             },
--             fill               = false,
--             borders            = image.borders,
--             border_image       = image.path,
--             sides_fit_policy   = mode,
--             widget             = wibox.container.border
--         }
--     end
-- end
--
-- @property sides_fit_policy
-- @tparam[opt="fit"] string sides_fit_policy
-- @propertyvalue "fit" (default)
-- @propertyvalue "repeat"
-- @propertyvalue "reflect"
-- @propertyvalue "pad"
-- @propemits true false
-- @see wibox.widget.imagebox.vertical_fit_policy
-- @see wibox.widget.imagebox.horizontal_fit_policy

--- How the sliced image is resized for the border filling.
--
-- Also note that if `slice` is set to `false`, this will be used for
-- the entire background.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_filling_fit_policy1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- for k, mode in ipairs {&#34fit&#34, &#34repeat&#34, &#34reflect&#34, &#34pad&#34} do
--     for idx, image in ipairs { image_path1, image_path2 } do
--         local w = wibox.widget {
--             {
--                 text          = &#34Central widget&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 widget        = wibox.widget.textbox
--             },
--             fill               = true,
--             borders            = idx == 1 and 10 or 30,
--             border_image       = image,
--             filling_fit_policy = mode,
--             widget             = wibox.container.border
--         }
--     end
-- end
--
-- @property filling_fit_policy
-- @tparam[opt="fit"] string filling_fit_policy
-- @propertyvalue "fit" (default)
-- @propertyvalue "repeat"
-- @propertyvalue "reflect"
-- @propertyvalue "pad"
-- @propemits true false
-- @see fill
-- @see wibox.widget.imagebox.vertical_fit_policy
-- @see wibox.widget.imagebox.horizontal_fit_policy

--- How the sliced image is resized for the border corners.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_corners_fit_policy1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- for k, mode in ipairs {&#34fit&#34, &#34repeat&#34, &#34reflect&#34, &#34pad&#34} do
--     for idx, image in ipairs { image_path1, image_path2 } do
--         local w = wibox.widget {
--             {
--                 text          = &#34Central widget&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 widget        = wibox.widget.textbox
--             },
--             fill               = false,
--             borders            = {
--                 left   = 10,
--                 right  = 50,
--                 top    = 10,
--                 bottom = 50,
--             },
--             border_image       = image,
--             corners_fit_policy = mode,
--             widget             = wibox.container.border
--         }
--     end
-- end
--
-- @property corners_fit_policy
-- @tparam[opt="fit"] string corners_fit_policy
-- @propertyvalue "fit" (default)
-- @propertyvalue "repeat"
-- @propertyvalue "reflect"
-- @propertyvalue "pad"
-- @propemits true false
-- @see wibox.widget.imagebox.vertical_fit_policy
-- @see wibox.widget.imagebox.horizontal_fit_policy

for _, mode in ipairs {"corners", "sides", "filling" } do
    module["set_"..mode.."_fit_policy"] = function(self, value)
        for _, widgets in pairs(self._private.slice_cache or {}) do
            for position, widget in pairs(widgets) do
                if slice_modes[position] == mode then
                    widget.horizontal_fit_policy = value
                    widget.vertical_fit_policy   = value
                end
            end
        end

        if mode == "filling" and self._private.background_widget then
            self._private.background_widget.horizontal_fit_policy = value
            self._private.background_widget.horizontal_fit_policy = value
        end

        self._private.pending_border_images = true
        self._private[mode.."_fit_policy"] = value
        self:emit_signal("property::"..mode.."_fit_policy", value)
        self:emit_signal("widget::redraw_needed")
    end
end

--- Stretch the child widget over the entire area.
--
-- By default, the widget honors the borders.
--
--
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_honor_borders1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
--     local w = wibox.widget {
--         {
--             {
--                 markup        = &#34<b>Central widget</b>&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 forced_height = 30,
--                 forced_width  = 30,
--                 widget        = wibox.widget.textbox
--             },
--             bg     = stripe_pattern,
--             widget = wibox.container.background
--         },
--         borders       = 10,
--         border_image  = image_path,
--         honor_borders = honor,
--         widget        = wibox.container.border
--     }
--
-- @property honor_borders
-- @tparam[opt=true] boolean honor_borders
-- @propemits true false
-- @see ontop

function module:set_honor_borders(value)
    if self._private.honor_borders == value then return end

    self._private.honor_borders = value
    self:emit_signal("property::honor_borders", value)
    self:emit_signal("widget::layout_changed")
end

--- Draw the child widget below or on top of the border.
--
-- `wibox.container.background` supports border clips because it knows the
-- shape of the border. `wibox.container.border` doesn't have this luxury
-- because the border is defined in an image and the "inner limit" could
-- be any pixel.
--
-- This proporty helps mitigate that by allowing the border to be drawn on
-- top of the child widget when `honor_borders` is set to false. When
-- `honor_borders` is `true`, it does nothing. In the example below, some
-- `paddings` are also necessary to look decent.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_ontop1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
--     local w = wibox.widget {
--         {
--             {
--                 markup        = &#34<b>Central widget</b>&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 forced_height = 30,
--                 forced_width  = 30,
--                 widget        = wibox.widget.textbox
--             },
--             bg     = stripe_pattern,
--             widget = wibox.container.background
--         },
--         paddings      = {
--             left   = 5,
--             top    = 3,
--             right  = 10,
--             bottom = 10,
--         },
--         borders       = 20,
--         border_image  = image_path,
--         ontop         = ontop,
--         honor_borders = false,
--         widget        = wibox.container.border
--     }
--
-- @property ontop
-- @tparam[opt=true] boolean ontop
-- @propemits true false
-- @see honor_borders
-- @see paddings

function module:set_ontop(value)
    self._private.ontop = value

    self:emit_signal("property::ontop", value)
    self:emit_signal("widget::layout_changed")
end

--- Use the center portion of the `border_image` as a background.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_fill1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
--     local w = wibox.widget {
--         {
--             text          = &#34Central widget&#34,
--             valign        = &#34center&#34,
--             align         = &#34center&#34,
--             forced_height = 30,
--             forced_width  = 30,
--             widget        = wibox.widget.textbox
--         },
--         borders      = 10,
--         border_image = image_path,
--         fill         = fill,
--         widget       = wibox.container.border
--     }
--
-- @property fill
-- @tparam[opt=false] boolean fill
-- @propemits true false
-- @see filling_fit_policy

function module:set_fill(value)
    if self._private.fill == value then return end

    self._private.fill = value
    self:emit_signal("property::fill", value)
    self:emit_signal("widget::redraw_needed")
end

--- Add some space between the border and the inner widget.
--
-- This is pretty much exactly what `wibox.container.margin` would be
-- used for, but since this is a very common use case, this container
-- also has it built-in.
--
-- A common use case for this is asymetric borders such as soft shadows.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_paddings1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- local paddings = {
--     0,
--     5,
--     10,
--     {
--         left   = 5,
--         top    = 5,
--         bottom = 10,
--         right  = 10,
--     }
-- }
--  
-- for _, padding in ipairs(paddings) do
--     local w = wibox.widget {
--         {
--             {
--                 markup        = &#34<b>Central widget</b>&#34,
--                 valign        = &#34center&#34,
--                 align         = &#34center&#34,
--                 forced_height = 30,
--                 forced_width  = 30,
--                 widget        = wibox.widget.textbox
--             },
--             bg     = stripe_pattern,
--             widget = wibox.container.background
--         },
--         borders      = 10,
--         paddings     = padding,
--         border_image = image_path,
--         widget       = wibox.container.border
--     }
-- end
--
-- @property paddings
-- @tparam[opt=0] number|table paddings
-- @propemits true false
-- @negativeallowed false
-- @see wibox.container.margin

function module:set_paddings(value)
    if self._private.paddings == value then return end

    if type(value) == "number" then
        value = {
            left   = value,
            right  = value,
            top    = value,
            bottom = value
        }
    end

    self._private.paddings = value
    self:emit_signal("property::paddings", value)
    self:emit_signal("widget::layout_changed")
end

--- Use individual widgets as a border.
--
-- Please note that this is mutually exclusive for each
-- corner or side with `border_images`. It
-- has priority over `border_image`.
--
-- Please note that if `borders` is not defined, the side widgets
-- (`left`, `right`, `top` and `bottom`) will be used to compute
-- the side of the borders. If you only have corner widgets, then
-- `borders` need to be defined.
--
--
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_border_widgets1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- local w = wibox.widget {
--     {
--         text          = &#34Central widget&#34,
--         valign        = &#34center&#34,
--         align         = &#34center&#34,
--         widget        = wibox.widget.textbox
--     },
--     border_widgets = {
--         top_left     = generic_widget(&#34top_left&#34),
--         top          = generic_widget(&#34top&#34),
--         top_right    = generic_widget(&#34top_right&#34),
--         right        = generic_widget(&#34right&#34),
--         bottom_right = generic_widget(&#34bottom_right&#34),
--         bottom       = generic_widget(&#34bottom&#34),
--         bottom_left  = generic_widget(&#34bottom_left&#34),
--         left         = generic_widget(&#34left&#34),
--     },
--     widget = wibox.container.border
-- }
--
-- @property border_widgets
-- @tparam[opt=nil] table|nil border_widgets
-- @tparam[opt=nil] wibox.widget border_widgets.top_left
-- @tparam[opt=nil] wibox.widget border_widgets.top
-- @tparam[opt=nil] wibox.widget border_widgets.top_right
-- @tparam[opt=nil] wibox.widget border_widgets.right
-- @tparam[opt=nil] wibox.widget border_widgets.bottom_right
-- @tparam[opt=nil] wibox.widget border_widgets.bottom
-- @tparam[opt=nil] wibox.widget border_widgets.bottom_left
-- @tparam[opt=nil] wibox.widget border_widgets.left
-- @propemits true false

function module:set_border_widgets(value)
    local widgets = {}

    if type(value) ~= "table" then
        for _, component in ipairs(components) do
            widgets[component] = value
                and base.make_widget_from_value(value)
                or nil
        end
    else
        for component, widget in pairs(value) do
            widgets[component] = widget
                and base.make_widget_from_value(widget)
                or nil
        end
    end

    self._private.border_widgets = widgets

    self:emit_signal("property::border_widgets", widgets)
    self:emit_signal("widget::redraw_needed")
    self:emit_signal("widget::layout_changed")
end

--- Merge the corners widgets into the side widgets.
--
-- Rather than have corner widgets/images, let the side
-- widget span the entire area. This feature is coupled with
-- `border_widgets` and does nothing when only an image is present.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_border_merging1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- for _, side in ipairs { &#34top&#34, &#34bottom&#34, &#34left&#34, &#34right&#34 } do
--     l:add(wibox.widget {
--         {
--             text   = side .. &#34 = true&#34,
--             valign = &#34center&#34,
--             align  = &#34center&#34,
--             widget = wibox.widget.textbox
--         },
--         border_merging = {
--             -- This is the equaivalent &#34side = true,&#34. &#34side&#34 is the loop
--             -- variable.
--             [side] = true
--         },
--         border_widgets = {
--             top_left     = generic_widget(&#34top_left&#34),
--             top          = generic_widget(&#34top&#34),
--             top_right    = generic_widget(&#34top_right&#34),
--             right        = generic_widget(&#34right&#34),
--             bottom_right = generic_widget(&#34bottom_right&#34),
--             bottom       = generic_widget(&#34bottom&#34),
--             bottom_left  = generic_widget(&#34bottom_left&#34),
--             left         = generic_widget(&#34left&#34),
--         },
--         widget = wibox.container.border
--     })
-- end
--
-- @property border_merging
-- @tparam table|nil border_merging
-- @tparam[opt=false] boolean border_merging.top Extend the `top` side
--  widget by replacing the `top_left` and `top_right` widgets.
-- @tparam[opt=false] boolean border_merging.bottom Extend the `bottom` side
--  widget by replacing the `bottom_left` and `bottom_right` widgets.
-- @tparam[opt=false] boolean border_merging.left Extend the `left` side
--  widget by replacing the `top_left` and `bottom_left` widgets.
-- @tparam[opt=false] boolean border_merging.right Extend the `right` side
--  widget by replacing the `top_right` and `bottom_right` widgets.
-- @propemits true false

function module:set_border_merging(value)
    self._private.border_merging = value

    self:emit_signal("property::border_widgets", value)
    self:emit_signal("widget::redraw_needed")
    self:emit_signal("widget::layout_changed")
end

--- When `border_widgets` is used, allow the border to grow due to corner widgets.
--
-- 
--
--<object class=&#34img-object&#34 data=&#34../images/AUTOGEN_wibox_container_border_expand_corners1.svg&#34 alt=&#34Usage example&#34 type=&#34image/svg+xml&#34></object>
--
-- @usage
-- for _, expand in ipairs { false, true } do
--     local w = wibox.widget {
--         {
--             text          = &#34expand_corners = &#34 .. (expand and &#34true&#34 or &#34false&#34),
--             valign        = &#34center&#34,
--             align         = &#34center&#34,
--             widget        = wibox.widget.textbox
--         },
--         border_widgets = {
--             top_left     = generic_widget(&#34top_left&#34),
--             top          = generic_widget(&#34top&#34),
--             top_right    = generic_widget(&#34top_right&#34),
--             right        = generic_widget(&#34right&#34),
--             bottom_right = generic_widget(&#34bottom_right&#34),
--             bottom       = generic_widget(&#34bottom&#34),
--             bottom_left  = generic_widget(&#34bottom_left&#34),
--             left         = generic_widget(&#34left&#34),
--         },
--         expand_corners = expand,
--         widget         = wibox.container.border
--     }
-- end
--
-- @property expand_corners
-- @tparam[opt=false] boolean expand_corners

function module:set_expand_corners(value)
    self._private.expand_corners = value

    self:emit_signal("widget::redraw_needed")
    self:emit_signal("widget::layout_changed")
end

local function new(_, args)
    args = args or {}

    local ret = base.make_widget(nil, nil, {enable_properties = true})
    ret._private.corners_fit_policy    = args.corners_fit_policy or "fit"
    ret._private.sides_fit_policy      = args.slides_fit_policy  or "fit"
    ret._private.filling_fit_policy    = args.filling_fit_policy or "fit"
    ret._private.honor_borders         = args.honor_borders ~= false
    ret._private.fill                  = args.fill or false
    ret._private.slice                 = args.slice == nil and true or args.slice

    gtable.crush(ret, module, true)
    gtable.crush(ret, args, false)

    return ret
end

return setmetatable(module, {__call=new})
