424 lines
18 KiB
Lua
424 lines
18 KiB
Lua
---------------------------------------------------------------------------
|
|
-- A calendar popup wibox.
|
|
--
|
|
-- Display a month or year calendar popup using `calendar_popup.month` or `calendar_popup.year`.
|
|
-- The calendar style can be tweaked by providing tables of style properties at creation:
|
|
-- `style_year`, `style_month`, `style_yearheader`, `style_header`,
|
|
-- `style_weekday`, `style_weeknumber`, `style_normal`, `style_focus` (see `cell_properties`).
|
|
--
|
|
-- The wibox accepts arguments for the calendar widget: `font`, `spacing`, `week_numbers`,
|
|
-- `start_sunday`, `long_weekdays`.
|
|
-- It also accepts the extra arguments `opacity`, `bg`, `screen` and `position`.
|
|
-- `opacity` and `bg` apply to the wibox itself, they are mainly useful to manage opacity
|
|
-- by setting `opacity` for the false opacity or setting `bg="#00000000"` for compositor opacity.
|
|
-- The `screen` argument forces the display of the wibox to this screen (instead of the focused screen by default).
|
|
-- The `position` argument is a two-characters string describing the screen alignment "[vertical][horizontal]",
|
|
-- e.g. "cc", "tr", "bl", ...
|
|
--
|
|
-- The wibox visibility can be changed calling the `toggle` method.
|
|
-- The `attach` method adds mouse bindings to an existing widget in order to toggle the display of the wibox.
|
|
--
|
|
--@DOC_awful_widget_calendar_popup_default_EXAMPLE@
|
|
--
|
|
-- @author getzze
|
|
-- @copyright 2017 getzze
|
|
-- @popupmod awful.widget.calendar_popup
|
|
---------------------------------------------------------------------------
|
|
|
|
local setmetatable = setmetatable
|
|
local string = string
|
|
local gears = require("gears")
|
|
local wibox = require("wibox")
|
|
local base = require("wibox.widget.base")
|
|
local ascreen = require("awful.screen")
|
|
local abutton = require("awful.button")
|
|
local beautiful = require("beautiful")
|
|
|
|
local calendar_popup = { offset = 0, mt = {} }
|
|
|
|
local properties = { "markup", "fg_color", "bg_color", "shape", "padding", "border_width", "border_color", "opacity" }
|
|
local styles = { "year", "month", "yearheader", "monthheader", "header", "weekday", "weeknumber", "normal", "focus" }
|
|
|
|
--- The generic calendar style table.
|
|
--
|
|
-- Each table property can also be defined by `beautiful.calendar_[flag]_[property]=val`.
|
|
-- @beautiful beautiful.calendar_style
|
|
-- @tparam cell_properties table Table of cell style properties
|
|
|
|
--- Cell properties.
|
|
-- @field markup Markup function or format string
|
|
-- @field fg_color Text foreground color
|
|
-- @field bg_color Text background color
|
|
-- @field shape Cell shape
|
|
-- @field padding Cell padding
|
|
-- @field border_width Calendar border width
|
|
-- @field border_color Calendar border color
|
|
-- @field opacity Cell opacity
|
|
-- @table cell_properties
|
|
|
|
--- Cell types (flags).
|
|
-- @field year Year calendar grid properties table
|
|
-- @field month Month calendar grid properties table
|
|
-- @field yearheader Year header cell properties table
|
|
-- @field header Month header cell properties table (called `monthheader` for a year calendar)
|
|
-- @field weekday Weekday cell properties table
|
|
-- @field weeknumber Weeknumber cell properties table
|
|
-- @field normal Normal day cell properties table
|
|
-- @field focus Current day cell properties table
|
|
-- @table cell_flags
|
|
|
|
--- Create a container for the grid layout
|
|
-- @tparam table tprops Table of calendar container properties.
|
|
-- @treturn function Embedding function widget,flag,date -> widget
|
|
local function embed(tprops)
|
|
local function fn (widget, flag, _)
|
|
if flag == "monthheader" and not tprops.monthheader then
|
|
flag = "header"
|
|
end
|
|
local props = tprops[flag]
|
|
-- Markup
|
|
if flag ~= "year" and flag ~= "month" then
|
|
local markup = widget:get_text()
|
|
local m = props.markup
|
|
if type(m) == "function" then
|
|
markup = m(markup)
|
|
elseif type(m) == "string" and string.find(m, "%s", 1, true) then
|
|
markup = string.format(m, markup)
|
|
end
|
|
widget:set_markup(markup)
|
|
end
|
|
|
|
local out = base.make_widget_declarative {
|
|
{
|
|
widget,
|
|
margins = props.padding + props.border_width,
|
|
widget = wibox.container.margin
|
|
},
|
|
shape = props.shape or gears.shape.rectangle,
|
|
border_color = props.border_color,
|
|
border_width = props.border_width,
|
|
fg = props.fg_color,
|
|
bg = props.bg_color,
|
|
opacity = props.opacity,
|
|
widget = wibox.container.background
|
|
}
|
|
return out
|
|
end
|
|
return fn
|
|
end
|
|
|
|
--- Parse the properties of the cell type and set default values
|
|
-- @tparam string cell The cell type
|
|
-- @tparam table args Table of properties to enforce
|
|
-- @treturn table The properties table
|
|
local function parse_cell_options(cell, args)
|
|
args = args or {}
|
|
local props = {}
|
|
local bl_style = beautiful.calendar_style or {}
|
|
|
|
for _, prop in ipairs(properties) do
|
|
local default
|
|
if prop == 'fg_color' then
|
|
default = cell == "focus" and beautiful.fg_focus or beautiful.fg_normal
|
|
elseif prop == 'bg_color' then
|
|
default = cell == "focus" and beautiful.bg_focus or beautiful.bg_normal
|
|
elseif prop == 'padding' then
|
|
default = 2
|
|
elseif prop == 'opacity' then
|
|
default = 1
|
|
elseif prop == 'shape' then
|
|
default = nil
|
|
elseif prop == 'border_width' then
|
|
default = beautiful.border_width or 0
|
|
elseif prop == 'border_color' then
|
|
default = beautiful.border_color_normal or beautiful.fg_normal
|
|
end
|
|
|
|
-- Get default
|
|
props[prop] = args[prop] or beautiful["calendar_" .. cell .. "_" .. prop] or bl_style[prop] or default
|
|
end
|
|
props['markup'] = cell == "focus" and
|
|
(args['markup'] or beautiful["calendar_" .. cell .. "_markup"] or bl_style['markup'] or
|
|
string.format('<span foreground="%s" background="%s"><b>%s</b></span>',
|
|
props['fg_color'], props['bg_color'], "%s")
|
|
)
|
|
return props
|
|
end
|
|
|
|
--- Parse the properties
|
|
-- @tparam table args Table of properties
|
|
-- @treturn table The properties table
|
|
local function parse_all_options(args)
|
|
args = args or {}
|
|
local props = {}
|
|
|
|
for _, cell in pairs(styles) do
|
|
if cell~="monthheader" or args.style_monthheader then
|
|
props[cell] = parse_cell_options(cell, args["style_" .. cell])
|
|
end
|
|
end
|
|
return props
|
|
end
|
|
|
|
--- Make the geometry of a wibox
|
|
-- @tparam widget widget Calendar widget
|
|
-- @tparam object screen Screen where to display the calendar (default to focused)
|
|
-- @tparam string position Two characters position of the calendar (default "cc")
|
|
-- @treturn number,number,number,number Geometry of the calendar, list of x, y, width, height
|
|
local function get_geometry(widget, screen, position)
|
|
local pos = position or "cc"
|
|
local s = screen or ascreen.focused()
|
|
local margin = widget._calendar_margin or 0
|
|
local wa = s.workarea
|
|
local width, height = widget:fit({screen=s, dpi=s.dpi}, wa.width, wa.height)
|
|
|
|
width = width < wa.width and width or wa.width
|
|
height = height < wa.height and height or wa.height
|
|
|
|
|
|
-- Set to position: pos = tl, tc, tr
|
|
-- cl, cc, cr
|
|
-- bl, bc, br
|
|
local x,y
|
|
if pos:sub(1,1) == "t" then
|
|
y = wa.y + margin
|
|
elseif pos:sub(1,1) == "b" then
|
|
y = wa.y + wa.height - (height + margin)
|
|
else --if pos:sub(1,1) == "c" then
|
|
y = wa.y + math.floor((wa.height - height) / 2)
|
|
end
|
|
if pos:sub(2,2) == "l" then
|
|
x = wa.x + margin
|
|
elseif pos:sub(2,2) == "r" then
|
|
x = wa.x + wa.width - (width + margin)
|
|
else --if pos:sub(2,2) == "c" then
|
|
x = wa.x + math.floor((wa.width - width) / 2)
|
|
end
|
|
|
|
return {x=x, y=y, width=width, height=height}
|
|
end
|
|
|
|
--- Call the calendar with offset
|
|
-- @tparam number offset Offset with respect to current month or year
|
|
-- @tparam string position Two-character position of the calendar in the screen
|
|
-- @tparam screen screen Screen where to display the calendar
|
|
-- @treturn wibox The wibox calendar
|
|
-- @method call_calendar
|
|
function calendar_popup:call_calendar(offset, position, screen)
|
|
local inc_offset = offset or 0
|
|
local pos = position or self.position
|
|
local s = screen or self.screen or ascreen.focused()
|
|
self.position = pos -- remember last position when changing offset
|
|
|
|
self.offset = inc_offset ~= 0 and self.offset + inc_offset or 0
|
|
|
|
local widget = self:get_widget()
|
|
local raw_date = os.date("*t")
|
|
local date = {day=raw_date.day, month=raw_date.month, year=raw_date.year}
|
|
if widget._private.type == "month" and self.offset ~= 0 then
|
|
local month_offset = (raw_date.month + self.offset - 1) % 12 + 1
|
|
local year_offset = raw_date.year + math.floor((raw_date.month + self.offset - 1) / 12)
|
|
date = {month=month_offset, year=year_offset }
|
|
elseif widget._private.type == "year" then
|
|
date = {year=raw_date.year + self.offset}
|
|
end
|
|
|
|
-- set date and screen before updating geometry
|
|
widget:set_date(date)
|
|
self:set_screen(s)
|
|
-- update geometry (depends on date and screen)
|
|
self:geometry(get_geometry(widget, s, pos))
|
|
return self
|
|
end
|
|
|
|
--- Toggle calendar visibility.
|
|
-- @method toggle
|
|
function calendar_popup:toggle()
|
|
self:call_calendar(0)
|
|
self.visible = not self.visible
|
|
end
|
|
|
|
--- Attach the calendar to a widget to display at a specific position.
|
|
--
|
|
-- local mytextclock = wibox.widget.textclock()
|
|
-- local month_calendar = awful.widget.calendar_popup.month()
|
|
-- month_calendar:attach(mytextclock, 'tr')
|
|
--
|
|
-- @param widget Widget to attach the calendar
|
|
-- @tparam[opt="tr"] string position Two characters string defining the position on the screen
|
|
-- @tparam[opt={}] table args Additional options
|
|
-- @tparam[opt=true] bool args.on_hover Show popup during mouse hover
|
|
-- @treturn wibox The wibox calendar
|
|
-- @method attach
|
|
function calendar_popup:attach(widget, position, args)
|
|
position = position or "tr"
|
|
args = args or {}
|
|
if args.on_hover == nil then args.on_hover=true end
|
|
|
|
widget.buttons = {
|
|
abutton({ }, 1, function ()
|
|
if not self.visible or self._calendar_clicked_on then
|
|
self:call_calendar(0, position)
|
|
self.visible = not self.visible
|
|
end
|
|
self._calendar_clicked_on = self.visible
|
|
end),
|
|
abutton({ }, 4, function () self:call_calendar(-1) end),
|
|
abutton({ }, 5, function () self:call_calendar( 1) end)
|
|
}
|
|
|
|
if args.on_hover then
|
|
widget:connect_signal("mouse::enter", function ()
|
|
if not self._calendar_clicked_on then
|
|
self:call_calendar(0, position)
|
|
self.visible = true
|
|
end
|
|
end)
|
|
widget:connect_signal("mouse::leave", function ()
|
|
if not self._calendar_clicked_on then
|
|
self.visible = false
|
|
end
|
|
end)
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Return a new calendar wibox by type.
|
|
--
|
|
-- A calendar widget displaying a `month` or a `year`
|
|
-- @tparam string caltype Type of calendar `month` or `year`
|
|
-- @tparam table args Properties of the widget
|
|
-- @tparam string args.position Two-character position of the calendar in the screen
|
|
-- @tparam screen args.screen Screen where to display the calendar
|
|
-- @tparam number args.opacity Wibox opacity
|
|
-- @tparam string args.bg Wibox background color
|
|
-- @tparam string args.font Calendar font
|
|
-- @tparam number args.spacing Calendar spacing
|
|
-- @tparam number args.margin Margin around calendar widget
|
|
-- @tparam boolean args.week_numbers Show weeknumbers
|
|
-- @tparam boolean args.start_sunday Start week on Sunday
|
|
-- @tparam boolean args.long_weekdays Format the weekdays with three characters instead of two
|
|
-- @tparam table args.style_year Container style for the year calendar (see `cell_properties`)
|
|
-- @tparam table args.style_month Container style for the month calendar (see `cell_properties`)
|
|
-- @tparam table args.style_yearheader Cell style for the year calendar header (see `cell_properties`)
|
|
-- @tparam table args.style_header Cell style for the month calendar header (see `cell_properties`)
|
|
-- @tparam table args.style_weekday Cell style for the weekday cells (see `cell_properties`)
|
|
-- @tparam table args.style_weeknumber Cell style for the weeknumber cells (see `cell_properties`)
|
|
-- @tparam table args.style_normal Cell style for the normal day cells (see `cell_properties`)
|
|
-- @tparam table args.style_focus Cell style for the current day cell (see `cell_properties`)
|
|
-- @treturn wibox A wibox containing the calendar
|
|
local function get_cal_wibox(caltype, args)
|
|
args = args or {}
|
|
|
|
local ret = wibox{ ontop = true,
|
|
opacity = args.opacity or 1,
|
|
bg = args.bg or gears.color.transparent
|
|
}
|
|
gears.table.crush(ret, calendar_popup, false)
|
|
|
|
ret.offset = 0
|
|
ret.position = args.position or "cc"
|
|
ret.screen = args.screen
|
|
|
|
local widget = wibox.widget {
|
|
font = args.font or beautiful.font,
|
|
spacing = args.spacing,
|
|
week_numbers = args.week_numbers,
|
|
start_sunday = args.start_sunday,
|
|
long_weekdays = args.long_weekdays,
|
|
fn_embed = embed(parse_all_options(args)),
|
|
_calendar_margin = args.margin,
|
|
widget = caltype == "year" and wibox.widget.calendar.year or wibox.widget.calendar.month
|
|
}
|
|
ret:set_widget(widget)
|
|
|
|
ret.buttons = {
|
|
abutton({ }, 1, function ()
|
|
ret.visible=false
|
|
ret._calendar_clicked_on=false
|
|
end),
|
|
abutton({ }, 3, function ()
|
|
ret.visible=false
|
|
ret._calendar_clicked_on=false
|
|
end),
|
|
abutton({ }, 4, function () ret:call_calendar(-1) end),
|
|
abutton({ }, 5, function () ret:call_calendar( 1) end)
|
|
}
|
|
|
|
return ret
|
|
end
|
|
|
|
--- A month calendar wibox.
|
|
--
|
|
-- It is highly customizable using the same options as for the widgets.
|
|
-- The options are set once and for all at creation, though.
|
|
--
|
|
--@DOC_wibox_awidget_calendar_month_wibox_EXAMPLE@
|
|
--
|
|
-- local mytextclock = wibox.widget.textclock()
|
|
-- local month_calendar = awful.widget.calendar_popup.month()
|
|
-- month_calendar:attach( mytextclock, "tr" )
|
|
--
|
|
-- @tparam table args Properties of the widget
|
|
-- @tparam string args.position Two-character position of the calendar in the screen
|
|
-- @tparam screen args.screen Screen where to display the calendar
|
|
-- @tparam number args.opacity Wibox opacity
|
|
-- @tparam string args.bg Wibox background color
|
|
-- @tparam string args.font Calendar font
|
|
-- @tparam number args.spacing Calendar spacing
|
|
-- @tparam number args.margin Margin around calendar widget
|
|
-- @tparam boolean args.week_numbers Show weeknumbers
|
|
-- @tparam boolean args.start_sunday Start week on Sunday
|
|
-- @tparam boolean args.long_weekdays Format the weekdays with three characters instead of two
|
|
-- @tparam table args.style_month Container style for the month calendar (see `cell_properties`)
|
|
-- @tparam table args.style_header Cell style for the month calendar header (see `cell_properties`)
|
|
-- @tparam table args.style_weekday Cell style for the weekday cells (see `cell_properties`)
|
|
-- @tparam table args.style_weeknumber Cell style for the weeknumber cells (see `cell_properties`)
|
|
-- @tparam table args.style_normal Cell style for the normal day cells (see `cell_properties`)
|
|
-- @tparam table args.style_focus Cell style for the current day cell (see `cell_properties`)
|
|
-- @treturn wibox A wibox containing the calendar
|
|
-- @constructorfct awful.widget.calendar_popup.month
|
|
function calendar_popup.month(args)
|
|
return get_cal_wibox("month", args)
|
|
end
|
|
|
|
--- A year calendar wibox.
|
|
--
|
|
-- It is highly customizable using the same options as for the widgets.
|
|
-- The options are set once and for all at creation, though.
|
|
--
|
|
--@DOC_wibox_awidget_calendar_year_wibox_EXAMPLE@
|
|
--
|
|
-- globalkeys = gears.table.join(globalkeys, awful.key(
|
|
-- { modkey, "Control" }, "c", function () year_calendar:toggle() end))
|
|
--
|
|
-- @tparam table args Properties of the widget
|
|
-- @tparam string args.position Two-character position of the calendar in the screen
|
|
-- @tparam screen args.screen Screen where to display the calendar
|
|
-- @tparam number args.opacity Wibox opacity
|
|
-- @tparam string args.bg Wibox background color
|
|
-- @tparam string args.font Calendar font
|
|
-- @tparam number args.spacing Calendar spacing
|
|
-- @tparam number args.margin Margin around calendar widget
|
|
-- @tparam boolean args.week_numbers Show weeknumbers
|
|
-- @tparam boolean args.start_sunday Start week on Sunday
|
|
-- @tparam boolean args.long_weekdays Format the weekdays with three characters instead of two
|
|
-- @tparam table args.style_year Container style for the year calendar (see `cell_properties`)
|
|
-- @tparam table args.style_month Container style for the month calendar (see `cell_properties`).
|
|
-- This field can also be called `style_monthheader`.
|
|
-- @tparam table args.style_yearheader Cell style for the year calendar header (see `cell_properties`)
|
|
-- @tparam table args.style_header Cell style for the month calendar header (see `cell_properties`)
|
|
-- @tparam table args.style_weekday Cell style for the weekday cells (see `cell_properties`)
|
|
-- @tparam table args.style_weeknumber Cell style for the weeknumber cells (see `cell_properties`)
|
|
-- @tparam table args.style_normal Cell style for the normal day cells (see `cell_properties`)
|
|
-- @tparam table args.style_focus Cell style for the current day cell (see `cell_properties`)
|
|
-- @treturn wibox A wibox containing the calendar
|
|
-- @constructorfct awful.widget.calendar_popup.year
|
|
function calendar_popup.year(args)
|
|
return get_cal_wibox("year", args)
|
|
end
|
|
|
|
return setmetatable(calendar_popup, calendar_popup.mt)
|
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|