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 /**Enumerates the supported UiComponent attributes.*/ 27 enum UiAttr : uint8_t { 28 ACCESSIBILITY_ID, 29 ID, 30 TEXT, 31 KEY, 32 TYPE, 33 BOUNDS, 34 ENABLED, 35 FOCUSED, 36 SELECTED, 37 CLICKABLE, 38 LONG_CLICKABLE, 39 SCROLLABLE, 40 CHECKABLE, 41 CHECKED, 42 // inner used attributes 43 HIERARCHY, 44 HASHCODE, 45 BOUNDSCENTER, 46 HOST_WINDOW_ID, 47 }; 48 49 /**Supported UiComponent attribute names. Ordered by <code>UiAttr</code> definition.*/ 50 constexpr std::string_view ATTR_NAMES[] = { 51 "accessibilityId", // ACCESSIBILITY_ID 52 "id", // ID 53 "text", // TEXT 54 "key", // KEY 55 "type", // TYPE 56 "bounds", // BOUNDS 57 "enabled", // ENABLED 58 "focused", // FOCUSED 59 "selected", // SELECTED 60 "clickable", // CLICKABLE 61 "longClickable", // LONG_CLICKABLE 62 "scrollable", // SCROLLABLE 63 "checkable", // CHECKABLE 64 "checked", // CHECKED 65 "hierarchy", // HIERARCHY 66 "hashcode", // HASHCODE 67 "boundsCenter", // BOUNDSCENTER 68 "hostWindowId", // HOST_WINDOW_ID 69 }; 70 71 struct Point { PointPoint72 Point() : px_(0), py_(0) {}; PointPoint73 Point(int32_t px, int32_t py) : px_(px), py_(py) {}; 74 int32_t px_; 75 int32_t py_; 76 }; 77 78 /**Represents a reasonable rectangle area.*/ 79 struct Rect { RectRect80 Rect(int32_t left, int32_t right, int32_t top, int32_t bottom) 81 : left_(left), right_(right), top_(top), bottom_(bottom) 82 { 83 DCHECK(right_ >= left_ && bottom_ >= top_); 84 }; 85 86 int32_t left_; 87 int32_t right_; 88 int32_t top_; 89 int32_t bottom_; 90 GetCenterXRect91 FORCE_INLINE int32_t GetCenterX() const 92 { 93 return (left_ + right_) / TWO; 94 } 95 GetCenterYRect96 FORCE_INLINE int32_t GetCenterY() const 97 { 98 return (top_ + bottom_) / TWO; 99 } 100 GetWidthRect101 FORCE_INLINE int32_t GetWidth() const 102 { 103 return right_ - left_; 104 } 105 GetHeightRect106 FORCE_INLINE int32_t GetHeight() const 107 { 108 return bottom_ - top_; 109 } 110 }; 111 112 /**Algorithm of rectangle.*/ 113 class RectAlgorithm { 114 public: 115 FORCE_INLINE static bool CheckEqual(const Rect &ra, const Rect &rb); 116 FORCE_INLINE static bool CheckIntersectant(const Rect &ra, const Rect &rb); 117 FORCE_INLINE static bool IsInnerPoint(const Rect &rect, const Point &point); 118 FORCE_INLINE static bool IsPointOnEdge(const Rect &rect, const Point &point); 119 static bool ComputeIntersection(const Rect &ra, const Rect &rb, Rect &result); 120 static bool ComputeMaxVisibleRegion(const Rect &rect, const std::vector<Rect> &overlays, Rect &out); 121 }; 122 CheckEqual(const Rect & ra,const Rect & rb)123 FORCE_INLINE bool RectAlgorithm::CheckEqual(const Rect &ra, const Rect &rb) 124 { 125 return ra.left_ == rb.left_ && ra.right_ == rb.right_ && ra.top_ == rb.top_ && ra.bottom_ == rb.bottom_; 126 } 127 CheckIntersectant(const Rect & ra,const Rect & rb)128 FORCE_INLINE bool RectAlgorithm::CheckIntersectant(const Rect &ra, const Rect &rb) 129 { 130 if (ra.left_ >= rb.right_ || ra.right_ <= rb.left_) { 131 return false; 132 } 133 if (ra.top_ >= rb.bottom_ || ra.bottom_ <= rb.top_) { 134 return false; 135 } 136 return true; 137 } 138 IsInnerPoint(const Rect & rect,const Point & point)139 FORCE_INLINE bool RectAlgorithm::IsInnerPoint(const Rect &rect, const Point& point) 140 { 141 if (point.px_ <= rect.left_ || point.px_ >= rect.right_ 142 || point.py_ <= rect.top_ || point.py_ >= rect.bottom_) { 143 return false; 144 } 145 return true; 146 } 147 IsPointOnEdge(const Rect & rect,const Point & point)148 FORCE_INLINE bool RectAlgorithm::IsPointOnEdge(const Rect &rect, const Point& point) 149 { 150 if ((point.px_ == rect.left_ || point.px_ == rect.right_) 151 && point.py_ >= rect.top_ && point.py_ <= rect.bottom_) { 152 return true; 153 } 154 if ((point.py_ == rect.top_ || point.py_ == rect.bottom_) 155 && point.px_ >= rect.left_ && point.px_ <= rect.right_) { 156 return true; 157 } 158 return false; 159 } 160 161 class Widget : public BackendClass { 162 public: 163 // disable default constructor, copy constructor and assignment operator Widget(std::string_view hierarchy)164 explicit Widget(std::string_view hierarchy) : hierarchy_(hierarchy) 165 { 166 attributes_.insert(std::make_pair(ATTR_NAMES[UiAttr::HIERARCHY], hierarchy)); 167 }; 168 ~Widget()169 ~Widget() override {} 170 GetFrontendClassDef()171 const FrontEndClassDef &GetFrontendClassDef() const override 172 { 173 return COMPONENT_DEF; 174 } 175 176 bool HasAttr(std::string_view name) const; 177 178 std::string GetAttr(std::string_view name, std::string_view defaultVal) const; 179 GetBounds()180 Rect GetBounds() const 181 { 182 return bounds_; 183 } 184 GetHierarchy()185 std::string GetHierarchy() const 186 { 187 return hierarchy_; 188 } 189 190 void SetAttr(std::string_view name, std::string_view value); 191 192 void SetHostTreeId(std::string_view tid); 193 194 std::string GetHostTreeId() const; 195 196 void SetBounds(const Rect &bounds); 197 198 std::string ToStr() const; 199 200 std::unique_ptr<Widget> Clone(std::string_view hostTreeId, std::string_view hierarchy) const; 201 202 std::map<std::string, std::string> GetAttrMap() const; 203 204 private: 205 const std::string hierarchy_; 206 std::string hostTreeId_; 207 std::map<std::string, std::string> attributes_; 208 Rect bounds_ = {0, 0, 0, 0}; 209 }; 210 211 // ensure Widget is movable, since we need to move a constructed Widget object into WidgetTree 212 static_assert(std::is_move_constructible<Widget>::value, "Widget need to be movable"); 213 214 class WidgetVisitor { 215 public: 216 virtual void Visit(const Widget &widget) = 0; 217 }; 218 219 class WidgetTree { 220 public: 221 WidgetTree() = delete; 222 ~WidgetTree()223 ~WidgetTree() {} 224 WidgetTree(std::string name)225 explicit WidgetTree(std::string name) : name_(std::move(name)), identifier_(GenerateTreeId()) {} 226 227 /** 228 * Construct tree nodes from the given dom data. 229 * 230 * @param dom: the dom json data. 231 * @param amendBounds: if or not amend widget bounds and visibility. 232 * */ 233 void ConstructFromDom(const nlohmann::json &dom, bool amendBounds); 234 235 /** 236 * Marshal tree nodes hierarchy into the given dom data. 237 * 238 * @param dom: the dom json data. 239 * */ 240 void MarshalIntoDom(nlohmann::json &dom) const; 241 242 void DfsTraverse(WidgetVisitor &visitor) const; 243 244 void DfsTraverseFronts(WidgetVisitor &visitor, const Widget &pivot) const; 245 246 void DfsTraverseRears(WidgetVisitor &visitor, const Widget &pivot) const; 247 248 void DfsTraverseDescendants(WidgetVisitor &visitor, const Widget &root) const; 249 250 /** 251 * Get the root widget node. 252 * 253 * @return the root widget node, or <code>nullptr</code> if the tree is empty or not constructed. 254 * */ 255 const Widget *GetRootWidget() const; 256 257 /** 258 * Get the parent widget node of the given widget 259 * 260 * @param widget: the child widget. 261 * @returns the parent widget pointer, <code>nullptr</code> if the given node it's the root node. 262 * */ 263 const Widget *GetParentWidget(const Widget &widget) const; 264 265 /** 266 * Get the child widget node of the given widget at the given index. 267 * 268 * @param widget: the parent widget. 269 * @param index: the child widget index, <b>starts from 0</b>. 270 * @returns the child widget pointer, or <code>nullptr</code> if there's no such child. 271 * */ 272 const Widget *GetChildWidget(const Widget &widget, uint32_t index) const; 273 274 /**Check if the given widget node hierarchy is the root node hierarchy.*/ 275 static bool IsRootWidgetHierarchy(std::string_view hierarchy); 276 277 /** 278 * Merge several tree into one tree. 279 * 280 * @param from: the subtrees to merge, should be sorted by z-order and the top one be at first. 281 * @param to: the root tree to merge into. 282 * */ 283 static void MergeTrees(const std::vector<std::unique_ptr<WidgetTree>> &from, WidgetTree &to); 284 285 private: 286 const std::string name_; 287 const std::string identifier_; 288 bool widgetsConstructed_ = false; 289 // widget-hierarchy VS widget-ptr 290 std::map<std::string, Widget> widgetMap_; 291 // widget-hierarchies, dfs order 292 std::vector<std::string> widgetHierarchyIdDfsOrder_; 293 294 /** 295 * Get WidgetTree by hierarchy,return <code>nullptr</code> if no such widget exist. 296 * */ 297 const Widget *GetWidgetByHierarchy(std::string_view hierarchy) const; 298 299 /**Check if the given widget is in this tree.*/ 300 inline bool CheckIsMyNode(const Widget &widget) const; 301 302 /**Generated an unique tree-identifier.*/ 303 static std::string GenerateTreeId(); 304 }; 305 306 /**Enumerates the supported UiComponent attributes.*/ 307 enum WindowMode : uint8_t { UNKNOWN, FULLSCREEN, SPLIT_PRIMARY, SPLIT_SECONDARY, FLOATING, PIP }; 308 309 // Represents a UI window on screen. 310 class Window : public BackendClass { 311 public: Window(int32_t id)312 explicit Window(int32_t id) : id_(id) {}; 313 ~Window()314 ~Window() override {} 315 GetFrontendClassDef()316 const FrontEndClassDef &GetFrontendClassDef() const override 317 { 318 return UI_WINDOW_DEF; 319 } 320 // plain properties, make them public for easy access 321 int32_t id_ = 0; 322 std::string bundleName_ = ""; 323 std::string title_ = ""; 324 bool focused_ = false; 325 bool actived_ = false; 326 bool decoratorEnabled_ = false; 327 Rect bounds_ = {0, 0, 0, 0}; 328 WindowMode mode_ = UNKNOWN; 329 }; 330 } // namespace OHOS::uitest 331 332 #endif