Skip to content

CalendarView

Header: winui/ui/datetime/CalendarView.hpp. Spec: winui/ui/datetime/CalendarView.hpp.

A month calendar: the days of one month in a grid of weeks, with the month's name above them and buttons to page through months.

Clicking a day selects it, and clicking a selected day removes it from the selection. Clicking the heading zooms out to the months of the year and then to the years of the decade, and clicking a month or a year zooms back in. The days of the neighboring months fill the first and last weeks, in a dimmed color when out_of_scope_enabled is true.

The names of the months and the days and the first day of the week come from regional_date_format() unless the calendar sets its own.

Each theme era draws its own month calendar: - Fluent 2 is 300 by 350 DIPs with round cells, today a filled accent circle and a selected day an accent ring. - Fluent and MDL2 are 296 by 332 with Windows 10's square cells: today a filled accent square, a selected day a 2 DIP accent border. - Aero and Metro are 225 by 160, the Vista design: a light blue box around the selected day, a blue outline around today, and a "Today:" line under the grid that goes to today. - Windows 9x and Luna are comctl32's month calendar: a title band in the caption color with the scroll bar's arrow buttons, the selected day a filled ellipse, today circled in red, and the "Today:" line. Clicking the title opens a menu of the months, Ctrl+PageUp and Ctrl+PageDown move by a year, and there is no hover. - Windows 3.x and 1.x/2.x had no month calendar and draw the classic one from their own parts.

Keyboard: the arrow keys move between days, Home and End go to the first and the last day of the month, PageUp and PageDown move by a month, Ctrl+Up zooms out and Ctrl+Down zooms in, and Enter or Space select the focused day or zoom into the focused month or year. The heading, the two paging buttons and the grid are four tab stops.

ui::CalendarView is the declarative form.

Example:

ui::CalendarViewControl calendar;
calendar.set_selection_mode(ui::CalendarViewSelectionMode::Multiple);
calendar.on_selected_dates_changed([](const std::vector<std::chrono::year_month_day>& dates)
                                   { std::printf("%zu days picked\n", dates.size()); });

ui::CalendarView

The declarative form of CalendarViewControl: a month calendar.

Example:

ui::Property<std::vector<std::chrono::year_month_day>> picked;

window.set_content(ui::CalendarView{
    .selected_dates      = picked,
    .group_label_visible = true,
    .is_blackout         = [](std::chrono::year_month_day date)
    {
        const std::chrono::weekday day{std::chrono::sys_days{date}};
        return day == std::chrono::Saturday || day == std::chrono::Sunday; // weekdays only
    },
});

Every field, in the order a designated initializer names them:

ui::CalendarView{
    .selected_dates, .min_date, .max_date, .selection_mode, .display_mode, .display_date,
    .first_day_of_week, .today_highlighted, .out_of_scope_enabled, .group_label_visible,
    .style, .margin, .width, .height, .min_width, .min_height, .max_width, .max_height,
    .horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility,
    .on_selected_dates_changed, .on_display_mode_changed, .on_pointer_entered,
    .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down, .on_wheel, .control,
    .is_blackout
}

Fields, in declaration order (designators must follow it):

