feat: add calendar visibility picker
Derive session calendars from event metadata and apply one visibility contract to month rendering, appointment focus, and search without filtering persistence writes. Add keyboard picker coverage, full-model save regression tests, documentation, warning-clean builds, sanitizer verification, and live PTY terminal restoration checks.
This commit is contained in:
@@ -44,6 +44,14 @@ void sort_events(EventRefs& events);
|
||||
[[nodiscard]] bool event_matches_query(const Event& event,
|
||||
std::string_view query);
|
||||
|
||||
// Calendar visibility is an application/query concern. An event whose
|
||||
// calendar is not yet represented remains visible so newly loaded or created
|
||||
// data cannot disappear merely because metadata has not been synchronized.
|
||||
[[nodiscard]] bool calendar_is_visible(std::span<const Calendar> calendars,
|
||||
std::string_view calendar_id) noexcept;
|
||||
[[nodiscard]] bool event_is_visible(const Event& event,
|
||||
std::span<const Calendar> calendars) noexcept;
|
||||
|
||||
// Event and query intervals are half-open. A zero-duration event is treated as
|
||||
// an instant and belongs to the interval containing its start time.
|
||||
[[nodiscard]] bool event_overlaps(const Event& event, TimePoint begin,
|
||||
|
||||
@@ -71,6 +71,8 @@ struct ScreenState {
|
||||
std::size_t search_result_index{0};
|
||||
std::string notification;
|
||||
bool notification_is_error{false};
|
||||
bool show_calendar_picker{false};
|
||||
std::size_t calendar_index{0};
|
||||
};
|
||||
|
||||
// Render a complete frame. The result contains no cursor positioning, which
|
||||
@@ -83,6 +85,12 @@ struct ScreenState {
|
||||
[[nodiscard]] std::string render_month(std::span<const Event> events,
|
||||
const ScreenState& state);
|
||||
|
||||
// Calendar visibility filters presentation only. Focus identities are still
|
||||
// derived from the complete event collection.
|
||||
[[nodiscard]] std::string render_month(std::span<const Event> events,
|
||||
std::span<const Calendar> calendars,
|
||||
const ScreenState& state);
|
||||
|
||||
// Stable focus identity shared by the renderer and controller. Non-recurring
|
||||
// events retain their historical UID/synthetic identity; recurring instances
|
||||
// additionally include their concrete start instant.
|
||||
@@ -109,6 +117,7 @@ enum class Action {
|
||||
undo,
|
||||
redo,
|
||||
search,
|
||||
calendars,
|
||||
quit,
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ public:
|
||||
[[nodiscard]] bool search_active() const noexcept {
|
||||
return state_.search_prompt || state_.show_search_results;
|
||||
}
|
||||
[[nodiscard]] bool calendar_picker_active() const noexcept {
|
||||
return state_.show_calendar_picker;
|
||||
}
|
||||
[[nodiscard]] const std::vector<Calendar>& calendars() const noexcept {
|
||||
return calendars_;
|
||||
}
|
||||
|
||||
private:
|
||||
struct HistorySnapshot {
|
||||
@@ -64,6 +70,7 @@ private:
|
||||
static constexpr std::size_t history_limit = 100;
|
||||
|
||||
std::vector<Event>& events_;
|
||||
std::vector<Calendar> calendars_;
|
||||
int input_fd_;
|
||||
int output_fd_;
|
||||
ScreenState state_;
|
||||
@@ -77,6 +84,11 @@ private:
|
||||
|
||||
[[nodiscard]] std::optional<EventOccurrence> focused_occurrence() const;
|
||||
[[nodiscard]] std::optional<std::size_t> focused_event_index() const;
|
||||
[[nodiscard]] EventOccurrences visible_occurrences_on_selected_day() const;
|
||||
void synchronize_calendars();
|
||||
void begin_calendar_picker();
|
||||
void close_calendar_picker();
|
||||
void toggle_selected_calendar();
|
||||
[[nodiscard]] HistorySnapshot capture_history_snapshot() const;
|
||||
void restore_history_snapshot(HistorySnapshot snapshot);
|
||||
void push_history(std::vector<HistoryEntry>& history, HistoryEntry entry);
|
||||
|
||||
Reference in New Issue
Block a user