result_selection_controller.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef ASH_APP_LIST_VIEWS_RESULT_SELECTION_CONTROLLER_H_
  5. #define ASH_APP_LIST_VIEWS_RESULT_SELECTION_CONTROLLER_H_
  6. #include <memory>
  7. #include <string>
  8. #include <vector>
  9. #include "ash/app_list/views/search_result_base_view.h"
  10. #include "ash/app_list/views/search_result_container_view.h"
  11. #include "ash/ash_export.h"
  12. #include "base/callback.h"
  13. namespace ash {
  14. class SearchResultContainerView;
  15. // This alias is intended to clarify the intended use of this class within the
  16. // context of this controller.
  17. using ResultSelectionModel = std::vector<SearchResultContainerView*>;
  18. // Stores and organizes the details for the 'coordinates' of the selected
  19. // result. This includes all information to determine exactly where a result is,
  20. // including both inter- and intra-container details, along with the traversal
  21. // direction for the container.
  22. struct ASH_EXPORT ResultLocationDetails {
  23. ResultLocationDetails();
  24. ResultLocationDetails(int container_index,
  25. int container_count,
  26. int result_index,
  27. int result_count,
  28. bool container_is_horizontal);
  29. bool operator==(const ResultLocationDetails& other) const;
  30. bool operator!=(const ResultLocationDetails& other) const;
  31. // True if the result is the first(0th) in its container
  32. bool is_first_result() const { return result_index == 0; }
  33. // True if the result is the last in its container
  34. bool is_last_result() const { return result_index == (result_count - 1); }
  35. // Index of the container within the overall nest of containers.
  36. int container_index = 0;
  37. // Number of containers to traverse among.
  38. int container_count = 0;
  39. // Index of the result within the list of results inside a container.
  40. int result_index = 0;
  41. // Number of results within the current container.
  42. int result_count = 0;
  43. // Whether the container is horizontally traversable.
  44. bool container_is_horizontal = false;
  45. };
  46. // A controller class to manage result selection across containers.
  47. class ASH_EXPORT ResultSelectionController {
  48. public:
  49. enum class MoveResult {
  50. // The selection has not changed (excluding the case covered by
  51. // kSelectionCycleRejected).
  52. kNone,
  53. // The selection has not changed because the selection would cycle.
  54. kSelectionCycleRejected,
  55. // The currently selected result has changed.
  56. //
  57. // Note: As long as the selected result remains the same, the result action
  58. // changes will be reported as kNone, mainly because the code that uses
  59. // MoveSelection() treats them the same.
  60. kResultChanged,
  61. };
  62. ResultSelectionController(
  63. const ResultSelectionModel* result_container_views,
  64. const base::RepeatingClosure& selection_change_callback);
  65. ResultSelectionController(const ResultSelectionController&) = delete;
  66. ResultSelectionController& operator=(const ResultSelectionController&) =
  67. delete;
  68. ~ResultSelectionController();
  69. // Returns the currently selected result.
  70. // Note that the return view might contain null result if results are
  71. // currently being updated.
  72. // As long as |block_selection_changes_| gets set while results are changing,
  73. // it should be safe to assume the result is not null after call to
  74. // MoveSelection() changes the selected result.
  75. SearchResultBaseView* selected_result() { return selected_result_; }
  76. // Returns the |ResultLocationDetails| object for the |selected_result_|.
  77. ResultLocationDetails* selected_location_details() {
  78. return selected_location_details_.get();
  79. }
  80. // Calls |SetSelection| using the result of |GetNextResultLocation|.
  81. MoveResult MoveSelection(const ui::KeyEvent& event);
  82. // Resets the selection to the first result.
  83. // |key_event| - The key event that triggered reselect, if any. Used to
  84. // determine whether selection should start at the last element.
  85. // |default_selection| - True if it resets the first result as default
  86. // selection.
  87. void ResetSelection(const ui::KeyEvent* key_event, bool default_selection);
  88. // Clears the |selected_result_|, |selected_location_details_|.
  89. void ClearSelection();
  90. // Used to block selection changes while async search result updates are in
  91. // flight, i.e. while the result views might point to obsolete null results.
  92. // Should be set for a short time, and setting this to false should generally
  93. // be followed by ResetSelection().
  94. void set_block_selection_changes(bool block_selection_changes) {
  95. block_selection_changes_ = block_selection_changes;
  96. }
  97. private:
  98. // Calls |GetNextResultLocationForLocation| using |selected_location_details_|
  99. // as the location
  100. MoveResult GetNextResultLocation(const ui::KeyEvent& event,
  101. ResultLocationDetails* next_location);
  102. // Logic for next is separated for modular use. You can ask for the "next"
  103. // location to be generated using any starting location/event combination.
  104. MoveResult GetNextResultLocationForLocation(
  105. const ui::KeyEvent& event,
  106. const ResultLocationDetails& location,
  107. ResultLocationDetails* next_location);
  108. // Sets the current selection to the provided |location|.
  109. void SetSelection(const ResultLocationDetails& location,
  110. bool reverse_tab_order);
  111. // Returns the location for the first result in the first non-empty result
  112. // container. Returns nullptr if no results exist.
  113. std::unique_ptr<ResultLocationDetails> GetFirstAvailableResultLocation()
  114. const;
  115. SearchResultBaseView* GetResultAtLocation(
  116. const ResultLocationDetails& location);
  117. // Returns the location of a result with the provided ID.
  118. // Returns nullptr if the result cannot be found.
  119. std::unique_ptr<ResultLocationDetails> FindResultWithId(
  120. const std::string& id);
  121. // Updates a |ResultLocationDetails| to a new container, updating most
  122. // attributes based on |result_selection_model_|.
  123. void ChangeContainer(ResultLocationDetails* location_details,
  124. int new_container_index);
  125. // Container views to be traversed by this controller.
  126. // Owned by |SearchResultPageView|.
  127. const ResultSelectionModel* result_selection_model_;
  128. // Returns true if the container at the given |index| within
  129. // |result_selection_model_| responds true to
  130. // |SearchResultContainerView|->|IsHorizontallyTraversable|.
  131. bool IsContainerAtIndexHorizontallyTraversable(int index) const;
  132. // The callback run when the selected result changes (including when the
  133. // selected result is cleared).
  134. base::RepeatingClosure selection_change_callback_;
  135. // The currently selected result view.
  136. SearchResultBaseView* selected_result_ = nullptr;
  137. // The currently selected result ID.
  138. std::string selected_result_id_;
  139. // If set, any attempt to change current selection will be rejected.
  140. bool block_selection_changes_ = false;
  141. // The |ResultLocationDetails| for the currently selected result view
  142. std::unique_ptr<ResultLocationDetails> selected_location_details_;
  143. };
  144. } // namespace ash
  145. #endif // ASH_APP_LIST_VIEWS_RESULT_SELECTION_CONTROLLER_H_