Field Type Default
selected_dates ui::TwoWayField<std::vector<std::chrono::year_month_day>> The selected dates (CalendarViewControl::set_selected_dates()).
min_date ui::Binding<std::optional<std::chrono::year_month_day>> The first date shown; unset is January 1, 100 years before today.
max_date ui::Binding<std::optional<std::chrono::year_month_day>> The last date shown; unset is December 31, 100 years after today.
selection_mode ui::Binding<ui::CalendarViewSelectionMode> {ui::CalendarViewSelectionMode::Single} How many dates can be selected.
display_mode ui::TwoWayField<ui::CalendarViewDisplayMode> {ui::CalendarViewDisplayMode::Month} Month, Year or Decade.
display_date ui::Binding<std::optional<std::chrono::year_month_day>> The month (or year, or decade) shown; unset is today's.
first_day_of_week ui::Binding<std::optional<std::chrono::weekday>> The day the weeks start on; unset follows regional_date_format().
today_highlighted ui::Binding<bool> {true} Whether today is drawn as a filled circle.
out_of_scope_enabled ui::Binding<bool> {true} Whether the days of the neighboring months are dimmed.
group_label_visible ui::Binding<bool> {false} Whether the first day of each month carries the month's name.
on_selected_dates_changed ui::Callback<void(const std::vector<std::chrono::year_month_day>&)> The new selection, from input or a bound value.
on_display_mode_changed ui::Callback<void(ui::CalendarViewDisplayMode)> The new display mode, from input or a bound value.
on_pointer_entered ui::Action The pointer moved onto the control.
on_pointer_exited ui::Action The pointer left the control.
on_got_focus ui::Action Keyboard focus arrived on the control, or on anything inside it, from outside it.
on_lost_focus ui::Action Keyboard focus left the control and everything inside it.
on_key_down ui::Callback<ui::Handling(const ui::KeyEvent&)> A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()).
on_wheel ui::Callback<ui::Handling(const ui::PointerEvent&)> A wheel turn over the control, before the control pages; ui::Handled stops it there (Element::on_wheel()).
control ui::ControlOut<ui::CalendarView> Out: the handle that receives the built control.
is_blackout ui::Callback<bool(std::chrono::year_month_day)> Marks dates that can't be selected; unset blocks none.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, enabled, accessibility every spec carries (conventions).

build

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::CalendarViewControl; the caller owns it.

ui::CalendarViewControl

Derives from ui::Element, ui::GridProvider, ui::TableProvider, ui::SelectionProvider, ui::ValueProvider.

A month calendar: the days of one month in a grid of weeks, with the month's name above them and buttons to page through months.

Clicking a day selects it, and clicking a selected day removes it from the selection. Clicking the heading zooms out to the months of the year and then to the years of the decade, and clicking a month or a year zooms back in. The days of the neighboring months fill the first and last weeks, in a dimmed color when out_of_scope_enabled is true.

The names of the months and the days and the first day of the week come from regional_date_format() unless the calendar sets its own.

Each theme era draws its own month calendar: - Fluent 2 is 300 by 350 DIPs with round cells, today a filled accent circle and a selected day an accent ring. - Fluent and MDL2 are 296 by 332 with Windows 10's square cells: today a filled accent square, a selected day a 2 DIP accent border. - Aero and Metro are 225 by 160, the Vista design: a light blue box around the selected day, a blue outline around today, and a "Today:" line under the grid that goes to today. - Windows 9x and Luna are comctl32's month calendar: a title band in the caption color with the scroll bar's arrow buttons, the selected day a filled ellipse, today circled in red, and the "Today:" line. Clicking the title opens a menu of the months, Ctrl+PageUp and Ctrl+PageDown move by a year, and there is no hover. - Windows 3.x and 1.x/2.x had no month calendar and draw the classic one from their own parts.

Keyboard: the arrow keys move between days, Home and End go to the first and the last day of the month, PageUp and PageDown move by a month, Ctrl+Up zooms out and Ctrl+Down zooms in, and Enter or Space select the focused day or zoom into the focused month or year. The heading, the two paging buttons and the grid are four tab stops.

ui::CalendarView is the declarative form.

Example:

ui::CalendarViewControl calendar;
calendar.set_selection_mode(ui::CalendarViewSelectionMode::Multiple);
calendar.on_selected_dates_changed([](const std::vector<std::chrono::year_month_day>& dates)
                                   { std::printf("%zu days picked\n", dates.size()); });

CalendarViewControl

CalendarViewControl()

A calendar on today's month, in single selection, with nothing selected.

~CalendarViewControl

~CalendarViewControl() override

Destroys the parts.

CalendarViewControl

CalendarViewControl(const CalendarViewControl&) = delete

operator=

CalendarViewControl& operator=(const CalendarViewControl&) = delete

selected_dates

const std::vector<std::chrono::year_month_day>& selected_dates() const

The selected dates, in the order they were selected.

set_selected_dates

void set_selected_dates(std::vector<std::chrono::year_month_day> dates)

