| // Copyright 2023 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_GLANCEABLES_TASKS_GLANCEABLES_TASK_VIEW_H_ |
| #define ASH_GLANCEABLES_TASKS_GLANCEABLES_TASK_VIEW_H_ |
| |
| #include <string> |
| #include <string_view> |
| |
| #include "ash/api/tasks/tasks_client.h" |
| #include "ash/api/tasks/tasks_types.h" |
| #include "ash/ash_export.h" |
| #include "ash/glanceables/tasks/glanceables_tasks_error_type.h" |
| #include "ash/style/error_message_toast.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/scoped_multi_source_observation.h" |
| #include "google_apis/common/api_error_codes.h" |
| #include "ui/base/metadata/metadata_header_macros.h" |
| #include "ui/views/layout/flex_layout_view.h" |
| #include "ui/views/view_observer.h" |
| |
| namespace views { |
| class ImageButton; |
| class ImageView; |
| class LabelButton; |
| } // namespace views |
| |
| namespace ash { |
| |
| class SystemTextfield; |
| |
| // `GlanceablesTaskView` uses `views::FlexLayout` to show tasks metadata within |
| // the `GlanceablesTasksView`. |
| // +---------------------------------------------------------------+ |
| // |`GlanceablesTaskView` | |
| // | | |
| // | +-----------------+ +---------------------------------------+ | |
| // | |'check_button_' | |'contents_view_' | | |
| // | | | | +-----------------------------------+ | | |
| // | | | | |'tasks_title_view_' | | | |
| // | | | | +-----------------------------------+ | | |
| // | | | | +-----------------------------------+ | | |
| // | | | | |'tasks_details_view_' | | | |
| // | | | | +-----------------------------------+ | | |
| // | +-----------------+ +---------------------------------------+ | |
| // +---------------------------------------------------------------+ |
| class ASH_EXPORT GlanceablesTaskView : public views::FlexLayoutView, |
| public views::ViewObserver { |
| METADATA_HEADER(GlanceablesTaskView, views::FlexLayoutView) |
| |
| public: |
| using MarkAsCompletedCallback = |
| base::RepeatingCallback<void(const std::string& task_id, bool completed)>; |
| using SaveCallback = base::RepeatingCallback<void( |
| base::WeakPtr<GlanceablesTaskView> view, |
| const std::string& task_id, |
| const std::string& title, |
| api::TasksClient::OnTaskSavedCallback callback)>; |
| using ShowErrorMessageCallback = |
| base::RepeatingCallback<void(GlanceablesTasksErrorType, |
| ErrorMessageToast::ButtonActionType)>; |
| using StateChangeObserverCallback = |
| base::RepeatingCallback<void(bool view_expanding)>; |
| // Modes of `tasks_title_view_` (simple label or text field). |
| enum class TaskTitleViewState { kNotInitialized, kView, kEdit }; |
| |
| GlanceablesTaskView(const api::Task* task, |
| MarkAsCompletedCallback mark_as_completed_callback, |
| SaveCallback save_callback, |
| base::RepeatingClosure edit_in_browser_callback, |
| ShowErrorMessageCallback show_error_message_callback); |
| GlanceablesTaskView(const GlanceablesTaskView&) = delete; |
| GlanceablesTaskView& operator=(const GlanceablesTaskView&) = delete; |
| ~GlanceablesTaskView() override; |
| |
| void set_state_change_observer(const StateChangeObserverCallback& observer) { |
| state_change_observer_ = observer; |
| } |
| |
| // views::ViewObserver: |
| void OnViewBlurred(views::View* observed_view) override; |
| void OnViewIsDeleting(views::View* observed_view) override; |
| |
| const views::ImageButton* GetCheckButtonForTest() const; |
| bool GetCompletedForTest() const; |
| void SetCheckedForTest(bool checked); |
| |
| // Updates `tasks_title_view_` according to `state`. |
| void UpdateTaskTitleViewForState(TaskTitleViewState state); |
| |
| // Sets the network to be connected. This should only be used in tests. |
| static void SetIsNetworkConnectedForTest(bool connected); |
| |
| private: |
| class CheckButton; |
| class TaskTitleButton; |
| |
| // Adds additional content (assigned task privacy notice if available + edit |
| // in browser button) when the view is in edit mode. |
| void AddExtraContentForEditState(); |
| |
| // Updates the margins of views in `contents_view_`. |
| void UpdateContentsMargins(TaskTitleViewState state); |
| |
| // Updates the `extra_content_for_edit_state_` visibility animating its |
| // opacity. When hiding the container, the container is hidden immediately, |
| // but copy of its layer is created, and animated in its place. |
| void AnimateExtraContentForEditStateVisibility(bool visible); |
| void OnExtraContentForEditStateVisibilityAnimationCompleted(); |
| |
| // Handles press events on `check_button_`. |
| void CheckButtonPressed(); |
| |
| // Handles press events on `task_title_button_`. |
| void TaskTitleButtonPressed(); |
| |
| // Handles finished editing event from the text field, updates `task_title_` |
| // and propagates new `title` to the server. |
| void OnFinishedEditing(std::u16string_view title); |
| |
| // Handles completion of running `save_callback_` callback. |
| // `task` - newly created or updated task. |
| void OnSaved(google_apis::ApiErrorCode http_error, const api::Task* task); |
| |
| // Owned by views hierarchy. |
| raw_ptr<CheckButton> check_button_ = nullptr; |
| raw_ptr<views::FlexLayoutView> contents_view_ = nullptr; |
| raw_ptr<views::FlexLayoutView> tasks_title_view_ = nullptr; |
| raw_ptr<TaskTitleButton> task_title_button_ = nullptr; |
| raw_ptr<SystemTextfield> task_title_textfield_ = nullptr; |
| raw_ptr<views::FlexLayoutView> tasks_details_view_ = nullptr; |
| raw_ptr<views::ImageView> origin_surface_type_icon_ = nullptr; |
| raw_ptr<views::View> extra_content_for_edit_state_ = nullptr; |
| raw_ptr<views::LabelButton> edit_in_browser_button_ = nullptr; |
| |
| // ID for the task represented by this view. |
| std::string task_id_; |
| |
| // Title of the task. |
| std::u16string task_title_; |
| |
| // The type of surface a task originates from. |
| api::Task::OriginSurfaceType origin_surface_type_; |
| |
| // Cached to reset the value of `task_title_` when the new title failed to |
| // commit after editing. |
| std::u16string task_title_before_edit_ = u""; |
| |
| bool saving_task_changes_ = false; |
| |
| // Marks the task as completed. |
| const MarkAsCompletedCallback mark_as_completed_callback_; |
| |
| // Saves the task (either creates or updates the existing one). |
| const SaveCallback save_callback_; |
| |
| // `edit_in_browser_button_` callback that opens the Tasks in browser. |
| const base::RepeatingClosure edit_in_browser_callback_; |
| |
| // Shows an error message in the parent `GlanceablesTasksView`. |
| const ShowErrorMessageCallback show_error_message_callback_; |
| |
| // Callback which, if set, gets called when the task view state changes |
| // between editing and viewing state. |
| StateChangeObserverCallback state_change_observer_; |
| |
| // Copy of `extra_content_for_edit_state_` (assigned task privacy notice if |
| // available + edit in browser button) layer used for the visibility animation |
| // when hiding the container. The container gets hidden immediately, and the |
| // layer is faded out in its place. Deleted when the fade out animation |
| // completes. |
| std::unique_ptr<ui::Layer> animating_extra_content_for_edit_state_layer_; |
| |
| base::ScopedMultiSourceObservation<views::View, GlanceablesTaskView> |
| edit_exit_observer_{this}; |
| |
| base::WeakPtrFactory<GlanceablesTaskView> state_change_weak_ptr_factory_{ |
| this}; |
| |
| base::WeakPtrFactory<GlanceablesTaskView> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_GLANCEABLES_TASKS_GLANCEABLES_TASK_VIEW_H_ |