1 /* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef UI_MODEL_H 17 #define UI_MODEL_H 18 19 #include <vector> 20 #include <sstream> 21 #include "common_utilities_hpp.h" 22 #include "frontend_api_handler.h" 23 #include "json.hpp" 24 25 namespace OHOS::uitest { 26 using namespace std; 27 28 /**Enumerates the supported UiComponent attributes.*/ 29 enum UiAttr : uint8_t { 30 ACCESSIBILITY_ID, 31 ID, 32 TEXT, 33 KEY, 34 TYPE, 35 BOUNDS, 36 ENABLED, 37 FOCUSED, 38 SELECTED, 39 CLICKABLE, 40 LONG_CLICKABLE, 41 SCROLLABLE, 42 CHECKABLE, 43 CHECKED, 44 ORIGBOUNDS, 45 // inner used attributes 46 HIERARCHY, 47 HASHCODE, 48 BOUNDSCENTER, 49 HOST_WINDOW_ID, 50 VISIBLE, 51 ABILITYNAME, 52 BUNDLENAME, 53 PAGEPATH, 54 }; 55 56 /**Supported UiComponent attribute names. Ordered by <code>UiAttr</code> definition.*/ 57 constexpr std::string_view ATTR_NAMES[] = { 58 "accessibilityId", // ACCESSIBILITY_ID 59 "id", // ID 60 "text", // TEXT 61 "key", // KEY 62 "type", // TYPE 63 "bounds", // BOUNDS 64 "enabled", // ENABLED 65 "focused", // FOCUSED 66 "selected", // SELECTED 67 "clickable", // CLICKABLE 68 "longClickable", // LONG_CLICKABLE 69 "scrollable", // SCROLLABLE 70 "checkable", // CHECKABLE 71 "checked", // CHECKED 72 "origBounds", // ORIGBOUNDS 73 "hierarchy", // HIERARCHY 74 "hashcode", // HASHCODE 75 "boundsCenter", // BOUNDSCENTER 76 "hostWindowId", // HOST_WINDOW_ID 77 "visible", // VISIBLE 78 "abilityName", // ABILITYNAME 79 "bundleName", // BUNDLENAME 80 "pagePath", // PAGEPATH 81 }; 82 83 struct Point { PointPoint84 Point() : px_(0), py_(0) {}; PointPoint85 Point(int32_t px, int32_t py) : px_(px), py_(py) {}; 86 int32_t px_; 87 int32_t py_; 88 }; 89 90 /**Represents a reasonable rectangle area.*/ 91 struct Rect { RectRect92 Rect(int32_t left, int32_t right, int32_t top, int32_t bottom) 93 : left_(left), right_(right), top_(top), bottom_(bottom) 94 { 95 DCHECK(right_ >= left_ && bottom_ >= top_); 96 }; 97 98 int32_t left_; 99 int32_t right_; 100 int32_t top_; 101 int32_t bottom_; 102 GetCenterXRect103 FORCE_INLINE int32_t GetCenterX() const 104 { 105 return (left_ + right_) / TWO; 106 } 107 GetCenterYRect108 FORCE_INLINE int32_t GetCenterY() const 109 { 110 return (top_ + bottom_) / TWO; 111 } 112 GetWidthRect113 FORCE_INLINE int32_t GetWidth() const 114 { 115 return right_ - left_; 116 } 117 GetHeightRect118 FORCE_INLINE int32_t GetHeight() const 119 { 120 return bottom_ - top_; 121 } 122 }; 123 124 /**Algorithm of rectangle.*/ 125 class RectAlgorithm { 126 public: 127 FORCE_INLINE static bool CheckEqual(const Rect &ra, const Rect &rb); 128 FORCE_INLINE static bool CheckIntersectant(const Rect &ra, const Rect &rb); 129 FORCE_INLINE static bool IsInnerPoint(const Rect &rect, const Point &point); 130 FORCE_INLINE static bool IsPointOnEdge(const Rect &rect, const Point &point); 131 static bool ComputeIntersection(const Rect &ra, const Rect &rb, Rect &result); 132 static bool ComputeMaxVisibleRegion(const Rect &rect, const std::vector<Rect> &overlays, Rect &out); 133 }; 134 CheckEqual(const Rect & ra,const Rect & rb)135 FORCE_INLINE bool RectAlgorithm::CheckEqual(const Rect &ra, const Rect &rb) 136 { 137 return ra.left_ == rb.left_ && ra.right_ == rb.right_ && ra.top_ == rb.top_ && ra.bottom_ == rb.bottom_; 138 } 139 CheckIntersectant(const Rect & ra,const Rect & rb)140 FORCE_INLINE bool RectAlgorithm::CheckIntersectant(const Rect &ra, const Rect &rb) 141 { 142 if (ra.left_ >= rb.right_ || ra.right_ <= rb.left_) { 143 return false; 144 } 145 if (ra.top_ >= rb.bottom_ || ra.bottom_ <= rb.top_) { 146 return false; 147 } 148 return true; 149 } 150 IsInnerPoint(const Rect & rect,const Point & point)151 FORCE_INLINE bool RectAlgorithm::IsInnerPoint(const Rect &rect, const Point& point) 152 { 153 if (point.px_ <= rect.left_ || point.px_ >= rect.right_ 154 || point.py_ <= rect.top_ || point.py_ >= rect.bottom_) { 155 return false; 156 } 157 return true; 158 } 159 IsPointOnEdge(const Rect & rect,const Point & point)160 FORCE_INLINE bool RectAlgorithm::IsPointOnEdge(const Rect &rect, const Point& point) 161 { 162 if ((point.px_ == rect.left_ || point.px_ == rect.right_) 163 && point.py_ >= rect.top_ && point.py_ <= rect.bottom_) { 164 return true; 165 } 166 if ((point.py_ == rect.top_ || point.py_ == rect.bottom_) 167 && point.px_ >= rect.left_ && point.px_ <= rect.right_) { 168 return true; 169 } 170 return false; 171 } 172 173 class Widget : public BackendClass { 174 public: 175 // disable default constructor, copy constructor and assignment operator Widget(std::string_view hierarchy)176 explicit Widget(std::string_view hierarchy) : hierarchy_(hierarchy) 177 { 178 attributes_.insert(std::make_pair(ATTR_NAMES[UiAttr::HIERARCHY], hierarchy)); 179 }; 180 ~Widget()181 ~Widget() override {} 182 GetFrontendClassDef()183 const FrontEndClassDef &GetFrontendClassDef() const override 184 { 185 return COMPONENT_DEF; 186 } 187 188 bool HasAttr(std::string_view name) const; 189 190 std::string GetAttr(std::string_view name, std::string_view defaultVal) const; 191 GetBounds()192 Rect GetBounds() const 193 { 194 return bounds_; 195 } 196 GetHierarchy()197 std::string GetHierarchy() const 198 { 199 return hierarchy_; 200 } 201 202 void SetAttr(std::string_view name, std::string_view value); 203 204 void SetHostTreeId(std::string_view tid); 205 206 std::string GetHostTreeId() const; 207 208 void SetBounds(const Rect &bounds); 209 210 std::string ToStr() const; 211 212 std::unique_ptr<Widget> Clone(std::string_view hostTreeId, std::string_view hierarchy) const; 213 214 std::map<std::string, std::string> GetAttrMap() const; 215 IsVisible()216 bool IsVisible() const 217 { 218 return GetAttr(ATTR_NAMES[UiAttr::VISIBLE], "") == "true"; 219 } 220 221 private: 222 const std::string hierarchy_; 223 std::string hostTreeId_; 224 std::map<std::string, std::string> attributes_; 225 Rect bounds_ = {0, 0, 0, 0}; 226 }; 227 228 // ensure Widget is movable, since we need to move a constructed Widget object into WidgetTree 229 static_assert(std::is_move_constructible<Widget>::value, "Widget need to be movable"); 230 231 class WidgetVisitor { 232 public: 233 virtual void Visit(const Widget &widget) = 0; 234 }; 235 236 class WidgetTree { 237 public: 238 WidgetTree() = delete; 239 ~WidgetTree()240 ~WidgetTree() {} 241 WidgetTree(std::string name)242 explicit WidgetTree(std::string name) : name_(std::move(name)), identifier_(GenerateTreeId()) {} 243 244 /** 245 * Construct tree nodes from the given dom data. 246 * 247 * @param dom: the dom json data. 248 * @param amendBounds: if or not amend widget bounds and visibility. 249 * */ 250 void ConstructFromDom(const nlohmann::json &dom, bool amendBounds); 251 252 /** 253 * Marshal tree nodes hierarchy into the given dom data. 254 * 255 * @param dom: the dom json data. 256 * */ 257 void MarshalIntoDom(nlohmann::json &dom) const; 258 259 void DfsTraverse(WidgetVisitor &visitor) const; 260 261 void DfsTraverseFronts(WidgetVisitor &visitor, const Widget &pivot) const; 262 263 void DfsTraverseRears(WidgetVisitor &visitor, const Widget &pivot) const; 264 265 void DfsTraverseParents(WidgetVisitor &visitor, const Widget &pivot) const; 266 267 void DfsTraverseDescendants(WidgetVisitor &visitor, const Widget &root) const; 268 269 /** 270 * Get the root widget node. 271 * 272 * @return the root widget node, or <code>nullptr</code> if the tree is empty or not constructed. 273 * */ 274 const Widget *GetRootWidget() const; 275 276 /** 277 * Get the parent widget node of the given widget 278 * 279 * @param widget: the child widget. 280 * @returns the parent widget pointer, <code>nullptr</code> if the given node it's the root node. 281 * */ 282 const Widget *GetParentWidget(const Widget &widget) const; 283 284 /** 285 * Get the child widget node of the given widget at the given index. 286 * 287 * @param widget: the parent widget. 288 * @param index: the child widget index, <b>starts from 0</b>. 289 * @returns the child widget pointer, or <code>nullptr</code> if there's no such child. 290 * */ 291 const Widget *GetChildWidget(const Widget &widget, uint32_t index) const; 292 293 /**Check if the given widget node hierarchy is the root node hierarchy.*/ 294 static bool IsRootWidgetHierarchy(std::string_view hierarchy); 295 296 /** 297 * Merge several tree into one tree. 298 * 299 * @param from: the subtrees to merge, should be sorted by z-order and the top one be at first. 300 * @param to: the root tree to merge into. 301 * @param mergedOrders: receive the indexes of subtree merged into the root. 302 * */ 303 static void MergeTrees(const std::vector<std::unique_ptr<WidgetTree>> &from, WidgetTree &to, 304 vector<int32_t> &mergedOrders); 305 306 private: 307 const std::string name_; 308 const std::string identifier_; 309 bool widgetsConstructed_ = false; 310 // widget-hierarchy VS widget-ptr 311 std::map<std::string, Widget> widgetMap_; 312 // widget-hierarchies, dfs order 313 std::vector<std::string> widgetHierarchyIdDfsOrder_; 314 315 /** 316 * Get WidgetTree by hierarchy,return <code>nullptr</code> if no such widget exist. 317 * */ 318 const Widget *GetWidgetByHierarchy(std::string_view hierarchy) const; 319 320 /**Check if the given widget is in this tree.*/ 321 inline bool CheckIsMyNode(const Widget &widget) const; 322 323 /**Generated an unique tree-identifier.*/ 324 static std::string GenerateTreeId(); 325 326 void EnsureParentVisible(const Widget &widget); 327 }; 328 329 class TreeSnapshotTaker : public WidgetVisitor { 330 public: TreeSnapshotTaker(vector<string> & displayNodes,vector<string> & allNodes)331 explicit TreeSnapshotTaker(vector<string> &displayNodes, vector<string> &allNodes) 332 : displayNodes_(displayNodes), allNodes_(allNodes) {}; 333 ~TreeSnapshotTaker()334 ~TreeSnapshotTaker() {} 335 336 void Visit(const Widget &widget) override; 337 private: 338 vector<string> &displayNodes_; 339 vector<string> &allNodes_; 340 }; 341 342 /**Enumerates the supported UiComponent attributes.*/ 343 enum WindowMode : uint8_t { UNKNOWN, FULLSCREEN, SPLIT_PRIMARY, SPLIT_SECONDARY, FLOATING, PIP }; 344 345 // Represents a UI window on screen. 346 class Window : public BackendClass { 347 public: Window(int32_t id)348 explicit Window(int32_t id) : id_(id) {}; 349 ~Window()350 ~Window() override {} 351 GetFrontendClassDef()352 const FrontEndClassDef &GetFrontendClassDef() const override 353 { 354 return UI_WINDOW_DEF; 355 } 356 // plain properties, make them public for easy access 357 int32_t id_ = 0; 358 std::string bundleName_ = ""; 359 std::string title_ = ""; 360 bool focused_ = false; 361 bool actived_ = false; 362 bool decoratorEnabled_ = false; 363 Rect bounds_ = {0, 0, 0, 0}; 364 Rect visibleBounds_ = {0, 0, 0, 0}; 365 WindowMode mode_ = UNKNOWN; 366 }; 367 } // namespace OHOS::uitest 368 369 #endif