Replaces the selection. Dates outside [min_date(), max_date()], blackout dates and repeats are dropped, and single selection keeps the first. Raises the change callback when the selection differs.

on_selected_dates_changed

void on_selected_dates_changed(Callback<void(const std::vector<std::chrono::year_month_day>&)> handler)

Called after the selection changed, from input or a setter, with the new selection.

selection_mode

CalendarViewSelectionMode selection_mode() const

How many dates can be selected. Switching to None clears the selection and to Single keeps the first date.

set_selection_mode

void set_selection_mode(CalendarViewSelectionMode mode)

See selection_mode().

min_date

std::chrono::year_month_day min_date() const

The first date the calendar shows: unset is January 1, 100 years before today.

max_date

std::chrono::year_month_day max_date() const

The last date the calendar shows: unset is December 31, 100 years after today.

set_date_range

void set_date_range(std::optional<std::chrono::year_month_day> min, std::optional<std::chrono::year_month_day> max)

Sets the range; unset follows today. A selected date outside it is dropped.

display_mode

CalendarViewDisplayMode display_mode() const

Month, Year or Decade.

set_display_mode

void set_display_mode(CalendarViewDisplayMode mode)

Zooms to mode, animated where the theme animates.

on_display_mode_changed

void on_display_mode_changed(Callback<void(CalendarViewDisplayMode)> handler)

Called after the display mode changed, from input or a setter.

display_date

std::chrono::year_month_day display_date() const

A date in the month (or year, or decade) the calendar shows.

set_display_date

void set_display_date(std::chrono::year_month_day date)

Shows the month, year or decade date is in, clamped to the range.

first_day_of_week

std::chrono::weekday first_day_of_week() const

The day the weeks start on; unset follows regional_date_format().

set_first_day_of_week

void set_first_day_of_week(std::optional<std::chrono::weekday> day)

See first_day_of_week().

today_highlighted

bool today_highlighted() const

Whether today is drawn as a filled circle. True by default.

set_today_highlighted

void set_today_highlighted(bool highlighted)

See today_highlighted().

out_of_scope_enabled

bool out_of_scope_enabled() const

Whether the days of the neighboring months are dimmed. True by default.

set_out_of_scope_enabled

void set_out_of_scope_enabled(bool enabled)

See out_of_scope_enabled().

group_label_visible

bool group_label_visible() const

Whether the first day of each month carries the month's abbreviated name above its number (and January, in the year view, the year). False by default.

set_group_label_visible

void set_group_label_visible(bool visible)

See group_label_visible().

set_blackout

void set_blackout(Callback<bool(std::chrono::year_month_day)> is_blackout)

Marks dates that can't be selected: they are drawn struck through and skipped by the selection. Unset blocks none.

is_blackout

bool is_blackout(std::chrono::year_month_day date) const

Whether date is a blackout date.

set_today

void set_today(std::optional<std::chrono::year_month_day> day)

The day drawn as today: unset (the default) is ui::today(). A test or a demo pins it.

today

std::chrono::year_month_day today() const

The day drawn as today.

focused_date

std::chrono::year_month_day focused_date() const

The day the keyboard is on in the month view: the selected day, or today, until an arrow key moves it.

focusable

bool focusable() const override

Whether this element can take keyboard focus (a leaf control opts in).

automation_role

AutomationRole automation_role() const override

What screen readers and other assistive tools see of this node: the role, name, help text and children here, and what a client can do with the node through the providers automation_providers() lists (ui/Automation.hpp).

automation_role() is the role this node reports; Group by default.

focus_ring_rect

Rect focus_ring_rect(const Theme& theme) const override

What the ring goes around, in window DIPs: bounds() by default. A control whose ring goes around one part of it (a slider's thumb, a list's current row) returns that part. theme is the one the element is drawn with, for a ring placed differently in each era.

focus_ring_corner_radius

float focus_ring_corner_radius(const Theme& theme) const override

The ring's corner radius: the theme's control radius by default.

focus_ring_inside

bool focus_ring_inside() const override

Whether the ring goes inside focus_ring_rect() in every era, its outer edge on the rect. False by default, which leaves it to the theme. An item inside a viewport (a list row, a tab, a menu item) returns true, as does a control in a tight spot, so a neighbor or the viewport edge never cuts its ring.

