LibGUI+Calendar: Add new month and year views to Calendar

And overhaul resize and paint events to fix layout edge cases in
which Calendar wasn't filling its parent widget completely. Ensures
month views always display prior month days for click navigation.
Converts Calendar app layout to GML.
This commit is contained in:
thankyouverycool 2021-03-29 20:43:40 -04:00 committed by Andreas Kling
parent effb426757
commit 4465b37897
6 changed files with 865 additions and 420 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

View file

@ -1,5 +1,8 @@
compile_gml(CalendarWindow.gml CalendarWindowGML.h calendar_window_gml)
set(SOURCES
AddEventDialog.cpp
CalendarWindowGML.h
main.cpp
)

View file

@ -0,0 +1,24 @@
@GUI::Widget {
fill_with_background_color: true
layout: @GUI::VerticalBoxLayout {
}
@GUI::ToolBarContainer {
name: "toolbar_container"
@GUI::ToolBar {
name: "toolbar"
}
}
@GUI::Frame {
name: "calendar_frame"
layout: @GUI::VerticalBoxLayout {
margins: [2, 2, 2, 2]
}
@GUI::Calendar {
name: "calendar"
}
}
}

View file

@ -25,7 +25,9 @@
*/
#include "AddEventDialog.h"
#include <Applications/Calendar/CalendarWindowGML.h>
#include <LibGUI/Action.h>
#include <LibGUI/ActionGroup.h>
#include <LibGUI/Application.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Button.h>
@ -36,15 +38,10 @@
#include <LibGUI/ToolBar.h>
#include <LibGUI/ToolBarContainer.h>
#include <LibGUI/Window.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Color.h>
#include <LibGfx/FontDatabase.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio recvfd sendfd rpath accept unix cpath fattr", nullptr) < 0) {
perror("pledge");
return 1;
@ -68,101 +65,92 @@ int main(int argc, char** argv)
auto window = GUI::Window::construct();
window->set_title("Calendar");
window->resize(600, 480);
window->set_minimum_size(171, 141);
window->set_icon(app_icon.bitmap_for_size(16));
auto& root_container = window->set_main_widget<GUI::Widget>();
root_container.set_fill_with_background_color(true);
root_container.set_layout<GUI::VerticalBoxLayout>();
auto& main_widget = window->set_main_widget<GUI::Widget>();
main_widget.load_from_gml(calendar_window_gml);
auto& toolbar_container = root_container.add<GUI::ToolBarContainer>();
auto& toolbar = toolbar_container.add<GUI::ToolBar>();
auto toolbar = main_widget.find_descendant_of_type_named<GUI::ToolBar>("toolbar");
auto calendar = main_widget.find_descendant_of_type_named<GUI::Calendar>("calendar");
auto& calendar_container = root_container.add<GUI::Frame>();
calendar_container.set_layout<GUI::VerticalBoxLayout>();
calendar_container.layout()->set_margins({ 2, 2, 2, 2 });
auto& calendar_widget = calendar_container.add<GUI::Calendar>(Core::DateTime::now());
RefPtr<GUI::Button> selected_calendar_button;
auto prev_date_action = GUI::Action::create("Previous date", { Mod_Alt, Key_Left }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), [&](const GUI::Action&) {
unsigned int target_month = calendar_widget.selected_month();
unsigned int target_year = calendar_widget.selected_year();
if (calendar_widget.mode() == GUI::Calendar::Month) {
target_month--;
if (calendar_widget.selected_month() <= 1) {
target_month = 12;
target_year--;
auto prev_date_action = GUI::Action::create({}, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), [&](const GUI::Action&) {
unsigned view_month = calendar->view_month();
unsigned view_year = calendar->view_year();
if (calendar->mode() == GUI::Calendar::Month) {
view_month--;
if (calendar->view_month() == 1) {
view_month = 12;
view_year--;
}
} else {
target_year--;
view_year--;
}
calendar_widget.update_tiles(target_year, target_month);
selected_calendar_button->set_text(calendar_widget.selected_calendar_text());
calendar->update_tiles(view_year, view_month);
});
auto next_date_action = GUI::Action::create("Next date", { Mod_Alt, Key_Right }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [&](const GUI::Action&) {
unsigned int target_month = calendar_widget.selected_month();
unsigned int target_year = calendar_widget.selected_year();
if (calendar_widget.mode() == GUI::Calendar::Month) {
target_month++;
if (calendar_widget.selected_month() >= 12) {
target_month = 1;
target_year++;
auto next_date_action = GUI::Action::create({}, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [&](const GUI::Action&) {
unsigned view_month = calendar->view_month();
unsigned view_year = calendar->view_year();
if (calendar->mode() == GUI::Calendar::Month) {
view_month++;
if (calendar->view_month() == 12) {
view_month = 1;
view_year++;
}
} else {
target_year++;
view_year++;
}
calendar_widget.update_tiles(target_year, target_month);
selected_calendar_button->set_text(calendar_widget.selected_calendar_text());
calendar->update_tiles(view_year, view_month);
});
auto add_event_action = GUI::Action::create("Add event", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"), [&](const GUI::Action&) {
AddEventDialog::show(calendar_widget.selected_date(), window);
AddEventDialog::show(calendar->selected_date(), window);
});
auto jump_to_action = GUI::Action::create("Jump to today", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"), [&](const GUI::Action&) {
if (calendar_widget.mode() == GUI::Calendar::Year)
calendar_widget.toggle_mode();
calendar_widget.set_selected_date(Core::DateTime::now());
calendar_widget.update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month());
selected_calendar_button->set_text(calendar_widget.selected_calendar_text());
calendar->set_selected_date(Core::DateTime::now());
calendar->update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month());
});
toolbar.add_action(prev_date_action);
selected_calendar_button = toolbar.add<GUI::Button>(calendar_widget.selected_calendar_text());
selected_calendar_button->set_fixed_width(70);
selected_calendar_button->set_button_style(Gfx::ButtonStyle::CoolBar);
selected_calendar_button->set_font(Gfx::FontDatabase::default_bold_fixed_width_font());
selected_calendar_button->on_click = [&](auto) {
calendar_widget.toggle_mode();
selected_calendar_button->set_text(calendar_widget.selected_calendar_text());
};
toolbar.add_action(next_date_action);
toolbar.add_separator();
toolbar.add_action(jump_to_action);
toolbar.add_action(add_event_action);
auto view_month_action = GUI::Action::create_checkable("Month view", { Mod_Ctrl, KeyCode::Key_1 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-month-view.png"), [&](const GUI::Action&) {
if (calendar->mode() == GUI::Calendar::Year)
calendar->toggle_mode();
});
view_month_action->set_checked(true);
calendar_widget.on_calendar_tile_click = [&] {
selected_calendar_button->set_text(calendar_widget.selected_calendar_text());
auto view_year_action = GUI::Action::create_checkable("Year view", { Mod_Ctrl, KeyCode::Key_2 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GUI::Action&) {
if (calendar->mode() == GUI::Calendar::Month)
calendar->toggle_mode();
});
auto view_type_action_group = make<GUI::ActionGroup>();
view_type_action_group->set_exclusive(true);
view_type_action_group->add_action(*view_month_action);
view_type_action_group->add_action(*view_year_action);
toolbar->add_action(prev_date_action);
toolbar->add_action(next_date_action);
toolbar->add_separator();
toolbar->add_action(jump_to_action);
toolbar->add_action(add_event_action);
toolbar->add_separator();
toolbar->add_action(view_month_action);
toolbar->add_action(view_year_action);
calendar->on_tile_doubleclick = [&] {
AddEventDialog::show(calendar->selected_date(), window);
};
calendar_widget.on_calendar_tile_doubleclick = [&] {
AddEventDialog::show(calendar_widget.selected_date(), window);
};
calendar_widget.on_month_tile_click = [&] {
selected_calendar_button->set_text(calendar_widget.selected_calendar_text());
calendar->on_month_click = [&] {
view_month_action->set_checked(true);
};
auto menubar = GUI::MenuBar::construct();
auto& app_menu = menubar->add_menu("File");
app_menu.add_action(GUI::Action::create("Add Event", { Mod_Ctrl | Mod_Shift, Key_E }, Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"),
[&](const GUI::Action&) {
AddEventDialog::show(calendar_widget.selected_date(), window);
AddEventDialog::show(calendar->selected_date(), window);
return;
}));
@ -173,6 +161,10 @@ int main(int argc, char** argv)
return;
}));
auto& view_menu = menubar->add_menu("View");
view_menu.add_action(*view_month_action);
view_menu.add_action(*view_year_action);
auto& help_menu = menubar->add_menu("Help");
help_menu.add_action(GUI::CommonActions::make_about_action("Calendar", app_icon, window));

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com>
* Copyright (c) 2020, the SerenityOS developers.
* Copyright (c) 2020-2021, the SerenityOS developers
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -29,9 +29,7 @@
#include <AK/String.h>
#include <LibCore/DateTime.h>
#include <LibGUI/Button.h>
#include <LibGUI/Frame.h>
#include <LibGUI/Label.h>
#include <LibGUI/Widget.h>
namespace GUI {
@ -45,97 +43,108 @@ public:
Year
};
enum {
ShortNames,
LongNames
enum Format {
ShortMonthYear,
LongMonthYear,
MonthOnly,
YearOnly
};
Calendar(Core::DateTime);
Calendar(Core::DateTime date_time = Core::DateTime::now(), Mode mode = Month);
virtual ~Calendar() override;
unsigned int selected_year() const { return m_selected_year; }
unsigned int selected_month() const { return m_selected_month; }
const String selected_calendar_text(bool long_names = ShortNames);
void update_tiles(unsigned int target_year, unsigned int target_month);
void set_selected_calendar(unsigned int year, unsigned int month);
void set_selected_date(Core::DateTime date_time) { m_selected_date = date_time; }
Core::DateTime selected_date() const { return m_selected_date; }
void toggle_mode();
void set_grid(bool grid);
bool has_grid() { return m_grid; }
Mode mode() const { return m_mode; }
Function<void()> on_calendar_tile_click;
Function<void()> on_calendar_tile_doubleclick;
Function<void()> on_month_tile_click;
void set_view_date(unsigned year, unsigned month)
{
m_view_year = year;
m_view_month = month;
}
unsigned view_year() const { return m_view_year; }
unsigned view_month() const { return m_view_month; }
String formatted_date(Format format = LongMonthYear);
Mode mode() const { return m_mode; }
void toggle_mode();
void update_tiles(unsigned year, unsigned month);
void set_grid(bool);
bool has_grid() const { return m_grid; }
void set_show_year(bool b) { m_show_year = b; }
bool is_showing_year() const { return m_show_year; }
void set_show_month_and_year(bool b) { m_show_month_year = b; }
bool is_showing_month_and_year() const { return m_show_month_year; }
void set_show_days_of_the_week(bool b) { m_show_days = b; }
bool is_showing_days_of_the_week() const { return m_show_days; }
Gfx::IntSize unadjusted_tile_size() const { return m_unadjusted_tile_size; }
void set_unadjusted_tile_size(int width, int height)
{
m_unadjusted_tile_size.set_width(width);
m_unadjusted_tile_size.set_height(height);
}
Function<void()> on_tile_click;
Function<void()> on_tile_doubleclick;
Function<void()> on_month_click;
private:
virtual void resize_event(GUI::ResizeEvent&) override;
virtual void paint_event(GUI::PaintEvent&) override;
virtual void mousemove_event(GUI::MouseEvent&) override;
virtual void mousedown_event(MouseEvent&) override;
virtual void mouseup_event(MouseEvent&) override;
virtual void doubleclick_event(MouseEvent&);
virtual void leave_event(Core::Event&) override;
class CalendarTile final : public GUI::Frame {
C_OBJECT(CalendarTile)
public:
CalendarTile(int index, Core::DateTime m_date_time);
void update_values(int index, Core::DateTime date_time);
virtual ~CalendarTile() override;
bool is_today() const;
bool is_hovered() const { return m_hovered; }
bool is_selected() const { return m_selected; }
void set_selected(bool b) { m_selected = b; }
bool is_outside_selection() const { return m_outside_selection; }
void set_outside_selection(bool b) { m_outside_selection = b; }
bool has_grid() const { return m_grid; }
void set_grid(bool b) { m_grid = b; }
Core::DateTime get_date_time() { return m_date_time; }
Function<void(int index)> on_doubleclick;
Function<void(int index)> on_click;
private:
virtual void doubleclick_event(GUI::MouseEvent&) override;
virtual void mousedown_event(GUI::MouseEvent&) override;
virtual void enter_event(Core::Event&) override;
virtual void leave_event(Core::Event&) override;
virtual void paint_event(GUI::PaintEvent&) override;
int m_index { 0 };
bool m_outside_selection { false };
bool m_hovered { false };
bool m_selected { false };
bool m_grid { true };
String m_display_date;
Core::DateTime m_date_time;
struct Day {
String name;
int width { 0 };
int height { 16 };
};
Vector<Day> m_days;
class MonthTile final : public GUI::Button {
C_OBJECT(MonthTile)
public:
MonthTile(int index, Core::DateTime m_date_time);
virtual ~MonthTile() override;
void update_values(Core::DateTime date_time) { m_date_time = date_time; }
Core::DateTime get_date_time() { return m_date_time; }
Function<void(int index)> on_indexed_click;
private:
virtual void mouseup_event(GUI::MouseEvent&) override;
int m_index { 0 };
Core::DateTime m_date_time;
struct MonthTile {
String name;
Gfx::IntRect rect;
int width { 0 };
int height { 0 };
bool is_hovered { false };
bool is_being_pressed { false };
};
Vector<MonthTile> m_months;
RefPtr<MonthTile> m_month_tiles[12];
RefPtr<CalendarTile> m_calendar_tiles[42];
RefPtr<GUI::Label> m_day_names[7];
RefPtr<GUI::Widget> m_week_rows[6];
RefPtr<GUI::Widget> m_month_rows[3];
RefPtr<GUI::Widget> m_month_tile_container;
RefPtr<GUI::Widget> m_calendar_tile_container;
RefPtr<GUI::Widget> m_day_name_container;
struct Tile {
Core::DateTime date_time;
Gfx::IntRect rect;
int width { 0 };
int height { 0 };
bool is_today { false };
bool is_selected { false };
bool is_hovered { false };
bool is_outside_selected_month { false };
};
Vector<Tile> m_tiles[12];
bool m_grid { true };
bool m_show_month_year { true };
bool m_show_days { true };
bool m_show_year { false };
bool m_show_month_tiles { false };
int m_currently_pressed_index { -1 };
unsigned m_view_year;
unsigned m_view_month;
Core::DateTime m_selected_date;
Core::DateTime m_previous_selected_date;
unsigned int m_selected_year { 0 };
unsigned int m_selected_month { 0 };
bool m_grid { true };
Gfx::IntSize m_unadjusted_tile_size;
Gfx::IntSize m_event_size;
Gfx::IntSize m_month_size[12];
Mode m_mode { Month };
};