mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +00:00
Calendar: Implement saving, loading, and displaying of calendars
The user can now save, load, and view calendars. A calendar is made up of an array of events which are saved in a JSON file. In the future we should implement the iCalendar standard instead of using a custom format.
This commit is contained in:
parent
db3e1b128c
commit
1b5b1e4153
Notes:
sideshowbarker
2024-07-17 09:56:35 +09:00
Author: https://github.com/monroeclinton Commit: https://github.com/SerenityOS/serenity/commit/1b5b1e4153 Pull-request: https://github.com/SerenityOS/serenity/pull/18841 Reviewed-by: https://github.com/ADKaster ✅ Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/caoimhebyrne ✅ Reviewed-by: https://github.com/gmta
17 changed files with 696 additions and 176 deletions
|
@ -30,6 +30,7 @@ ruby=*.rb
|
|||
shell=*.sh,*.bash,*.zsh
|
||||
sound=*.wav,*.flac,*.mp3,*.qoa
|
||||
spreadsheet=*.sheets,*.csv
|
||||
calendar=*.cal
|
||||
text=*.txt
|
||||
truetype=*.ttf
|
||||
pixelpaint=*.pp
|
||||
|
|
|
@ -2,3 +2,6 @@
|
|||
Name=Calendar
|
||||
Executable=/bin/Calendar
|
||||
Category=Office
|
||||
|
||||
[Launcher]
|
||||
FileTypes=cal
|
||||
|
|
BIN
Base/res/icons/16x16/filetype-calendar.png
Normal file
BIN
Base/res/icons/16x16/filetype-calendar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 B |
BIN
Base/res/icons/32x32/filetype-calendar.png
Normal file
BIN
Base/res/icons/32x32/filetype-calendar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 257 B |
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
* Copyright (c) 2022-2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -19,9 +19,12 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
|
||||
AddEventDialog::AddEventDialog(Core::DateTime date_time, Window* parent_window)
|
||||
namespace Calendar {
|
||||
|
||||
AddEventDialog::AddEventDialog(Core::DateTime date_time, EventManager& event_manager, Window* parent_window)
|
||||
: Dialog(parent_window)
|
||||
, m_date_time(date_time)
|
||||
, m_event_manager(event_manager)
|
||||
{
|
||||
resize(158, 130);
|
||||
set_title("Add Event");
|
||||
|
@ -42,6 +45,7 @@ AddEventDialog::AddEventDialog(Core::DateTime date_time, Window* parent_window)
|
|||
add_label.set_font(Gfx::FontDatabase::default_font().bold_variant());
|
||||
|
||||
auto& event_title_textbox = top_container.add<GUI::TextBox>();
|
||||
event_title_textbox.set_name("event_title_textbox");
|
||||
event_title_textbox.set_fixed_height(20);
|
||||
|
||||
auto& middle_container = widget->add<GUI::Widget>();
|
||||
|
@ -92,8 +96,9 @@ AddEventDialog::AddEventDialog(Core::DateTime date_time, Window* parent_window)
|
|||
button_container.add_spacer().release_value_but_fixme_should_propagate_errors();
|
||||
auto& ok_button = button_container.add<GUI::Button>("OK"_short_string);
|
||||
ok_button.set_fixed_size(80, 20);
|
||||
ok_button.on_click = [this](auto) {
|
||||
dbgln("TODO: Add event icon on specific tile");
|
||||
ok_button.on_click = [&](auto) {
|
||||
add_event_to_calendar().release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
done(ExecResult::OK);
|
||||
};
|
||||
|
||||
|
@ -110,6 +115,19 @@ AddEventDialog::AddEventDialog(Core::DateTime date_time, Window* parent_window)
|
|||
event_title_textbox.set_focus(true);
|
||||
}
|
||||
|
||||
ErrorOr<void> AddEventDialog::add_event_to_calendar()
|
||||
{
|
||||
JsonObject event;
|
||||
auto start_date = TRY(String::formatted("{}-{:0>2d}-{:0>2d}", m_date_time.year(), m_date_time.month(), m_date_time.day()));
|
||||
auto summary = find_descendant_of_type_named<GUI::TextBox>("event_title_textbox")->get_text();
|
||||
event.set("start_date", JsonValue(start_date));
|
||||
event.set("summary", JsonValue(summary));
|
||||
TRY(m_event_manager.add_event(event));
|
||||
m_event_manager.set_dirty(true);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int AddEventDialog::MonthListModel::row_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return 12;
|
||||
|
@ -176,3 +194,5 @@ GUI::Variant AddEventDialog::MeridiemListModel::data(const GUI::ModelIndex& inde
|
|||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,30 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
* Copyright (c) 2022-2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "EventManager.h"
|
||||
#include <LibGUI/Calendar.h>
|
||||
#include <LibGUI/Dialog.h>
|
||||
#include <LibGUI/Model.h>
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
namespace Calendar {
|
||||
|
||||
class AddEventDialog final : public GUI::Dialog {
|
||||
C_OBJECT(AddEventDialog)
|
||||
public:
|
||||
virtual ~AddEventDialog() override = default;
|
||||
|
||||
static void show(Core::DateTime date_time, Window* parent_window = nullptr)
|
||||
static void show(Core::DateTime date_time, EventManager& event_manager, Window* parent_window = nullptr)
|
||||
{
|
||||
auto dialog = AddEventDialog::construct(date_time, parent_window);
|
||||
auto dialog = AddEventDialog::construct(date_time, event_manager, parent_window);
|
||||
dialog->exec();
|
||||
}
|
||||
|
||||
private:
|
||||
AddEventDialog(Core::DateTime date_time, Window* parent_window = nullptr);
|
||||
AddEventDialog(Core::DateTime date_time, EventManager& event_manager, Window* parent_window = nullptr);
|
||||
|
||||
ErrorOr<void> add_event_to_calendar();
|
||||
|
||||
class MonthListModel final : public GUI::Model {
|
||||
public:
|
||||
|
@ -65,4 +70,7 @@ private:
|
|||
};
|
||||
|
||||
Core::DateTime m_date_time;
|
||||
EventManager& m_event_manager;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ compile_gml(CalendarWindow.gml CalendarWindowGML.h calendar_window_gml)
|
|||
|
||||
set(SOURCES
|
||||
AddEventDialog.cpp
|
||||
CalendarWidget.cpp
|
||||
EventCalendar.cpp
|
||||
EventManager.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
|
@ -15,4 +18,4 @@ set(GENERATED_SOURCES
|
|||
)
|
||||
|
||||
serenity_app(Calendar ICON app-calendar)
|
||||
target_link_libraries(Calendar PRIVATE LibConfig LibCore LibGfx LibGUI LibMain)
|
||||
target_link_libraries(Calendar PRIVATE LibConfig LibCore LibFileSystem LibFileSystemAccessClient LibGfx LibGUI LibMain)
|
||||
|
|
275
Userland/Applications/Calendar/CalendarWidget.cpp
Normal file
275
Userland/Applications/Calendar/CalendarWidget.cpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "CalendarWidget.h"
|
||||
#include "AddEventDialog.h"
|
||||
#include <AK/JsonParser.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <Applications/Calendar/CalendarWindowGML.h>
|
||||
#include <LibConfig/Client.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Calendar.h>
|
||||
#include <LibGUI/Icon.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Menubar.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
#include <LibGUI/Process.h>
|
||||
#include <LibGUI/Toolbar.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
||||
namespace Calendar {
|
||||
|
||||
ErrorOr<NonnullRefPtr<CalendarWidget>> CalendarWidget::create(GUI::Window* parent_window)
|
||||
{
|
||||
auto widget = TRY(AK::adopt_nonnull_ref_or_enomem(new (nothrow) CalendarWidget));
|
||||
TRY(widget->load_from_gml(calendar_window_gml));
|
||||
|
||||
widget->m_event_calendar = widget->find_descendant_of_type_named<EventCalendar>("calendar");
|
||||
widget->create_on_events_change();
|
||||
|
||||
auto toolbar = widget->find_descendant_of_type_named<GUI::Toolbar>("toolbar");
|
||||
auto calendar = widget->m_event_calendar;
|
||||
|
||||
auto prev_date_action = TRY(widget->create_prev_date_action());
|
||||
auto next_date_action = TRY(widget->create_next_date_action());
|
||||
|
||||
auto add_event_action = TRY(widget->create_add_event_action());
|
||||
|
||||
auto jump_to_action = TRY(widget->create_jump_to_action());
|
||||
|
||||
auto view_month_action = TRY(widget->create_view_month_action());
|
||||
view_month_action->set_checked(true);
|
||||
|
||||
auto view_year_action = TRY(widget->create_view_year_action());
|
||||
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);
|
||||
auto default_view = Config::read_string("Calendar"sv, "View"sv, "DefaultView"sv, "Month"sv);
|
||||
if (default_view == "Year")
|
||||
view_year_action->set_checked(true);
|
||||
|
||||
auto open_settings_action = TRY(widget->create_open_settings_action());
|
||||
|
||||
(void)TRY(toolbar->try_add_action(prev_date_action));
|
||||
(void)TRY(toolbar->try_add_action(next_date_action));
|
||||
TRY(toolbar->try_add_separator());
|
||||
(void)TRY(toolbar->try_add_action(jump_to_action));
|
||||
(void)TRY(toolbar->try_add_action(add_event_action));
|
||||
TRY(toolbar->try_add_separator());
|
||||
(void)TRY(toolbar->try_add_action(view_month_action));
|
||||
(void)TRY(toolbar->try_add_action(view_year_action));
|
||||
(void)TRY(toolbar->try_add_action(open_settings_action));
|
||||
|
||||
widget->create_on_tile_doubleclick();
|
||||
|
||||
calendar->on_month_click = [&] {
|
||||
view_month_action->set_checked(true);
|
||||
};
|
||||
|
||||
auto new_calendar_action = TRY(widget->create_new_calendar_action());
|
||||
auto open_calendar_action = widget->create_open_calendar_action();
|
||||
|
||||
auto save_as_action = widget->create_save_as_action();
|
||||
auto save_action = widget->create_save_action(save_as_action);
|
||||
|
||||
auto& file_menu = parent_window->add_menu("&File"_short_string);
|
||||
file_menu.add_action(open_settings_action);
|
||||
file_menu.add_action(new_calendar_action);
|
||||
file_menu.add_action(open_calendar_action);
|
||||
file_menu.add_action(save_as_action);
|
||||
file_menu.add_action(save_action);
|
||||
|
||||
TRY(file_menu.try_add_separator());
|
||||
|
||||
TRY(file_menu.try_add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
||||
GUI::Application::the()->quit();
|
||||
})));
|
||||
|
||||
auto& event_menu = parent_window->add_menu("&Event"_short_string);
|
||||
event_menu.add_action(add_event_action);
|
||||
|
||||
auto view_menu = TRY(parent_window->try_add_menu("&View"_short_string));
|
||||
TRY(view_menu->try_add_action(*view_month_action));
|
||||
TRY(view_menu->try_add_action(*view_year_action));
|
||||
|
||||
auto help_menu = TRY(parent_window->try_add_menu("&Help"_short_string));
|
||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(parent_window)));
|
||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Calendar", TRY(GUI::Icon::try_create_default_icon("app-calendar"sv)), parent_window)));
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
void CalendarWidget::create_on_events_change()
|
||||
{
|
||||
m_event_calendar->event_manager().on_events_change = [&]() {
|
||||
m_event_calendar->repaint();
|
||||
window()->set_modified(true);
|
||||
update_window_title();
|
||||
};
|
||||
}
|
||||
|
||||
void CalendarWidget::load_file(FileSystemAccessClient::File file)
|
||||
{
|
||||
auto result = m_event_calendar->event_manager().load_file(file);
|
||||
if (result.is_error()) {
|
||||
GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Cannot load file: {}", result.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
window()->set_modified(false);
|
||||
update_window_title();
|
||||
}
|
||||
|
||||
NonnullRefPtr<GUI::Action> CalendarWidget::create_save_action(GUI::Action& save_as_action)
|
||||
{
|
||||
return GUI::CommonActions::make_save_action([&](auto&) {
|
||||
if (current_filename().is_empty()) {
|
||||
save_as_action.activate();
|
||||
return;
|
||||
}
|
||||
|
||||
auto response = FileSystemAccessClient::Client::the().request_file(window(), current_filename().to_deprecated_string(), Core::File::OpenMode::Write);
|
||||
if (response.is_error())
|
||||
return;
|
||||
|
||||
auto result = m_event_calendar->event_manager().save(response.value());
|
||||
if (result.is_error()) {
|
||||
GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Cannot save file: {}", result.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
window()->set_modified(false);
|
||||
update_window_title();
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<GUI::Action> CalendarWidget::create_save_as_action()
|
||||
{
|
||||
return GUI::CommonActions::make_save_as_action([&](auto&) {
|
||||
auto response = FileSystemAccessClient::Client::the().save_file(window(), "calendar", "cal");
|
||||
if (response.is_error())
|
||||
return;
|
||||
|
||||
auto result = m_event_calendar->event_manager().save(response.value());
|
||||
if (result.is_error()) {
|
||||
GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Cannot save file: {}", result.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
window()->set_modified(false);
|
||||
update_window_title();
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_new_calendar_action()
|
||||
{
|
||||
return GUI::Action::create("&New Calendar", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-calendar.png"sv)), [&](const GUI::Action&) {
|
||||
auto response = FileSystemAccessClient::Client::the().save_file(window(), "calendar", "cal", Core::File::OpenMode::Write);
|
||||
|
||||
if (response.is_error())
|
||||
return;
|
||||
|
||||
m_event_calendar->event_manager().clear();
|
||||
|
||||
auto result = m_event_calendar->event_manager().save(response.value());
|
||||
if (result.is_error()) {
|
||||
GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Cannot save file: {}", result.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
update_window_title();
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<GUI::Action> CalendarWidget::create_open_calendar_action()
|
||||
{
|
||||
return GUI::CommonActions::make_open_action([&](auto&) {
|
||||
auto response = FileSystemAccessClient::Client::the().open_file(window());
|
||||
if (response.is_error())
|
||||
return;
|
||||
(void)load_file(response.release_value());
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_prev_date_action()
|
||||
{
|
||||
return GUI::Action::create({}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"sv)), [&](const GUI::Action&) {
|
||||
m_event_calendar->show_previous_date();
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_next_date_action()
|
||||
{
|
||||
return GUI::Action::create({}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv)), [&](const GUI::Action&) {
|
||||
m_event_calendar->show_next_date();
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarWidget::update_window_title()
|
||||
{
|
||||
StringBuilder builder;
|
||||
if (current_filename().is_empty())
|
||||
builder.append("Untitled"sv);
|
||||
else
|
||||
builder.append(current_filename());
|
||||
builder.append("[*] - Calendar"sv);
|
||||
|
||||
window()->set_title(builder.to_deprecated_string());
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_add_event_action()
|
||||
{
|
||||
return GUI::Action::create("&Add Event", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"sv)), [&](const GUI::Action&) {
|
||||
AddEventDialog::show(m_event_calendar->selected_date(), m_event_calendar->event_manager(), window());
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_jump_to_action()
|
||||
{
|
||||
return GUI::Action::create("Jump to &Today", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"sv)), [&](const GUI::Action&) {
|
||||
m_event_calendar->set_selected_date(Core::DateTime::now());
|
||||
m_event_calendar->update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month());
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_view_month_action()
|
||||
{
|
||||
return GUI::Action::create_checkable("&Month View", { Mod_Ctrl, KeyCode::Key_1 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-month-view.png"sv)), [&](const GUI::Action&) {
|
||||
if (m_event_calendar->mode() == GUI::Calendar::Year)
|
||||
m_event_calendar->toggle_mode();
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_view_year_action()
|
||||
{
|
||||
return GUI::Action::create_checkable("&Year View", { Mod_Ctrl, KeyCode::Key_2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"sv)), [&](const GUI::Action&) {
|
||||
if (m_event_calendar->mode() == GUI::Calendar::Month)
|
||||
m_event_calendar->toggle_mode();
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> CalendarWidget::create_open_settings_action()
|
||||
{
|
||||
return GUI::Action::create("Calendar &Settings", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-settings.png"sv)), [&](GUI::Action const&) {
|
||||
GUI::Process::spawn_or_show_error(window(), "/bin/CalendarSettings"sv);
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarWidget::create_on_tile_doubleclick()
|
||||
{
|
||||
m_event_calendar->on_tile_doubleclick = [&] {
|
||||
AddEventDialog::show(m_event_calendar->selected_date(), m_event_calendar->event_manager(), window());
|
||||
};
|
||||
}
|
||||
|
||||
}
|
48
Userland/Applications/Calendar/CalendarWidget.h
Normal file
48
Userland/Applications/Calendar/CalendarWidget.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "EventCalendar.h"
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibFileSystemAccessClient/Client.h>
|
||||
#include <LibGUI/Calendar.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
|
||||
namespace Calendar {
|
||||
|
||||
class CalendarWidget final : public GUI::Widget {
|
||||
C_OBJECT(CalendarWidget);
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<CalendarWidget>> create(GUI::Window*);
|
||||
virtual ~CalendarWidget() override = default;
|
||||
|
||||
void update_window_title();
|
||||
void load_file(FileSystemAccessClient::File file);
|
||||
|
||||
private:
|
||||
void create_on_tile_doubleclick();
|
||||
|
||||
String const& current_filename() const { return m_event_calendar->event_manager().current_filename(); }
|
||||
|
||||
void create_on_events_change();
|
||||
NonnullRefPtr<GUI::Action> create_save_as_action();
|
||||
NonnullRefPtr<GUI::Action> create_save_action(GUI::Action& save_as_action);
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_new_calendar_action();
|
||||
NonnullRefPtr<GUI::Action> create_open_calendar_action();
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_prev_date_action();
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_next_date_action();
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_add_event_action();
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_jump_to_action();
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_view_month_action();
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_view_year_action();
|
||||
ErrorOr<NonnullRefPtr<GUI::Action>> create_open_settings_action();
|
||||
|
||||
RefPtr<EventCalendar> m_event_calendar;
|
||||
};
|
||||
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
@GUI::Calendar {
|
||||
@::Calendar::EventCalendar {
|
||||
name: "calendar"
|
||||
}
|
||||
}
|
||||
|
|
52
Userland/Applications/Calendar/EventCalendar.cpp
Normal file
52
Userland/Applications/Calendar/EventCalendar.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "EventCalendar.h"
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
|
||||
REGISTER_WIDGET(::Calendar, EventCalendar);
|
||||
|
||||
namespace Calendar {
|
||||
|
||||
static constexpr int tile_breakpoint = 50;
|
||||
|
||||
EventCalendar::EventCalendar(Core::DateTime date_time, Mode mode)
|
||||
: Calendar(date_time, mode)
|
||||
, m_event_manager(EventManager::create())
|
||||
{
|
||||
}
|
||||
|
||||
void EventCalendar::paint_tile(GUI::Painter& painter, GUI::Calendar::Tile& tile, Gfx::IntRect& tile_rect, int x_offset, int y_offset, int day_offset)
|
||||
{
|
||||
Calendar::paint_tile(painter, tile, tile_rect, x_offset, y_offset, day_offset);
|
||||
|
||||
auto events = m_event_manager->events();
|
||||
|
||||
if (tile.width > tile_breakpoint && tile.height > tile_breakpoint) {
|
||||
auto index = 0;
|
||||
auto font_height = font().x_height();
|
||||
events.for_each([&](JsonValue const& value) {
|
||||
auto const& event = value.as_object();
|
||||
|
||||
if (!event.has("start_date"sv) || !event.has("summary"sv))
|
||||
return;
|
||||
|
||||
auto start_date = event.get("start_date"sv).value().to_deprecated_string();
|
||||
auto summary = event.get("summary"sv).value().to_deprecated_string();
|
||||
|
||||
if (start_date == DeprecatedString::formatted("{}-{:0>2d}-{:0>2d}", tile.year, tile.month, tile.day)) {
|
||||
|
||||
auto text_rect = tile.rect.translated(4, 4 + (font_height + 8) * ++index);
|
||||
|
||||
painter.draw_text(text_rect, summary, Gfx::FontDatabase::default_font(), Gfx::TextAlignment::TopLeft, palette().base_text());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
34
Userland/Applications/Calendar/EventCalendar.h
Normal file
34
Userland/Applications/Calendar/EventCalendar.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "EventManager.h"
|
||||
#include <LibFileSystemAccessClient/Client.h>
|
||||
#include <LibGUI/Calendar.h>
|
||||
|
||||
namespace Calendar {
|
||||
|
||||
class EventCalendar final : public GUI::Calendar {
|
||||
C_OBJECT(EventCalendar);
|
||||
|
||||
public:
|
||||
virtual ~EventCalendar() override = default;
|
||||
|
||||
EventManager& event_manager() const { return *m_event_manager; }
|
||||
|
||||
private:
|
||||
EventCalendar(Core::DateTime date_time = Core::DateTime::now(), Mode mode = Month);
|
||||
|
||||
ErrorOr<void> save(FileSystemAccessClient::File& file);
|
||||
ErrorOr<void> load_file(FileSystemAccessClient::File& file);
|
||||
|
||||
virtual void paint_tile(GUI::Painter&, GUI::Calendar::Tile&, Gfx::IntRect&, int x_offset, int y_offset, int day_offset) override;
|
||||
|
||||
OwnPtr<EventManager> m_event_manager;
|
||||
};
|
||||
|
||||
}
|
63
Userland/Applications/Calendar/EventManager.cpp
Normal file
63
Userland/Applications/Calendar/EventManager.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "EventManager.h"
|
||||
#include <AK/JsonParser.h>
|
||||
#include <LibConfig/Client.h>
|
||||
#include <LibFileSystemAccessClient/Client.h>
|
||||
|
||||
namespace Calendar {
|
||||
|
||||
EventManager::EventManager()
|
||||
{
|
||||
}
|
||||
|
||||
OwnPtr<EventManager> EventManager::create()
|
||||
{
|
||||
return adopt_own(*new EventManager());
|
||||
}
|
||||
|
||||
ErrorOr<void> EventManager::add_event(JsonObject event)
|
||||
{
|
||||
TRY(m_events.append(move(event)));
|
||||
set_dirty(true);
|
||||
on_events_change();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void EventManager::set_events(JsonArray events)
|
||||
{
|
||||
m_events = move(events);
|
||||
on_events_change();
|
||||
}
|
||||
|
||||
ErrorOr<void> EventManager::save(FileSystemAccessClient::File& file)
|
||||
{
|
||||
set_filename(file.filename());
|
||||
set_dirty(false);
|
||||
|
||||
auto stream = file.release_stream();
|
||||
TRY(stream->write_some(m_events.to_deprecated_string().bytes()));
|
||||
stream->close();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> EventManager::load_file(FileSystemAccessClient::File& file)
|
||||
{
|
||||
set_filename(file.filename());
|
||||
set_dirty(false);
|
||||
|
||||
auto content = TRY(file.stream().read_until_eof());
|
||||
auto events = TRY(AK::JsonParser(content).parse());
|
||||
|
||||
set_events(events.as_array());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
49
Userland/Applications/Calendar/EventManager.h
Normal file
49
Userland/Applications/Calendar/EventManager.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibFileSystemAccessClient/Client.h>
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
namespace Calendar {
|
||||
|
||||
class EventManager {
|
||||
AK_MAKE_NONCOPYABLE(EventManager);
|
||||
AK_MAKE_NONMOVABLE(EventManager);
|
||||
|
||||
public:
|
||||
static OwnPtr<EventManager> create();
|
||||
|
||||
String const& current_filename() const { return m_current_filename; }
|
||||
void set_filename(String const& filename) { m_current_filename = filename; }
|
||||
bool dirty() const { return m_dirty; }
|
||||
void set_dirty(bool dirty) { m_dirty = dirty; }
|
||||
|
||||
ErrorOr<void> save(FileSystemAccessClient::File& file);
|
||||
ErrorOr<void> load_file(FileSystemAccessClient::File& file);
|
||||
ErrorOr<void> add_event(JsonObject);
|
||||
void set_events(JsonArray events);
|
||||
void clear() { m_events.clear(); }
|
||||
|
||||
JsonArray const& events() const { return m_events; }
|
||||
|
||||
Function<void()> on_events_change;
|
||||
|
||||
private:
|
||||
explicit EventManager();
|
||||
|
||||
JsonArray m_events;
|
||||
|
||||
String m_current_filename;
|
||||
bool m_dirty { false };
|
||||
};
|
||||
|
||||
}
|
|
@ -5,9 +5,12 @@
|
|||
*/
|
||||
|
||||
#include "AddEventDialog.h"
|
||||
#include <Applications/Calendar/CalendarWindowGML.h>
|
||||
#include "CalendarWidget.h"
|
||||
#include <LibConfig/Client.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibFileSystemAccessClient/Client.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
#include <LibGUI/Application.h>
|
||||
|
@ -16,24 +19,42 @@
|
|||
#include <LibGUI/Icon.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Menubar.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGUI/Process.h>
|
||||
#include <LibGUI/Toolbar.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
TRY(Core::System::pledge("stdio recvfd sendfd rpath proc exec unix"));
|
||||
TRY(Core::System::pledge("stdio recvfd sendfd rpath wpath cpath proc exec unix"));
|
||||
|
||||
auto app = TRY(GUI::Application::create(arguments));
|
||||
|
||||
StringView filename;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_positional_argument(filename, "File to read from", "file", Core::ArgsParser::Required::No);
|
||||
|
||||
args_parser.parse(arguments);
|
||||
|
||||
if (!filename.is_empty()) {
|
||||
if (!FileSystem::exists(filename) || FileSystem::is_directory(filename)) {
|
||||
warnln("File does not exist or is a directory: {}", filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
Config::pledge_domain("Calendar");
|
||||
Config::monitor_domain("Calendar");
|
||||
|
||||
TRY(Core::System::pledge("stdio recvfd sendfd rpath proc exec"));
|
||||
TRY(Core::System::pledge("stdio recvfd sendfd rpath wpath cpath proc exec unix"));
|
||||
TRY(Core::System::unveil("/etc/timezone", "r"));
|
||||
TRY(Core::System::unveil("/res", "r"));
|
||||
TRY(Core::System::unveil("/bin/CalendarSettings", "x"));
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw"));
|
||||
TRY(Core::System::unveil(nullptr, nullptr));
|
||||
|
||||
auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-calendar"sv));
|
||||
|
@ -42,92 +63,18 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
window->resize(600, 480);
|
||||
window->set_icon(app_icon.bitmap_for_size(16));
|
||||
|
||||
auto main_widget = TRY(window->set_main_widget<GUI::Widget>());
|
||||
TRY(main_widget->load_from_gml(calendar_window_gml));
|
||||
|
||||
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 prev_date_action = GUI::Action::create({}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"sv)), [&](const GUI::Action&) {
|
||||
calendar->show_previous_date();
|
||||
});
|
||||
|
||||
auto next_date_action = GUI::Action::create({}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv)), [&](const GUI::Action&) {
|
||||
calendar->show_next_date();
|
||||
});
|
||||
|
||||
auto add_event_action = GUI::Action::create("&Add Event", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"sv)), [&](const GUI::Action&) {
|
||||
AddEventDialog::show(calendar->selected_date(), window);
|
||||
});
|
||||
|
||||
auto jump_to_action = GUI::Action::create("Jump to &Today", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"sv)), [&](const GUI::Action&) {
|
||||
calendar->set_selected_date(Core::DateTime::now());
|
||||
calendar->update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month());
|
||||
});
|
||||
|
||||
auto view_month_action = GUI::Action::create_checkable("&Month View", { Mod_Ctrl, KeyCode::Key_1 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-month-view.png"sv)), [&](const GUI::Action&) {
|
||||
if (calendar->mode() == GUI::Calendar::Year)
|
||||
calendar->toggle_mode();
|
||||
});
|
||||
view_month_action->set_checked(true);
|
||||
|
||||
auto view_year_action = GUI::Action::create_checkable("&Year View", { Mod_Ctrl, KeyCode::Key_2 }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"sv)), [&](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);
|
||||
auto default_view = Config::read_string("Calendar"sv, "View"sv, "DefaultView"sv, "Month"sv);
|
||||
if (default_view == "Year")
|
||||
view_year_action->set_checked(true);
|
||||
|
||||
auto open_settings_action = GUI::Action::create("Calendar &Settings", {}, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-settings.png"sv)), [&](GUI::Action const&) {
|
||||
GUI::Process::spawn_or_show_error(window, "/bin/CalendarSettings"sv);
|
||||
});
|
||||
|
||||
(void)TRY(toolbar->try_add_action(prev_date_action));
|
||||
(void)TRY(toolbar->try_add_action(next_date_action));
|
||||
TRY(toolbar->try_add_separator());
|
||||
(void)TRY(toolbar->try_add_action(jump_to_action));
|
||||
(void)TRY(toolbar->try_add_action(add_event_action));
|
||||
TRY(toolbar->try_add_separator());
|
||||
(void)TRY(toolbar->try_add_action(view_month_action));
|
||||
(void)TRY(toolbar->try_add_action(view_year_action));
|
||||
(void)TRY(toolbar->try_add_action(open_settings_action));
|
||||
|
||||
calendar->on_tile_doubleclick = [&] {
|
||||
AddEventDialog::show(calendar->selected_date(), window);
|
||||
};
|
||||
|
||||
calendar->on_month_click = [&] {
|
||||
view_month_action->set_checked(true);
|
||||
};
|
||||
|
||||
auto& file_menu = window->add_menu("&File"_short_string);
|
||||
file_menu.add_action(GUI::Action::create("&Add Event", { Mod_Ctrl | Mod_Shift, Key_E }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"sv)),
|
||||
[&](const GUI::Action&) {
|
||||
AddEventDialog::show(calendar->selected_date(), window);
|
||||
}));
|
||||
file_menu.add_action(open_settings_action);
|
||||
|
||||
TRY(file_menu.try_add_separator());
|
||||
|
||||
TRY(file_menu.try_add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
||||
GUI::Application::the()->quit();
|
||||
})));
|
||||
|
||||
auto view_menu = TRY(window->try_add_menu("&View"_short_string));
|
||||
TRY(view_menu->try_add_action(*view_month_action));
|
||||
TRY(view_menu->try_add_action(*view_year_action));
|
||||
|
||||
auto help_menu = TRY(window->try_add_menu("&Help"_short_string));
|
||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(window)));
|
||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Calendar", app_icon, window)));
|
||||
auto calendar_widget = TRY(Calendar::CalendarWidget::create(window));
|
||||
window->set_main_widget(calendar_widget);
|
||||
|
||||
window->show();
|
||||
|
||||
if (!filename.is_empty()) {
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, filename);
|
||||
if (!response.is_error()) {
|
||||
calendar_widget->load_file(response.release_value());
|
||||
}
|
||||
}
|
||||
|
||||
app->exec();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -396,8 +396,6 @@ void Calendar::paint_event(GUI::PaintEvent& event)
|
|||
|
||||
painter.translate(frame_thickness(), frame_thickness());
|
||||
|
||||
int width = unadjusted_tile_size().width();
|
||||
int height = unadjusted_tile_size().height();
|
||||
int x_offset = 0;
|
||||
int y_offset = 0;
|
||||
|
||||
|
@ -492,7 +490,6 @@ void Calendar::paint_event(GUI::PaintEvent& event)
|
|||
if (j > 0)
|
||||
y_offset += m_tiles[0][(j - 1) * 7].height + 1;
|
||||
for (int k = 0; k < 7; k++) {
|
||||
bool is_weekend = is_day_in_weekend((DayOfWeek)((k + to_underlying(m_first_day_of_week)) % 7));
|
||||
if (k > 0)
|
||||
x_offset += m_tiles[0][k - 1].width + 1;
|
||||
auto tile_rect = Gfx::IntRect(
|
||||
|
@ -502,48 +499,8 @@ void Calendar::paint_event(GUI::PaintEvent& event)
|
|||
m_tiles[0][i].height);
|
||||
m_tiles[0][i].rect = tile_rect.translated(frame_thickness(), frame_thickness());
|
||||
|
||||
Color background_color = palette().base();
|
||||
paint_tile(painter, m_tiles[0][i], tile_rect, x_offset, y_offset, k);
|
||||
|
||||
if (m_tiles[0][i].is_hovered || m_tiles[0][i].is_selected) {
|
||||
background_color = palette().hover_highlight();
|
||||
} else if (is_weekend) {
|
||||
background_color = palette().gutter();
|
||||
}
|
||||
|
||||
painter.fill_rect(tile_rect, background_color);
|
||||
|
||||
auto text_alignment = Gfx::TextAlignment::TopRight;
|
||||
auto text_rect = Gfx::IntRect(
|
||||
x_offset,
|
||||
y_offset + 4,
|
||||
m_tiles[0][i].width - 4,
|
||||
font().pixel_size_rounded_up() + 4);
|
||||
|
||||
if (width > 150 && height > 150) {
|
||||
set_font(extra_large_font);
|
||||
} else if (width > 100 && height > 100) {
|
||||
set_font(large_font);
|
||||
} else if (width > 50 && height > 50) {
|
||||
set_font(medium_font);
|
||||
} else if (width >= 30 && height >= 30) {
|
||||
set_font(small_font);
|
||||
} else {
|
||||
set_font(small_font);
|
||||
text_alignment = Gfx::TextAlignment::Center;
|
||||
text_rect = Gfx::IntRect(tile_rect);
|
||||
}
|
||||
|
||||
auto display_date = DeprecatedString::number(m_tiles[0][i].day);
|
||||
if (m_tiles[0][i].is_selected && (width < 30 || height < 30))
|
||||
painter.draw_rect(tile_rect, palette().base_text());
|
||||
|
||||
if (m_tiles[0][i].is_today && !m_tiles[0][i].is_outside_selected_month) {
|
||||
painter.draw_text(text_rect, display_date, font().bold_variant(), text_alignment, palette().base_text());
|
||||
} else if (m_tiles[0][i].is_outside_selected_month) {
|
||||
painter.draw_text(text_rect, display_date, m_tiles[0][i].is_today ? font().bold_variant() : font(), text_alignment, Color::LightGray);
|
||||
} else {
|
||||
painter.draw_text(text_rect, display_date, font(), text_alignment, palette().base_text());
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
@ -631,26 +588,8 @@ void Calendar::paint_event(GUI::PaintEvent& event)
|
|||
m_tiles[l][i].height);
|
||||
m_tiles[l][i].rect = tile_rect.translated(frame_thickness(), frame_thickness());
|
||||
|
||||
if (m_tiles[l][i].is_hovered || m_tiles[l][i].is_selected)
|
||||
painter.fill_rect(tile_rect, palette().hover_highlight());
|
||||
else
|
||||
painter.fill_rect(tile_rect, palette().base());
|
||||
paint_tile(painter, m_tiles[0][i], tile_rect, x_offset, y_offset, k);
|
||||
|
||||
if (width > 50 && height > 50) {
|
||||
set_font(medium_font);
|
||||
} else {
|
||||
set_font(small_font);
|
||||
}
|
||||
|
||||
auto display_date = DeprecatedString::number(m_tiles[l][i].day);
|
||||
if (m_tiles[l][i].is_selected)
|
||||
painter.draw_rect(tile_rect, palette().base_text());
|
||||
|
||||
if (m_tiles[l][i].is_today && !m_tiles[l][i].is_outside_selected_month) {
|
||||
painter.draw_text(tile_rect, display_date, font().bold_variant(), Gfx::TextAlignment::Center, palette().base_text());
|
||||
} else if (!m_tiles[l][i].is_outside_selected_month) {
|
||||
painter.draw_text(tile_rect, display_date, font(), Gfx::TextAlignment::Center, palette().base_text());
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
@ -658,6 +597,80 @@ void Calendar::paint_event(GUI::PaintEvent& event)
|
|||
}
|
||||
}
|
||||
|
||||
void Calendar::paint_tile(GUI::Painter& painter, GUI::Calendar::Tile& tile, Gfx::IntRect& tile_rect, int x_offset, int y_offset, int day_offset)
|
||||
{
|
||||
int width = unadjusted_tile_size().width();
|
||||
int height = unadjusted_tile_size().height();
|
||||
|
||||
if (mode() == Month) {
|
||||
bool is_weekend = is_day_in_weekend((DayOfWeek)((day_offset + to_underlying(m_first_day_of_week)) % 7));
|
||||
|
||||
Color background_color = palette().base();
|
||||
|
||||
if (tile.is_hovered || tile.is_selected) {
|
||||
background_color = palette().hover_highlight();
|
||||
} else if (is_weekend) {
|
||||
background_color = palette().gutter();
|
||||
}
|
||||
|
||||
painter.fill_rect(tile_rect, background_color);
|
||||
|
||||
auto text_alignment = Gfx::TextAlignment::TopRight;
|
||||
auto text_rect = Gfx::IntRect(
|
||||
x_offset,
|
||||
y_offset + 4,
|
||||
tile.width - 4,
|
||||
font().pixel_size_rounded_up() + 4);
|
||||
|
||||
if (width > 150 && height > 150) {
|
||||
set_font(extra_large_font);
|
||||
} else if (width > 100 && height > 100) {
|
||||
set_font(large_font);
|
||||
} else if (width > 50 && height > 50) {
|
||||
set_font(medium_font);
|
||||
} else if (width >= 30 && height >= 30) {
|
||||
set_font(small_font);
|
||||
} else {
|
||||
set_font(small_font);
|
||||
text_alignment = Gfx::TextAlignment::Center;
|
||||
text_rect = Gfx::IntRect(tile_rect);
|
||||
}
|
||||
|
||||
auto display_date = DeprecatedString::number(tile.day);
|
||||
if (tile.is_selected && (width < 30 || height < 30))
|
||||
painter.draw_rect(tile_rect, palette().base_text());
|
||||
|
||||
if (tile.is_today && !tile.is_outside_selected_month) {
|
||||
painter.draw_text(text_rect, display_date, font().bold_variant(), text_alignment, palette().base_text());
|
||||
} else if (tile.is_outside_selected_month) {
|
||||
painter.draw_text(text_rect, display_date, tile.is_today ? font().bold_variant() : font(), text_alignment, Color::LightGray);
|
||||
} else {
|
||||
painter.draw_text(text_rect, display_date, font(), text_alignment, palette().base_text());
|
||||
}
|
||||
} else {
|
||||
if (tile.is_hovered || tile.is_selected)
|
||||
painter.fill_rect(tile_rect, palette().hover_highlight());
|
||||
else
|
||||
painter.fill_rect(tile_rect, palette().base());
|
||||
|
||||
if (width > 50 && height > 50) {
|
||||
set_font(medium_font);
|
||||
} else {
|
||||
set_font(small_font);
|
||||
}
|
||||
|
||||
auto display_date = DeprecatedString::number(tile.day);
|
||||
if (tile.is_selected)
|
||||
painter.draw_rect(tile_rect, palette().base_text());
|
||||
|
||||
if (tile.is_today && !tile.is_outside_selected_month) {
|
||||
painter.draw_text(tile_rect, display_date, font().bold_variant(), Gfx::TextAlignment::Center, palette().base_text());
|
||||
} else if (!tile.is_outside_selected_month) {
|
||||
painter.draw_text(tile_rect, display_date, font(), Gfx::TextAlignment::Center, palette().base_text());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Calendar::leave_event(Core::Event&)
|
||||
{
|
||||
int months;
|
||||
|
|
|
@ -17,12 +17,25 @@
|
|||
|
||||
namespace GUI {
|
||||
|
||||
class Calendar final
|
||||
class Calendar
|
||||
: public GUI::AbstractScrollableWidget
|
||||
, public Config::Listener {
|
||||
C_OBJECT(Calendar)
|
||||
|
||||
public:
|
||||
struct Tile {
|
||||
unsigned year;
|
||||
unsigned month;
|
||||
unsigned day;
|
||||
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 };
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
Month,
|
||||
Year
|
||||
|
@ -35,6 +48,8 @@ public:
|
|||
YearOnly
|
||||
};
|
||||
|
||||
virtual ~Calendar() override = default;
|
||||
|
||||
void set_selected_date(Core::DateTime date_time) { m_selected_date = date_time; }
|
||||
Core::DateTime selected_date() const { return m_selected_date; }
|
||||
|
||||
|
@ -77,20 +92,21 @@ public:
|
|||
|
||||
virtual void config_string_did_change(StringView, StringView, StringView, StringView) override;
|
||||
virtual void config_i32_did_change(StringView, StringView, StringView, i32 value) override;
|
||||
virtual void paint_event(GUI::PaintEvent&) override;
|
||||
virtual void paint_tile(GUI::Painter&, GUI::Calendar::Tile&, Gfx::IntRect&, int x_offset, int y_offset, int day_offset);
|
||||
|
||||
Function<void()> on_scroll;
|
||||
Function<void()> on_tile_click;
|
||||
Function<void()> on_tile_doubleclick;
|
||||
Function<void()> on_month_click;
|
||||
|
||||
private:
|
||||
protected:
|
||||
Calendar(Core::DateTime date_time = Core::DateTime::now(), Mode mode = Month);
|
||||
virtual ~Calendar() override = default;
|
||||
|
||||
private:
|
||||
static size_t day_of_week_index(DeprecatedString const&);
|
||||
|
||||
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;
|
||||
|
@ -127,18 +143,6 @@ private:
|
|||
};
|
||||
Vector<MonthTile> m_months;
|
||||
|
||||
struct Tile {
|
||||
unsigned year;
|
||||
unsigned month;
|
||||
unsigned day;
|
||||
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 };
|
||||
|
|
Loading…
Reference in a new issue