for_each_child

void for_each_child(ChildVisitor visit) const override

Call visit once for each child this element owns, in order (back to front where they overlap). The one override a container needs beyond measure, arrange and render. The default of every tree-wide walk recurses through it (collect_focusables(), the popup walk, clear_dirty(), build_visual_tree(), sync_composition(), sync_native(), tick() and automation_children()), so a custom container that lists its children here gets focus, popups, dirty clearing, ticks, composition, native controls and automation right without forwarding any of them. Default: no children, a leaf.

List the children the walks should reach, normally every one this element owns. A walk that must see a different set overrides that walk alone: a composite that is one tab stop and one automation node, a split view leaving its closed pane out of focus and popups. A control that parks parts (an unselected page, a hidden button) may leave them out here instead and clear them in clear_dirty_override() (and tick them in tick_override()), so a parked part never stays dirty.

Const so the const walks can use it, though it hands out mutable children: they are owned objects of their own, not part of this element's state. A tick can run a handler that adds or removes children, so a container that allows that walks its list by index.

Example:

class Row : public ui::Element
{
    void for_each_child(ui::ChildVisitor visit) const override
    {
        for (const auto& child : children_)
        {
            visit(*child);
        }
    }

    std::vector<std::unique_ptr<ui::Element>> children_;
};

collect_focusables

void collect_focusables(std::vector<Element*>& out) override

The heading, the two paging buttons, then the grid, as four tab stops.

automation_children

void automation_children(std::vector<Element*>& out) const override

The children an automation client sees under this node, immediate children only. A client walks the tree one level at a time, and the parent comes from parent(). Defaults to every child for_each_child() lists. Override it only to show a client something else: the children in a different order, or without parts that are only decoration. A composite that is one automation node lists nothing.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Implement these instead of measure() and arrange(). The base class has already applied margin, size limits and alignment by the time these run. measure_override() is pure (every concrete Element must size itself); arrange_override() defaults to a no-op since most leaves need nothing beyond having bounds() set, which the base already did.

arrange_override

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Position children within the rect arrange() settled on (also in bounds(), window DIPs). Default: nothing, for a leaf.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

What the control draws, in layout coordinates; render() has already applied the visual layer. A container draws its children through their render(), never their render_override().

pointer_override

bool pointer_override(const PointerEvent& e) override

A pointer event for this element: the element under the pointer, one inside it that did not handle the event, or the element holding the capture. The position is in layout coordinates (handle_pointer()). Return true when the event did something here, and it goes no further up; a handled press also captures the pointer until its release. A control that acts on a press (a click, a toggle, a caret placed, a drag started) returns true for the press and its release, so a list row or a card around it does not act on the same click. Default: false.

key_override

bool key_override(const KeyEvent& e) override

A key for this element: the focused one, or one inside it that did not handle it. Return true when the key did something here, and it goes no further; false lets it bubble on to the parent and then to the shortcuts, a dialog's default button and arrow-key focus moves. Default: false.

tick_override

void tick_override(Duration delta) override

This element's own part of tick(): a clock of its own (ToolTip's hover delay, an animation's phase, a caret blink). Runs after the visual layer has advanced and before the children are ticked, which tick() does by itself. Tick only parts that for_each_child() does not list here. Default: nothing.

enabled_changed

void enabled_changed() override

is_enabled() flipped, through set_enabled() here or on an ancestor, or a move to another parent. Drop any press or hover state. The element is redrawn either way. No-op by default.

Enumerations

CalendarViewSelectionMode

enum class CalendarViewSelectionMode

How many dates a CalendarView lets the user select.

Value
None Days can't be selected; the calendar only shows dates.
Single One date at a time: a click replaces the selection (the default).
Multiple Any number of dates: a click adds or removes one.

CalendarViewDisplayMode

enum class CalendarViewDisplayMode

What a CalendarView shows.

Value
Month The days of one month, six weeks at a time (the default).
Year The months of one year.
Decade The years of one decade.

Tests

  • tests/test_calendar_view.cpp