chromium/ash/system/time/calendar_month_view.h

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SYSTEM_TIME_CALENDAR_MONTH_VIEW_H_
#define ASH_SYSTEM_TIME_CALENDAR_MONTH_VIEW_H_

#include "ash/ash_export.h"
#include "ash/system/time/calendar_list_model.h"
#include "ash/system/time/calendar_model.h"
#include "ash/system/time/calendar_view_controller.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/view.h"

namespace ui {
class Event;
}  // namespace ui

namespace ash {

// Renders a Calendar date cell. Pass in `true` as `is_grayed_out_date` if
// the date is not in the current month view's month.
class CalendarDateCellView : public CalendarViewController::Observer,
                             public views::LabelButton {
  METADATA_HEADER(CalendarDateCellView, views::LabelButton)

 public:
  CalendarDateCellView(CalendarViewController* calendar_view_controller,
                       base::Time date,
                       base::TimeDelta time_difference,
                       bool is_grayed_out_date,
                       bool should_fetch_calendar_data,
                       int row_index,
                       bool is_fetched);
  CalendarDateCellView(const CalendarDateCellView& other) = delete;
  CalendarDateCellView& operator=(const CalendarDateCellView& other) = delete;
  ~CalendarDateCellView() override;

  // views::View:
  void OnThemeChanged() override;

  // Draws the background for 'today'. If today is a grayed out date, which is
  // shown in its previous/next month, this background won't be drawn.
  void OnPaintBackground(gfx::Canvas* canvas) override;

  // CalendarViewController::Observer:
  void OnSelectedDateUpdated() override;
  void CloseEventList() override;

  // Enables focus behavior of this cell.
  void EnableFocus();

  // Disables focus behavior of this cell.
  void DisableFocus();

  // Sets the tooltip label and a11y label based on the `event_number_`.
  void SetTooltipAndAccessibleName();

  // Updates the fetching status and checks if needs to repaint.
  void UpdateFetchStatus(bool is_fetched);

  // When focusing on the date cell for the first time, it shows "Use arrow keys
  // to navigate between dates" as instructions.
  void SetFirstOnFocusedAccessibilityLabel();

  // The row index in the date's month view.
  int row_index() const { return row_index_; }

  // Whether this CalendarDateCellView represents today, when the view was
  // constructed.
  bool is_today() const { return is_today_; }

 protected:
  // views::Button:
  void PaintButtonContents(gfx::Canvas* canvas) override;

 private:
  // For unit tests.
  friend class CalendarMonthViewFetchTest;
  friend class CalendarMonthViewTest;

  // Callback called when this view is activated.
  void OnDateCellActivated(const ui::Event& event);

  // Computes the position of the indicator that the day has events.
  gfx::Point GetEventsPresentIndicatorCenterPosition();

  // Draw the indicator if the day has events.
  void MaybeDrawEventsIndicator(gfx::Canvas* canvas);

  // The date used to render this cell view.
  const base::Time date_;

  const bool grayed_out_;

  // Indicates whether calendar data is expected to be fetched for this cell.
  const bool should_fetch_calendar_data_;

  // The row index in the current month for this date cell. Starts from 0.
  const int row_index_;

  // If the events of this date cell's month view have been fetched.
  bool is_fetched_;

  // If the current cell is selected.
  bool is_selected_ = false;

  // Whether this CalendarDateCellView represented today when the view was
  // constructed.
  const bool is_today_;

  // The number of event for `date_`.
  int event_number_ = 0;

  // For testing. Whether the events indicator is drawn or not.
  bool is_events_indicator_drawn = false;

  // The tool tip for this view. Before events data is back, only show date.
  // After the events date is back, show date and event numbers.
  std::u16string tool_tip_;

  // The time difference from UTC time based on `date_`;
  base::TimeDelta time_difference_;

  // Owned by UnifiedCalendarViewController.
  const raw_ptr<CalendarViewController> calendar_view_controller_;

  base::ScopedObservation<CalendarViewController,
                          CalendarViewController::Observer>
      scoped_calendar_view_controller_observer_{this};
};

//  Container for `CalendarDateCellView` for a single month.
class ASH_EXPORT CalendarMonthView : public views::View,
                                     public CalendarListModel::Observer,
                                     public CalendarModel::Observer {
  METADATA_HEADER(CalendarMonthView, views::View)
 public:
  CalendarMonthView(base::Time first_day_of_month,
                    CalendarViewController* calendar_view_controller);
  CalendarMonthView(const CalendarMonthView& other) = delete;
  CalendarMonthView& operator=(const CalendarMonthView& other) = delete;
  ~CalendarMonthView() override;

  // CalendarListModel::Observer:
  void OnCalendarListFetchComplete() override;

  // CalendarModel::Observer:
  void OnEventsFetched(const CalendarModel::FetchingStatus status,
                       const base::Time start_time) override;

  // Enable each cell's focus behavior.
  void EnableFocus();

  // Disable each cell's focus behavior.
  void DisableFocus();

  // Updates is_fetched_ for each date cell and schedules repaint.
  void UpdateIsFetchedAndRepaint(bool updated_is_fetched);

  // Gets the cells of each row that should be first focused on.
  std::vector<raw_ptr<CalendarDateCellView, VectorExperimental>>
  focused_cells() {
    return focused_cells_;
  }

  // If today's cell is in this view.
  bool has_today() { return has_today_; }

  // Returns the index of this month view's last row.
  int last_row_index() const { return last_row_index_; }

  // If this month contains any events.
  bool has_events() { return has_events_; }

 private:
  // For unit tests.
  friend class CalendarMonthViewTest;
  friend class CalendarMonthViewFetchTest;

  // Adds the `current_date`'s `CalendarDateCellView` to the table layout and
  // returns it.
  CalendarDateCellView* AddDateCellToLayout(base::Time current_date,
                                            int column,
                                            bool is_in_current_month,
                                            int row_index,
                                            bool is_fetched,
                                            bool should_fetch_calendar_data);

  // Fetches events.
  void FetchEvents(const base::Time& month);

  // Owned by `CalendarView`.
  const raw_ptr<CalendarViewController> calendar_view_controller_;

  // If today's cell is in this view.
  bool has_today_ = false;

  // The index of this month view's last row.
  int last_row_index_;

  bool has_events_ = false;

  // The cells of each row that should be first focused on. These
  // `CalendarDateCellView`s are the children of this view.
  std::vector<raw_ptr<CalendarDateCellView, VectorExperimental>> focused_cells_;

  // UTC midnight to designate the month whose events will be fetched.
  base::Time fetch_month_;

  const raw_ptr<CalendarListModel> calendar_list_model_;

  base::ScopedObservation<CalendarListModel, CalendarListModel::Observer>
      scoped_calendar_list_model_observer_{this};

  // Raw pointer to the (singleton) CalendarModel, to avoid a bunch of
  // daisy-chained calls to get the std::unique_ptr<>.
  const raw_ptr<CalendarModel> calendar_model_;

  base::ScopedObservation<CalendarModel, CalendarModel::Observer>
      scoped_calendar_model_observer_{this};
};

}  // namespace ash

#endif  // ASH_SYSTEM_TIME_CALENDAR_MONTH_VIEW_H_