Merge pull request #1689 from getzze/wibox_calendar

Add calendar widgets (wibox and popup)
This commit is contained in:
Emmanuel Lepage Vallée 2017-06-12 03:11:24 -04:00 committed by GitHub
commit e993d7a117
13 changed files with 1104 additions and 0 deletions

View File

@ -0,0 +1,390 @@
---------------------------------------------------------------------------
-- 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_wibox_awidget_defaults_calendar_popup_EXAMPLE@
--
-- @author getzze
-- @copyright 2017 getzze
-- @classmod 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,
shape_border_color = props.border_color,
shape_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 == 'markup' then
default = cell == "focus" and string.format('<span foreground="%s" background="%s"><b>%s</b></span>',
beautiful.fg_focus, beautiful.bg_focus, "%s")
elseif 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_normal or beautiful.fg_normal
end
-- Get default
props[prop] = args[prop] or beautiful["calendar_" .. cell .. "_" .. prop] or bl_style[prop] or default
end
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, s = position or "cc", screen or ascreen.focused()
local wa = s.workarea
local width, height = widget:fit({screen=s, dpi=beautiful.xresources.get_dpi(s)}, 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
elseif pos:sub(1,1) == "b" then
y = wa.y + wa.height - height
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
elseif pos:sub(2,2) == "r" then
x = wa.x + wa.width - width
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
function calendar_popup:call_calendar(offset, position, screen)
local inc_offset, pos, s = offset or 0, position or self.position, 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
raw_date.month = raw_date.month + self.offset
raw_date = os.date("*t", os.time(raw_date))
date = {month=raw_date.month, year=raw_date.year}
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
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 = calendar.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
-- @treturn wibox The wibox calendar
function calendar_popup:attach(widget, position)
position = position or "tr"
widget:buttons(gears.table.join(
abutton({ }, 1, function ()
self:call_calendar(0, position)
self.visible = not self.visible
end),
abutton({ }, 4, function () self:call_calendar(-1) end),
abutton({ }, 5, function () self:call_calendar( 1) 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 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.year_style Container style for the year calendar (see `cell_properties`)
-- @tparam table args.month_style Container style for the month calendar (see `cell_properties`)
-- @tparam table args.yearheader_style Cell style for the year calendar header (see `cell_properties`)
-- @tparam table args.header_style Cell style for the month calendar header (see `cell_properties`)
-- @tparam table args.weekday_style Cell style for the weekday cells (see `cell_properties`)
-- @tparam table args.weeknumber_style Cell style for the weeknumber cells (see `cell_properties`)
-- @tparam table args.normal_style Cell style for the normal day cells (see `cell_properties`)
-- @tparam table args.focus_style 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
}
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,
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)),
widget = caltype == "year" and wibox.widget.calendar.year or wibox.widget.calendar.month
}
ret:set_widget(widget)
ret:buttons(gears.table.join(
abutton({ }, 1, function () ret.visible=false end),
abutton({ }, 3, function () ret.visible=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()
-- 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 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.month_style Container style for the month calendar (see `cell_properties`)
-- @tparam table args.header_style Cell style for the month calendar header (see `cell_properties`)
-- @tparam table args.weekday_style Cell style for the weekday cells (see `cell_properties`)
-- @tparam table args.weeknumber_style Cell style for the weeknumber cells (see `cell_properties`)
-- @tparam table args.normal_style Cell style for the normal day cells (see `cell_properties`)
-- @tparam table args.focus_style Cell style for the current day cell (see `cell_properties`)
-- @treturn wibox A wibox containing the calendar
-- @function awful.calendar.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 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.year_style Container style for the year calendar (see `cell_properties`)
-- @tparam table args.month_style Container style for the month calendar (see `cell_properties`).
-- This field can also be called `monthheader_style`.
-- @tparam table args.yearheader_style Cell style for the year calendar header (see `cell_properties`)
-- @tparam table args.header_style Cell style for the month calendar header (see `cell_properties`)
-- @tparam table args.weekday_style Cell style for the weekday cells (see `cell_properties`)
-- @tparam table args.weeknumber_style Cell style for the weeknumber cells (see `cell_properties`)
-- @tparam table args.normal_style Cell style for the normal day cells (see `cell_properties`)
-- @tparam table args.focus_style Cell style for the current day cell (see `cell_properties`)
-- @treturn wibox A wibox containing the calendar
-- @function awful.calendar.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

View File

@ -21,6 +21,7 @@ return
watch = require("awful.widget.watch"); watch = require("awful.widget.watch");
only_on_screen = require("awful.widget.only_on_screen"); only_on_screen = require("awful.widget.only_on_screen");
clienticon = require("awful.widget.clienticon"); clienticon = require("awful.widget.clienticon");
calendar_popup = require("awful.widget.calendar_popup");
} }
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,385 @@
---------------------------------------------------------------------------
-- A calendar widget
--
-- This module defines two widgets: a month calendar and a year calendar
--
-- The two widgets have a `date` property, in the form of
-- a table {day=[number|nil], month=[number|nil], year=[number]}.
--
-- The `year` widget displays the whole specified year, e.g. {year=2006}.
--
-- The `month` widget displays the calendar for the specified month, e.g. {month=12, year=2006},
-- highlighting the specified day if the day is provided in the date, e.g. {day=22, month=12, year=2006}.
--
-- Cell and container styles can be overridden using the `fn_embed` callback function
-- which is called before adding the widgets to the layouts. The `fn_embed` function
-- takes three arguments, the original widget, the flag (`string` used to identified the widget)
-- and the date (`table`).
-- It returns another widget, embedding (and modifying) the original widget.
--
--@DOC_wibox_widget_defaults_calendar_EXAMPLE@
--
-- @author getzze
-- @copyright 2017 getzze
-- @classmod wibox.widget.calendar
---------------------------------------------------------------------------
local setmetatable = setmetatable
local string = string
local gtable = require("gears.table")
local vertical = require("wibox.layout.fixed").vertical
local grid = require("wibox.layout.grid")
local textbox = require("wibox.widget.textbox")
local bgcontainer = require("wibox.container.background")
local base = require("wibox.widget.base")
local beautiful = require("beautiful")
local calendar = { mt = {} }
local properties = { "date", "font", "spacing", "week_numbers", "start_sunday", "long_weekdays", "fn_embed" }
--- The calendar font.
-- @beautiful beautiful.calendar_font
-- @tparam string font Font of the calendar
--- The calendar spacing.
-- @beautiful beautiful.calendar_spacing
-- @tparam number spacing Spacing of the grid (twice this value for inter-month spacing)
--- Display the calendar week numbers.
-- @beautiful beautiful.calendar_week_numbers
-- @param boolean Display week numbers
--- Start the week on Sunday.
-- @beautiful beautiful.calendar_start_sunday
-- @param boolean Start the week on Sunday
--- Format the weekdays with three characters instead of two
-- @beautiful beautiful.calendar_long_weekdays
-- @param boolean Use three characters for the weekdays instead of two
--- The calendar date.
--
-- A table representing the date {day=[number|nil], month=[number|nil], year=[number]}.
--
-- E.g.. {day=21, month=2, year=2005}, {month=2, year=2005}, {year=2005}
-- @tparam date table Date table.
-- @tparam number date.year Date year
-- @tparam number|nil date.month Date month
-- @tparam number|nil date.day Date day
-- @property date
--- The calendar font.
--
-- Choose a monospace font for a better rendering.
--@DOC_wibox_widget_calendar_font_EXAMPLE@
-- @param[opt="Monospace 10"] string Font of the calendar
-- @property font
--- The calendar spacing.
--
-- The spacing between cells in the month.
-- The spacing between months in a year calendar is twice this value.
-- @param[opt=5] number Spacing of the grid
-- @property spacing
--- Display the calendar week numbers.
--
--@DOC_wibox_widget_calendar_week_numbers_EXAMPLE@
-- @param[opt=false] boolean Display week numbers
-- @property week_numbers
--- Start the week on Sunday.
--
--@DOC_wibox_widget_calendar_start_sunday_EXAMPLE@
-- @param[opt=false] boolean Start the week on Sunday
-- @property start_sunday
--- Format the weekdays with three characters instead of two
--
--@DOC_wibox_widget_calendar_long_weekdays_EXAMPLE@
-- @param[opt=false] boolean Use three characters for the weekdays instead of two
-- @property long_weekdays
--- The widget encapsulating function.
--
-- Function that takes a widget, flag (`string`) and date (`table`) as argument
-- and returns a widget encapsulating the input widget.
--
-- Default value: function (widget, flag, date) return widget end
--
-- It is used to add a container to the grid layout and to the cells:
--
--@DOC_wibox_widget_calendar_fn_embed_cell_EXAMPLE@
-- @param function Function to embed the widget depending on its flag
-- @property fn_embed
--- Make a textbox
-- @tparam string text Text of the textbox
-- @tparam string font Font of the text
-- @tparam boolean center Center the text horizontally
-- @treturn wibox.widget.textbox
local function make_cell(text, font, center)
return base.make_widget_declarative {
markup = text,
align = center and "center" or "right",
valign = 'center',
font = font,
widget = textbox
}
end
--- Create a grid layout with the month calendar
-- @tparam table props Table of calendar properties
-- @tparam table date Date table
-- @tparam number date.year Date year
-- @tparam number date.month Date month
-- @tparam number|nil date.day Date day
-- @treturn widget Grid layout
local function create_month(props, date)
local num_rows = 8
local num_columns = props.week_numbers and 8 or 7
-- Create grid layout
local layout = base.make_widget_declarative{
layout = grid,
expand = true,
homogeneous = true,
spacing = props.spacing,
forced_num_rows = num_rows,
forced_num_cols = num_columns,
}
local start_row = 3
local start_column = num_columns - 6
local week_start = props.start_sunday and 1 or 2
local last_day = os.date("*t", os.time{year=date.year, month=date.month+1, day=0})
local month_days = last_day.day
local column_fday = (last_day.wday - month_days + 1 - week_start ) % 7
--local flags = {"header", "weekdays", "weeknumber", "normal", "focus"}
local cell_date, t, i, j, w, flag, text
-- Header
flag = "header"
t = os.time{year=date.year, month=date.month, day=1}
if props.subtype=="monthheader" then
flag = "monthheader"
text = os.date("%B", t)
else
text = os.date("%B %Y", t)
end
w = props.fn_embed(make_cell(text, props.font, true), flag, date)
layout:add_widget_at(w, 1, 1, 1, num_columns)
-- Days
i = start_row
j = column_fday + start_column
local current_week = nil
local drawn_weekdays = 0
for d=1, month_days do
cell_date = {year=date.year, month=date.month, day=d}
t = os.time(cell_date)
-- Week number
if props.week_numbers then
text = os.date("%V", t)
if tonumber(text) ~= current_week then
flag = "weeknumber"
w = props.fn_embed(make_cell(text, props.font), flag, cell_date)
layout:add_widget_at(w, i, 1, 1, 1)
current_week = tonumber(text)
end
end
-- Week days
if drawn_weekdays < 7 then
flag = "weekday"
text = os.date("%a", t)
if not props.long_weekdays then
text = string.sub(text, 1, 2)
end
w = props.fn_embed(make_cell(text, props.font), flag, cell_date)
layout:add_widget_at(w, 2, j, 1, 1)
drawn_weekdays = drawn_weekdays +1
end
-- Normal day
flag = "normal"
text = string.format("%2d", d)
-- Focus day
if date.day == d then
flag = "focus"
text = "<b>"..text.."</b>"
end
w = props.fn_embed(make_cell(text, props.font), flag, cell_date)
layout:add_widget_at(w, i, j, 1, 1)
-- find next cell
i,j = layout:get_next_empty(i,j)
if j < start_column then j = start_column end
end
return props.fn_embed(layout, "month", date)
end
--- Create a grid layout for the year calendar
-- @tparam table props Table of year calendar properties
-- @param date Year to display (number or string)
-- @treturn widget Grid layout
local function create_year(props, date)
-- Create a grid widget with the 12 months
local in_layout = base.make_widget_declarative{
layout = grid,
expand = true,
homogeneous = true,
spacing = 2*props.spacing,
forced_num_cols = 4,
forced_num_rows = 3,
}
local month_date
local current_date = os.date("*t")
for month=1,12 do
if date.year == current_date.year and month == current_date.month then
month_date = {day=current_date.day, month=current_date.month, year=current_date.year}
else
month_date = {month=month, year=date.year}
end
in_layout:add(create_month(props, month_date))
end
-- Create a vertical layout
local flag, text = "yearheader", string.format("%s", date.year)
local year_header = props.fn_embed(make_cell(text, props.font, true), flag, date)
local out_layout = base.make_widget_declarative{
year_header,
in_layout,
spacing = 2*props.spacing, -- separate header from calendar grid
layout = vertical
}
return props.fn_embed(out_layout, "year", date)
end
--- Set the container to the current date
-- @param self Widget to update
local function fill_container(self)
local date = self._private.date
if date then
-- Create calendar grid
if self._private.type == "month" then
self._private.container:set_widget(create_month(self._private, date))
elseif self._private.type == "year" then
self._private.container:set_widget(create_year(self._private, date))
end
else
self._private.container:set_widget(nil)
end
self:emit_signal("widget::layout_changed")
end
-- Set the calendar date
function calendar:set_date(date)
if date ~= self._private.date then
self._private.date = date
-- (Re)create calendar grid
fill_container(self)
end
end
-- Build properties function
for _, prop in ipairs(properties) do
-- setter
if not calendar["set_" .. prop] then
calendar["set_" .. prop] = function(self, value)
if (string.sub(prop,1,3)=="fn_" and type(value) == "function") or self._private[prop] ~= value then
self._private[prop] = value
-- (Re)create calendar grid
fill_container(self)
end
end
end
-- getter
if not calendar["get_" .. prop] then
calendar["get_" .. prop] = function(self)
return self._private[prop]
end
end
end
--- Return a new calendar widget by type.
--
-- @tparam string type Type of the calendar, `year` or `month`
-- @tparam table date Date of the calendar
-- @tparam number date.year Date year
-- @tparam number|nil date.month Date month
-- @tparam number|nil date.day Date day
-- @tparam[opt="Monospace 10"] string font Font of the calendar
-- @treturn widget The calendar widget
local function get_calendar(type, date, font)
local ct = bgcontainer()
local ret = base.make_widget(ct, "calendar", {enable_properties = true})
gtable.crush(ret, calendar, true)
ret._private.type = type
ret._private.container = ct
-- default values
ret._private.date = date
ret._private.font = font or beautiful.calendar_font or "Monospace 10"
ret._private.spacing = beautiful.calendar_spacing or 5
ret._private.week_numbers = beautiful.calendar_week_numbers or false
ret._private.start_sunday = beautiful.calendar_start_sunday or false
ret._private.long_weekdays = beautiful.calendar_long_weekdays or false
ret._private.fn_embed = function (w, _) return w end
-- header specific
ret._private.subtype = type=="year" and "monthheader" or "fullheader"
fill_container(ret)
return ret
end
--- A month calendar widget.
--
-- A calendar widget is a grid containing the calendar for one month.
-- If the day is specified in the date, its cell is highlighted.
--
--@DOC_wibox_widget_calendar_month_EXAMPLE@
-- @tparam table date Date of the calendar
-- @tparam number date.year Date year
-- @tparam number date.month Date month
-- @tparam number|nil date.day Date day
-- @tparam[opt="Monospace 10"] string font Font of the calendar
-- @treturn widget The month calendar widget
-- @function wibox.widget.calendar.month
function calendar.month(date, font)
return get_calendar("month", date, font)
end
--- A year calendar widget.
--
-- A calendar widget is a grid containing the calendar for one year.
--
--@DOC_wibox_widget_calendar_year_EXAMPLE@
-- @tparam table date Date of the calendar
-- @tparam number date.year Date year
-- @tparam number|nil date.month Date month
-- @tparam number|nil date.day Date day
-- @tparam[opt="Monospace 10"] string font Font of the calendar
-- @treturn widget The year calendar widget
-- @function wibox.widget.calendar.year
function calendar.year(date, font)
return get_calendar("year", date, font)
end
return setmetatable(calendar, calendar.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -19,6 +19,7 @@ local widget = {
checkbox = require("wibox.widget.checkbox"); checkbox = require("wibox.widget.checkbox");
piechart = require("wibox.widget.piechart"); piechart = require("wibox.widget.piechart");
slider = require("wibox.widget.slider"); slider = require("wibox.widget.slider");
calendar = require("wibox.widget.calendar");
} }
setmetatable(widget, { setmetatable(widget, {

View File

@ -0,0 +1,84 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local gears = require("gears") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("monospace 10") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local styles = {}
local function rounded_shape(size, partial)
if partial then
return function(cr, width, height)
gears.shape.partially_rounded_rect(cr, width, height,
false, true, false, true, 5)
end
else
return function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, size)
end
end
end
styles.month = { padding = 5,
bg_color = "#555555",
border_width = 2,
shape = rounded_shape(10)
}
styles.normal = { shape = rounded_shape(5) }
styles.focus = { fg_color = "#000000",
bg_color = "#ff9800",
markup = function(t) return '<b>' .. t .. '</b>' end,
shape = rounded_shape(5, true)
}
styles.header = { fg_color = "#de5e1e",
markup = function(t) return '<b>' .. t .. '</b>' end,
shape = rounded_shape(10)
}
styles.weekday = { fg_color = "#7788af",
markup = function(t) return '<b>' .. t .. '</b>' end,
shape = rounded_shape(5)
}
local function decorate_cell(widget, flag, date)
if flag=="monthheader" and not styles.monthheader then
flag = "header"
end
local props = styles[flag] or {}
if props.markup and widget.get_text and widget.set_markup then
widget:set_markup(props.markup(widget:get_text()))
end
-- Change bg color for weekends
local d = {year=date.year, month=(date.month or 1), day=(date.day or 1)}
local weekday = tonumber(os.date("%w", os.time(d)))
local default_bg = (weekday==0 or weekday==6) and "#232323" or "#383838"
local ret = wibox.widget {
{
widget,
margins = (props.padding or 2) + (props.border_width or 0),
widget = wibox.container.margin
},
shape = props.shape,
shape_border_color = props.border_color or "#b9214f",
shape_border_width = props.border_width or 0,
fg = props.fg_color or "#999999",
bg = props.bg_color or default_bg,
widget = wibox.container.background
}
return ret
end
local cal = wibox.widget {
date = os.date("*t"),
fn_embed = decorate_cell,
widget = wibox.widget.calendar.month
}
parent:add(cal) --DOC_HIDE

View File

@ -0,0 +1,17 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("sans 12") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local cal = wibox.widget.calendar.month(
os.date("*t"), "sans 12")
parent:add(cal) --DOC_HIDE

View File

@ -0,0 +1,21 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("monospace 10") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local cal = wibox.widget {
date = os.date("*t"),
font = "Monospace 10",
long_weekdays = true,
widget = wibox.widget.calendar.month
}
parent:add(cal) --DOC_HIDE

View File

@ -0,0 +1,51 @@
local parent = ... --DOC_HIDE_ALL
local wibox = require("wibox") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("monospace 10") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local date = os.date("*t") --DOC_HIDE
local w = wibox.widget {
{
{
{
text = '{day='..date.day..', month='..date.month..',\n year='..date.year..'}',
align = 'center',
widget = wibox.widget.textbox
},
border_width = 2,
bg = beautiful.bg_normal,
widget = wibox.container.background
},
wibox.widget.calendar.month({day=date.day, month=date.month, year=date.year}),
spacing = 10,
widget = wibox.layout.fixed.vertical
},
{
{
{
text = '{month='..date.month..',\n year='..date.year..'}',
align = 'center',
widget = wibox.widget.textbox
},
border_width = 2,
bg = beautiful.bg_normal,
widget = wibox.container.background
},
wibox.widget.calendar.month({month=date.month, year=date.year}),
spacing = 10,
widget = wibox.layout.fixed.vertical
},
spacing = 20,
widget = wibox.layout.flex.horizontal
}
parent:add(w) --DOC_HIDE

View File

@ -0,0 +1,21 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("monospace 10") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local cal = wibox.widget {
date = os.date("*t"),
font = "Monospace 10",
start_sunday = true,
widget = wibox.widget.calendar.month
}
parent:add(cal) --DOC_HIDE

View File

@ -0,0 +1,21 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("monospace 10") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local cal = wibox.widget {
date = os.date("*t"),
font = "Monospace 10",
week_numbers = true,
widget = wibox.widget.calendar.month
}
parent:add(cal) --DOC_HIDE

View File

@ -0,0 +1,25 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("monospace 10") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local cal = wibox.widget {
date = os.date("*t"),
font = "Monospace 8",
spacing = 2,
week_numbers = false,
start_sunday = false,
widget = wibox.widget.calendar.year
}
parent:add(cal) --DOC_HIDE
local w,h = parent:fit({dpi=96}, 9999, 9999) --DOC_HIDE
return w+30, h --DOC_HIDE

View File

@ -0,0 +1,16 @@
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require( "beautiful" ) --DOC_HIDE
local Pango = require("lgi").Pango --DOC_HIDE
-- Beautiful fake get_font --DOC_HIDE
local f = Pango.FontDescription.from_string("monospace 10") --DOC_HIDE
beautiful.get_font = function() return f end --DOC_HIDE
-- Fake beautiful theme --DOC_HIDE
beautiful.fg_focus = "#ff9800" --DOC_HIDE
beautiful.bg_focus = "#b9214f" --DOC_HIDE
local cal = wibox.widget.calendar.month(os.date("*t"))
parent:add(cal) --DOC_HIDE

View File

@ -0,0 +1,71 @@
--- Test for awful.widget.calendar
local runner = require("_runner")
local awful = require("awful")
local calendar = require("awful.widget.calendar_popup")
local wa = awful.screen.focused().workarea
local cmonth = calendar.month()
local cyear = calendar.year()
local current_date = os.date("*t")
local day, month, year = current_date.day, current_date.month, current_date.year
local steps = {
-- Check center geometry
function(count)
if count == 1 then
cmonth:call_calendar(0, 'cc')
else
local geo = cmonth:geometry()
assert( math.abs((wa.x + wa.width/2.0) - (geo.x + geo.width/2.0)) < 2 )
assert( math.abs((wa.y + wa.height/2.0) - (geo.y + geo.height/2.0)) < 2 )
return true
end
end,
-- Check top-right geometry
function(count)
if count == 1 then
cmonth:call_calendar(0, 'tr')
else
local geo = cmonth:geometry()
assert(wa.x + wa.width == geo.x + geo.width)
assert(wa.y == geo.y)
return true
end
end,
-- Check visibility
function()
local v = cyear.visible
cyear:toggle()
assert(v == not cyear.visible)
return true
end,
-- Check current date
function()
local mdate = cmonth:get_widget():get_date()
assert(mdate.day==day and mdate.month==month and mdate.year==year)
local ydate = cyear:get_widget():get_date()
assert(ydate.year==year)
return true
end,
-- Check new date
function(count)
if count == 1 then
-- Increment
cmonth:call_calendar(1)
cyear:call_calendar(-1)
else
local mdate = cmonth:get_widget():get_date()
assert( mdate.day==nil and
((mdate.month==month+1 and mdate.year==year) or (mdate.month==1 and mdate.year==year+1)) )
local ydate = cyear:get_widget():get_date()
assert(ydate.year==year-1)
return true
end
end,
}
runner.run_steps(steps)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80