add(desktop_decoration) Customize the desktop bar
This commit is contained in:
parent
8c158dd6c3
commit
baeb76de22
|
@ -12,6 +12,8 @@ rc_configuration.menu = require 'rc.configuration.menu'
|
|||
|
||||
rc_configuration.rules = require 'rc.configuration.rules'
|
||||
|
||||
rc_configuration.prompt_commands = require 'rc.configuration.prompt_commands'
|
||||
|
||||
rc_configuration.tag_layouts = require 'rc.configuration.tag_layouts'
|
||||
|
||||
return rc_configuration
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
local atag = require 'awful.tag'
|
||||
local layout_suit = require 'awful.layout.suit'
|
||||
|
||||
local commands = {}
|
||||
|
||||
commands['o'] = {
|
||||
callback = function (parameters)
|
||||
local tag_name = parameters[1] or 'New-Tag'
|
||||
|
||||
atag.add(tag_name, {
|
||||
layout = layout_suit.tile
|
||||
}):view_only()
|
||||
end
|
||||
}
|
||||
|
||||
commands['O'] = {
|
||||
callback = function (parameters)
|
||||
local aspawn = require 'awful.spawn'
|
||||
|
||||
local application = parameters[1]
|
||||
local tag_name = parameters[2] or application
|
||||
|
||||
local t = atag.add(tag_name, {
|
||||
layout = layout_suit.tile,
|
||||
volatile = true,
|
||||
})
|
||||
t:view_only()
|
||||
aspawn(application, { tag = t })
|
||||
end
|
||||
}
|
||||
|
||||
commands['q'] = {
|
||||
callback = function ()
|
||||
local ascreen = require 'awful.screen'
|
||||
|
||||
local tags = ascreen.focused().selected_tags
|
||||
|
||||
for _,tag in ipairs(tags) do
|
||||
tag.volatile = true
|
||||
|
||||
for _,client in ipairs(tag:clients()) do
|
||||
client:kill()
|
||||
end
|
||||
|
||||
tag:delete()
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
return commands
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path style="fill:#ECEFF1;" d="M16,20H20V16H16M16,14H20V10H16M10,8H14V4H10M16,8H20V4H16M10,14H14V10H10M4,14H8V10H4M4,20H8V16H4M10,20H14V16H10M4,8H8V4H4V8Z" /></svg>
|
After Width: | Height: | Size: 441 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24" fill="#ECEFF1" opacity="1"><path d="M16,20H8V6H16M16.67,4H15V2H9V4H7.33A1.33,1.33 0 0,0 6,5.33V20.67C6,21.4 6.6,22 7.33,22H16.67A1.33,1.33 0 0,0 18,20.67V5.33C18,4.6 17.4,4 16.67,4Z"></path></svg>
|
After Width: | Height: | Size: 337 B |
|
@ -7,13 +7,52 @@ local icons_dir = assets_dir .. 'icons/'
|
|||
|
||||
local theme = {}
|
||||
|
||||
theme.border_color_normal = '#0000ff'
|
||||
--- Basic
|
||||
theme.font = 'Noto Mono 9'
|
||||
|
||||
--- Tags
|
||||
theme.hometag_master_width_factor = 0.65
|
||||
|
||||
--- Icons
|
||||
theme.icon_hometag = icons_dir .. 'home-circle.svg'
|
||||
theme.icon_apps = icons_dir .. 'apps.svg'
|
||||
theme.icon_battery_outline = icons_dir .. 'battery-outline.svg'
|
||||
|
||||
--- Wibar
|
||||
-- My wibar is a transparent dock at the bottom of the screen.
|
||||
-- Height and width ahve to be set by the constructor to use screen DPI and
|
||||
-- screen percentage values.
|
||||
theme.wibar_stretch = false
|
||||
theme.wibar_border_width = 0
|
||||
theme.wibar_border_color = nil
|
||||
theme.wibar_ontop = false
|
||||
theme.wibar_opacity = 1
|
||||
theme.wibar_type = 'dock'
|
||||
theme.wibar_bg = '#0000'
|
||||
|
||||
--- Spacing and margins
|
||||
-- Naming convention:
|
||||
-- <rc.ui.desktop_decoration element>_<widget element>_<property>
|
||||
theme.spacing = 8
|
||||
theme.margin = 4
|
||||
theme.padding = 4
|
||||
|
||||
theme.bar_height = 32 + (theme.margin * 2) -- This is the outer size of the bar
|
||||
theme.bar_width = '80%' -- Make the bar size relative to the screen
|
||||
theme.bar_margin_x = theme.margin * 2
|
||||
theme.bar_margin_y = theme.margin
|
||||
theme.bar_padding_x = theme.padding * 2
|
||||
theme.bar_padding_y = 0
|
||||
|
||||
theme.bar_box_margin_x = 0
|
||||
theme.bar_box_margin_y = theme.margin
|
||||
theme.bar_box_padding_x = theme.padding * 2
|
||||
theme.bar_box_padding_y = theme.padding
|
||||
theme.bar_box_spacing = theme.spacing
|
||||
|
||||
--- Widgets specific
|
||||
theme.bg_systray = '#455A64'
|
||||
theme.systray_icon_spacing = 3
|
||||
|
||||
--- Wallpaper
|
||||
theme.wallpaper = function (screen)
|
||||
|
|
|
@ -1,27 +1,92 @@
|
|||
local awibar = require 'awful.wibar'
|
||||
local awful = require 'awful'
|
||||
local battery_widget = require 'battery-widget'
|
||||
local beautiful = require 'beautiful'
|
||||
local wibox = require 'wibox'
|
||||
|
||||
local container_background = require 'wibox.container.background'
|
||||
local container_margin = require 'wibox.container.margin'
|
||||
local container_place = require 'wibox.container.place'
|
||||
|
||||
local gshape = require 'gears.shape'
|
||||
|
||||
local layout_align = require 'wibox.layout.align'
|
||||
local layout_fixed = require 'wibox.layout.fixed'
|
||||
|
||||
local systray = require 'wibox.widget.systray'
|
||||
local textclock = require 'wibox.widget.textclock'
|
||||
local widget = require 'wibox.widget'
|
||||
|
||||
local mybattery = require 'rc.ui.desktop_decoration.widgets.battery'
|
||||
local mycommands = require 'rc.configuration.prompt_commands'
|
||||
local mymainmenu = require 'rc.ui.menu.mymainmenu'
|
||||
local myprompt = require 'rc.ui.desktop_decoration.widgets.prompt'
|
||||
local mytaglist = require 'MyTagListWidget'
|
||||
|
||||
local capi = {
|
||||
client = _G.client,
|
||||
screen = _G.screen
|
||||
}
|
||||
|
||||
local modkey = 'Mod4'
|
||||
local abs = math.abs
|
||||
local dpi = beautiful.xresources.apply_dpi
|
||||
|
||||
local bar = { _private = { instances = {} }, mt = {} }
|
||||
|
||||
local function get_screen_id(screen)
|
||||
local s = capi.screen[screen] or screen
|
||||
local function get_screen_id (screen)
|
||||
local s = capi.screen[screen or 1]
|
||||
|
||||
return s.index
|
||||
end
|
||||
|
||||
-- Get the bar instance for a given screen
|
||||
function bar:instance(screen)
|
||||
--- Build a widget box in the bar.
|
||||
-- A widget box is a combinaision of "shaped" `wibox.container/background` and
|
||||
-- `wibox.container.margin` to create this nice round-colored-buttons I like.
|
||||
-- @tparam args table
|
||||
-- @tparam args.screen The screen where the widget will be drawn.
|
||||
-- @tparam args.widget The widget to draw.
|
||||
-- @tparam[opt] args.bg The box's background.
|
||||
-- @tpram[opt] args.fg The box's foreground (used mainly for textbox text color).
|
||||
-- @treturn wibox.container.background The builded widget.
|
||||
local function build_widget (args)
|
||||
local screen_id = get_screen_id(args.screen)
|
||||
|
||||
-- Callback for the shape function.
|
||||
-- If the widget is almost a square: draw a circle. Otherwise, draw a
|
||||
-- rounded_bar.
|
||||
local shape_callback = function (cr, width, height)
|
||||
local shape = gshape.circle
|
||||
|
||||
-- 10 is an arbitrary value I found after some tests :shrug:
|
||||
if abs(width - height) > 10 then
|
||||
shape = gshape.rounded_bar
|
||||
end
|
||||
|
||||
return shape(cr, width, height)
|
||||
end
|
||||
|
||||
local box_widget = widget {
|
||||
widget = container_background,
|
||||
shape = shape_callback,
|
||||
bg = args.bg,
|
||||
fg = args.fg,
|
||||
{
|
||||
widget = container_margin,
|
||||
draw_empty = false,
|
||||
top = dpi(beautiful.bar_box_padding_y, screen_id),
|
||||
bottom = dpi(beautiful.bar_box_padding_y, screen_id),
|
||||
right = dpi(beautiful.bar_box_padding_x, screen_id),
|
||||
left = dpi(beautiful.bar_box_padding_x, screen_id),
|
||||
args.widget
|
||||
}
|
||||
}
|
||||
|
||||
return box_widget
|
||||
end
|
||||
|
||||
local bar = { _private = { instances = {} }, mt = {} }
|
||||
|
||||
--- Get the bar instance for a given screen.
|
||||
-- If no instance was found, we build a new one.
|
||||
-- @tparam screen screen|integer The bar's screen.
|
||||
-- @treturn wibox.wibar
|
||||
function bar:instance (screen)
|
||||
local screen_id = get_screen_id(screen)
|
||||
local instance = self._private.instances[screen_id]
|
||||
|
||||
|
@ -36,94 +101,124 @@ end
|
|||
function bar.new (screen)
|
||||
local my_bar = {}
|
||||
|
||||
my_bar.launcher = build_widget {
|
||||
screen = screen,
|
||||
bg = '#2196F3',
|
||||
widget = awful.widget.launcher {
|
||||
image = beautiful.icon_apps,
|
||||
menu = mymainmenu()
|
||||
}
|
||||
}
|
||||
|
||||
my_bar.textclock = build_widget {
|
||||
bg = '#FF5722',
|
||||
fg = '#ECEFF1',
|
||||
widget = textclock('%l:%M %p')
|
||||
}
|
||||
|
||||
my_bar.promptbox = myprompt {
|
||||
commands = mycommands
|
||||
}
|
||||
|
||||
-- This widget needs to be reworded and integrated inside the project.
|
||||
my_bar.taglist = mytaglist.new {
|
||||
screen = screen
|
||||
}
|
||||
|
||||
my_bar.battery = build_widget {
|
||||
screen = screen,
|
||||
bg = '#673AB7',
|
||||
widget = mybattery {
|
||||
screen = screen,
|
||||
device_path = battery_widget.get_BAT0_device_path(),
|
||||
color = '#ECEFF1'
|
||||
}
|
||||
}
|
||||
|
||||
my_bar.systray = build_widget {
|
||||
screen = screen,
|
||||
bg = '#455A64',
|
||||
widget = systray()
|
||||
}
|
||||
|
||||
my_bar.wibar = awibar {
|
||||
screen = screen,
|
||||
position = 'top'
|
||||
}
|
||||
|
||||
my_bar.launcher = awful.widget.launcher {
|
||||
image = beautiful.awesome_icon,
|
||||
menu = mymainmenu()
|
||||
}
|
||||
|
||||
my_bar.keyboardlayout = awful.widget.keyboardlayout()
|
||||
|
||||
my_bar.textclock = wibox.widget.textclock()
|
||||
|
||||
my_bar.promptbox = awful.widget.prompt()
|
||||
|
||||
my_bar.layoutbox = awful.widget.layoutbox {
|
||||
screen = screen,
|
||||
buttons = {
|
||||
awful.button({ }, 1, function () awful.layout.inc( 1) end),
|
||||
awful.button({ }, 3, function () awful.layout.inc(-1) end),
|
||||
awful.button({ }, 4, function () awful.layout.inc(-1) end),
|
||||
awful.button({ }, 5, function () awful.layout.inc( 1) end),
|
||||
}
|
||||
}
|
||||
|
||||
my_bar.taglist = awful.widget.taglist {
|
||||
screen = screen,
|
||||
filter = awful.widget.taglist.filter.all,
|
||||
buttons = {
|
||||
awful.button({}, 1, function(tag)
|
||||
tag:view_only()
|
||||
end),
|
||||
awful.button({ modkey }, 1, function(tag)
|
||||
if capi.client.focus then
|
||||
capi.client.focus:move_to_tag(tag)
|
||||
end
|
||||
end),
|
||||
awful.button({}, 3, awful.tag.viewtoggle),
|
||||
awful.button({ modkey }, 3, function(tag)
|
||||
if capi.client.focus then
|
||||
capi.client.focus:toggle_tag(tag)
|
||||
end
|
||||
end),
|
||||
awful.button({ }, 4, function(tag)
|
||||
awful.tag.viewprev(tag.screen)
|
||||
end),
|
||||
awful.button({ }, 5, function(tag)
|
||||
awful.tag.viewnext(tag.screen)
|
||||
end)
|
||||
}
|
||||
}
|
||||
|
||||
my_bar.tasklist = awful.widget.tasklist {
|
||||
screen = screen,
|
||||
filter = awful.widget.tasklist.filter.currenttags,
|
||||
buttons = {
|
||||
awful.button({ }, 1, function (c)
|
||||
c:activate { context = "tasklist", action = "toggle_minimization" }
|
||||
end),
|
||||
awful.button({ }, 3, function() awful.menu.client_list { theme = { width = 250 } } end),
|
||||
awful.button({ }, 4, function() awful.client.focus.byidx(-1) end),
|
||||
awful.button({ }, 5, function() awful.client.focus.byidx( 1) end),
|
||||
}
|
||||
position = 'bottom',
|
||||
height = dpi(beautiful.bar_height, screen),
|
||||
width = beautiful.bar_width -- Width is a percentage of the screen size
|
||||
}
|
||||
|
||||
my_bar.wibar:setup {
|
||||
layout = wibox.layout.align.horizontal,
|
||||
{ -- Left widgets
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
my_bar.launcher,
|
||||
my_bar.taglist,
|
||||
my_bar.promptbox,
|
||||
},
|
||||
screen.mytasklist, -- Middle widget
|
||||
{ -- Right widgets
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
my_bar.keyboardlayout,
|
||||
wibox.widget.systray(),
|
||||
my_bar.textclock,
|
||||
my_bar.layoutbox,
|
||||
-- Bar margins
|
||||
widget = container_margin,
|
||||
top = dpi(beautiful.bar_margin_y, screen),
|
||||
bottom = dpi(beautiful.bar_margin_y, screen),
|
||||
right = dpi(beautiful.bar_margin_x, screen),
|
||||
left = dpi(beautiful.bar_margin_x, screen),
|
||||
{
|
||||
-- Physical bar
|
||||
widget = container_background,
|
||||
bg = '#263238D9',
|
||||
shape = function (cr, width, height)
|
||||
gshape.rounded_rect(cr, width, height, 10)
|
||||
end,
|
||||
{
|
||||
-- Bar paddings
|
||||
widget = container_margin,
|
||||
top = dpi(beautiful.bar_padding_y, screen),
|
||||
bottom = dpi(beautiful.bar_padding_y, screen),
|
||||
right = dpi(beautiful.bar_padding_x, screen),
|
||||
left = dpi(beautiful.bar_padding_x, screen),
|
||||
{
|
||||
-- Bar content
|
||||
layout = layout_align.horizontal,
|
||||
{
|
||||
-- Left side of the bar, box margins
|
||||
widget = container_margin,
|
||||
top = dpi(beautiful.bar_box_margin_y, screen),
|
||||
bottom = dpi(beautiful.bar_box_margin_y, screen),
|
||||
right = dpi(beautiful.bar_box_margin_x, screen),
|
||||
left = dpi(beautiful.bar_box_margin_x, screen),
|
||||
{
|
||||
-- Left widget boxes
|
||||
layout = layout_fixed.horizontal,
|
||||
spacing = dpi(beautiful.bar_box_spacing, screen),
|
||||
my_bar.launcher,
|
||||
my_bar.promptbox
|
||||
}
|
||||
},
|
||||
|
||||
-- Middle widget is the custom taglist
|
||||
-- it doesn't need box/margins/...
|
||||
{
|
||||
widget = container_place,
|
||||
my_bar.taglist
|
||||
},
|
||||
{
|
||||
-- Right side of the bar, box margins
|
||||
widget = container_margin,
|
||||
top = dpi(beautiful.bar_box_margin_y, screen),
|
||||
bottom = dpi(beautiful.bar_box_margin_y, screen),
|
||||
right = dpi(beautiful.bar_box_margin_x, screen),
|
||||
left = dpi(beautiful.bar_box_margin_x, screen),
|
||||
{
|
||||
-- Right widget boxes
|
||||
layout = layout_fixed.horizontal,
|
||||
spacing = dpi(beautiful.bar_box_spacing, screen),
|
||||
my_bar.systray,
|
||||
my_bar.battery,
|
||||
my_bar.textclock
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return my_bar
|
||||
end
|
||||
|
||||
function bar.mt:__call(...)
|
||||
function bar.mt:__call (...)
|
||||
return self:instance(...)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- An implementation of my battery_widget widget with a dynamicly generated
|
||||
-- icon based on the battery percentage level.
|
||||
--
|
||||
-- Find more about my battery_widget widget at
|
||||
-- https://github.com/Aire-One/awesome-battery_widget
|
||||
--
|
||||
-- @author Aire-One
|
||||
-- @copyright 2020 Aire-One
|
||||
---------------------------------------------------------------------------
|
||||
local lgi = require 'lgi'
|
||||
local cairo = lgi.cairo
|
||||
local rsvg = lgi.Rsvg
|
||||
|
||||
local atooltip = require 'awful.tooltip'
|
||||
local beautiful = require 'beautiful'
|
||||
local gcolor = require 'gears.color'
|
||||
local imagebox = require 'wibox.widget.imagebox'
|
||||
|
||||
local battery_widget = require 'battery-widget'
|
||||
|
||||
local my_battery = {}
|
||||
local mt = {}
|
||||
|
||||
--- Generate a drawing for the battery icon.
|
||||
-- @tparam number percentage The percentage of battery remaining.
|
||||
-- @tparam gears.color|string color The color of the drawing.
|
||||
-- @treturn cairo.ImageSurface The generated surface for the drawing.
|
||||
function my_battery.draw_battery (percentage, color)
|
||||
local svg_handle = rsvg.Handle.new_from_file(beautiful.icon_battery_outline)
|
||||
if not svg_handle then return end
|
||||
local surface = cairo.ImageSurface.create(cairo.Format.ARGB32, 24, 24)
|
||||
local cr = cairo.Context(surface)
|
||||
svg_handle:render_cairo(cr)
|
||||
|
||||
local max_height = 14
|
||||
local x = 8
|
||||
local width = 8
|
||||
local height = percentage / 100 * max_height
|
||||
local y = 6 + max_height - height
|
||||
|
||||
cr:set_source(gcolor(color))
|
||||
cr:rectangle(x, y, width, height)
|
||||
cr:fill()
|
||||
|
||||
return surface
|
||||
end
|
||||
|
||||
--- Update handler for the battery widget. This is the function called
|
||||
-- by the signal system on battery update.
|
||||
-- @tparam my_battery widget The battery widget to update.
|
||||
-- @tparam UPowerGLib.Device device The UPower device to monitor.
|
||||
function my_battery.update (widget, device)
|
||||
widget.image = my_battery.draw_battery(device.percentage, widget.color)
|
||||
end
|
||||
|
||||
--- Give the widget template for the battery widget.
|
||||
-- (It's a basic `wibox.widget.imagebox` for my battery implementation)
|
||||
function my_battery.widget_template ()
|
||||
local widget = imagebox()
|
||||
widget.resize = true
|
||||
|
||||
return widget
|
||||
end
|
||||
|
||||
--- Constructor of my battery widget!
|
||||
-- @tparam table args Table of parameters.
|
||||
-- @tparam screen|number args.screen The screen of the wodget.
|
||||
-- @tparam string args.device_path The path of the UPower device to monitor.
|
||||
-- @tparam gears.color|string args.color Color to use to draw the battery.
|
||||
-- @treturn my_battery The instantiated widget.
|
||||
function my_battery.new (args)
|
||||
local widget = battery_widget {
|
||||
screen = args.screen,
|
||||
device_path = args.device_path,
|
||||
widget_template = my_battery.widget_template(),
|
||||
instant_update = true
|
||||
}
|
||||
|
||||
widget.color = args.color
|
||||
|
||||
widget:connect_signal('upower::update', function (w, device)
|
||||
my_battery.update(w, device)
|
||||
end)
|
||||
|
||||
widget.tooltip = atooltip {
|
||||
objects = { widget },
|
||||
timer_function = function ()
|
||||
return string.format('%3d', widget.device.percentage) .. '%'
|
||||
end
|
||||
}
|
||||
|
||||
return widget
|
||||
end
|
||||
|
||||
function mt.__call (self, ...)
|
||||
return my_battery.new(...)
|
||||
end
|
||||
|
||||
return setmetatable(my_battery, mt)
|
|
@ -0,0 +1,67 @@
|
|||
local acompletion = require 'awful.completion'
|
||||
local aspawn = require 'awful.spawn'
|
||||
local gstring = require 'gears.string'
|
||||
local gtable = require 'gears.table'
|
||||
local prompt = require 'awful.widget.prompt'
|
||||
|
||||
local string = string
|
||||
|
||||
local function completion_cb (self, command_before_comp, cur_pos_before_comp, ncomp)
|
||||
return acompletion.generic(
|
||||
command_before_comp, cur_pos_before_comp, ncomp,
|
||||
self.completion_keywords)
|
||||
end
|
||||
|
||||
local function exe_cb (self, input)
|
||||
-- Exit if the input is empty
|
||||
if not input or #input == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Trim
|
||||
input = string.gsub(input, "^%s*(.-)%s*$", "%1")
|
||||
|
||||
-- If the input is not a VI command, spawn it
|
||||
if input:sub(1,1) ~= ':' then
|
||||
aspawn(input)
|
||||
return
|
||||
end
|
||||
|
||||
-- Parse the custom command
|
||||
local command, parameters = input:gmatch(':([%w-]+)%s*(.*)')()
|
||||
command = command:sub(1,1)
|
||||
parameters = gstring.split(parameters, '%s')
|
||||
|
||||
-- Quit if the command doesn't exist
|
||||
if not self.commands[command] then
|
||||
print('":' .. command .. '" not reconized as a command')
|
||||
return
|
||||
end
|
||||
|
||||
self.commands[command].callback(parameters)
|
||||
end
|
||||
|
||||
local my_prompt = { mt = {} }
|
||||
|
||||
function my_prompt.new (args)
|
||||
local prompt_widget = nil
|
||||
prompt_widget = prompt {
|
||||
prompt = '<b></b> ', -- needs the 3 spaces
|
||||
completion_callback = function (...) return completion_cb(prompt_widget, ...) end,
|
||||
exe_callback = function (...) return exe_cb(prompt_widget, ...) end
|
||||
}
|
||||
|
||||
prompt_widget.commands = args.commands
|
||||
prompt_widget.completion_keywords = gtable.join(
|
||||
-- TODO: find applications list
|
||||
gtable.find_keys(args.commands, function () return true end, false),
|
||||
args.completion_keywords or {})
|
||||
|
||||
return prompt_widget
|
||||
end
|
||||
|
||||
function my_prompt.mt.__call (self, ...)
|
||||
return my_prompt.new(...)
|
||||
end
|
||||
|
||||
return setmetatable(my_prompt, my_prompt.mt)
|
Loading…
Reference in New Issue