2 * Copyright(c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 extern alias TizenSystemInformation;
18 using TizenSystemInformation.Tizen.System;
21 using System.Collections.Generic;
22 using System.ComponentModel;
24 using System.Threading;
25 using Tizen.NUI.BaseComponents;
29 public partial class Window
31 #region Constant Fields
32 #endregion //Constant Fields
35 private IBorderInterface borderInterface = null;
36 private Layer borderWindowRootLayer = null;
37 private Layer borderWindowBottomLayer = null;
38 private bool isBorderWindow = false;
41 private View rootView = null;
42 private BorderView borderView = null;
43 private View topView = null;
44 private View contentsView = null;
45 private View bottomView = null;
46 private bool isTop = false;
47 private bool isBottom = false;
48 private float borderHeight = 0;
49 private int screenWidth = 0;
50 private int screenHeight = 0;
53 private Size2D minSize = null;
54 private Size2D maxSize = null;
55 private BorderResizePolicyType borderResizePolicy = BorderResizePolicyType.Free;
59 #endregion //Constructors
62 #endregion //Distructors
65 internal delegate void BorderCloseDelegate();
66 private BorderCloseDelegate borderCloseDelegate = null;
68 #endregion //Delegates
75 /// This is an enum for the resize direction or move value when the border area is touched.
77 [EditorBrowsable(EditorBrowsableState.Never)]
78 public enum BorderDirection
80 None = ResizeDirection.None,
81 TopLeft = ResizeDirection.TopLeft,
82 Top = ResizeDirection.Top,
83 TopRight = ResizeDirection.TopRight,
84 Left = ResizeDirection.Left,
85 Right = ResizeDirection.Right,
86 BottomLeft = ResizeDirection.BottomLeft,
87 Bottom = ResizeDirection.Bottom,
88 BottomRight = ResizeDirection.BottomRight,
93 /// This enum is the policy when resizing the border window.
95 [EditorBrowsable(EditorBrowsableState.Never)]
96 public enum BorderResizePolicyType
99 /// The window can be resized freely.
103 /// The window is resized according to the ratio.
107 /// The window is not resized and is fixed.
114 #endregion //Interfaces
118 /// Whether the border is enabled.
120 [EditorBrowsable(EditorBrowsableState.Never)]
121 public bool IsBorderEnabled => isBorderWindow;
122 #endregion //Properties
125 #endregion //Indexers
130 /// Update BorderProperty
132 internal void UpdateProperty()
134 if (borderInterface != null)
137 if (isTop) height += topView.SizeHeight;
138 if (isBottom) height += bottomView.SizeHeight;
140 if (height != borderHeight)
142 float diff = height - borderHeight;
143 borderHeight = height;
144 WindowSize = new Size2D(WindowSize.Width, WindowSize.Height + (int)(diff));
147 if (minSize != borderInterface.MinSize)
149 using Size2D mimimumSize = new Size2D(borderInterface.MinSize.Width, borderInterface.MinSize.Height + (int)borderHeight);
150 SetMimimumSize(mimimumSize);
151 minSize = borderInterface.MinSize;
153 if (maxSize != borderInterface.MaxSize)
155 using Size2D maximumSize = new Size2D(borderInterface.MaxSize.Width, borderInterface.MaxSize.Height + (int)borderHeight);
156 SetMaximumSize(maximumSize);
157 maxSize = borderInterface.MaxSize;
159 if (borderResizePolicy != borderInterface.ResizePolicy)
161 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "0");
162 borderResizePolicy = borderInterface.ResizePolicy;
163 if (borderResizePolicy == BorderResizePolicyType.KeepRatio)
165 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
171 /// Called when the border is closed.
172 /// If the delegate is declared, the delegate is called, otherwise window is destroyed.
174 internal void BorderDestroy()
176 if (borderCloseDelegate != null)
178 borderCloseDelegate();
186 /// Enable the border window with IBorderInterface.
187 /// This adds a border area to the Window.
188 /// The border's UI is configured using IBorderInterface.
189 /// Users can reisze and move by touching the border area.
191 /// <param name="borderInterface">The IBorderInterface.</param>
192 /// <param name="borderCloseDelegate">The BorderCloseDelegate. When close, this delegate is called.</param>
193 /// <returns>Whether the border window is enabled</returns>
194 internal bool EnableBorder(IBorderInterface borderInterface, BorderCloseDelegate borderCloseDelegate = null)
196 if (isBorderWindow == true)
198 Tizen.Log.Error("NUI", $"Already EnableBorderWindow\n");
204 Information.TryGetValue<int>("http://tizen.org/feature/screen.width", out screenWidth);
205 Information.TryGetValue<int>("http://tizen.org/feature/screen.height", out screenHeight);
207 catch (DllNotFoundException e)
209 Tizen.Log.Fatal("NUI", $"{e}\n");
212 if (borderInterface == null)
214 borderInterface = new DefaultBorder();
216 this.borderInterface = borderInterface;
217 this.borderCloseDelegate = borderCloseDelegate;
219 GetDefaultLayer().Name = "OriginalRootLayer";
221 SetTransparency(true);
222 BackgroundColor = Color.Transparent;
223 borderInterface.BorderWindow = this;
225 if (CreateBorder() == true)
227 using var realWindowSize = new Size2D(WindowSize.Width, WindowSize.Height);
229 isBorderWindow = true;
231 Resized += OnBorderWindowResized;
233 Moved += OnBorderWindowMoved;
235 borderInterface.OnCreated(borderView);
237 // Increase the window size as much as the border area.
239 if (isTop) borderHeight += topView.SizeHeight;
240 if (isBottom) borderHeight += bottomView.SizeHeight;
242 // Sets the minimum / maximum size to be resized by RequestResizeToServer.
243 if (borderInterface.MinSize != null)
245 using Size2D mimimumSize = new Size2D(borderInterface.MinSize.Width, borderInterface.MinSize.Height + (int)borderHeight);
246 SetMimimumSize(mimimumSize);
248 if (borderInterface.MaxSize != null)
250 using Size2D maximumSize = new Size2D(borderInterface.MaxSize.Width, borderInterface.MaxSize.Height + (int)borderHeight);
251 SetMaximumSize(maximumSize);
254 // When running the app for the first time, if it runs in full size, do Maximize(true).
255 if (screenWidth != 0 && screenHeight != 0 &&
256 realWindowSize.Width >= screenWidth && realWindowSize.Height >= screenHeight &&
257 IsMaximized() == false)
260 borderInterface.OnMaximize(true);
261 ResizedEventArgs e = new ResizedEventArgs();
262 e.WindowSize = WindowSize;
263 OnBorderWindowResized(this, e);
267 WindowSize += new Size2D((int)borderInterface.BorderLineThickness * 2, (int)(borderHeight + borderInterface.BorderLineThickness * 2));
270 // If it is BorderResizePolicyType.KeepRatio type, it will be resized according to the ratio.
271 if (borderInterface.ResizePolicy == BorderResizePolicyType.KeepRatio)
273 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
276 // Add a view to the border layer.
277 GetBorderWindowBottomLayer().Add(rootView);
279 FocusChanged += OnWindowFocusChanged;
285 this.borderInterface.Dispose();
290 private void OnWindowFocusChanged(object sender, Window.FocusChangedEventArgs e)
292 if (e.FocusGained == true && IsMaximized() == false)
294 // Raises the window when the window is focused.
299 /// Create the border UI.
300 private bool CreateBorder()
302 rootView = new View()
304 WidthResizePolicy = ResizePolicyType.FillToParent,
305 HeightResizePolicy = ResizePolicyType.FillToParent,
306 BackgroundColor = Color.Transparent,
309 ushort padding = (ushort) borderInterface.BorderLineThickness;
310 borderView = new BorderView()
312 GrabTouchAfterLeave = true,
313 WidthResizePolicy = ResizePolicyType.FillToParent,
314 HeightResizePolicy = ResizePolicyType.FillToParent,
315 BackgroundColor = Color.Transparent,
316 Layout = new LinearLayout() {
317 LinearOrientation = LinearLayout.Orientation.Vertical,
318 LinearAlignment = LinearLayout.Alignment.Top
320 Padding = new Extents(padding, padding, padding, padding),
322 borderInterface.CreateBorderView(borderView);
326 WidthSpecification = LayoutParamPolicies.MatchParent,
327 SizeHeight = borderInterface.BorderHeight,
328 BackgroundColor = Color.Transparent,
331 contentsView = new View()
333 BackgroundColor = Color.Transparent,
334 WidthSpecification = LayoutParamPolicies.MatchParent,
337 bottomView = new View()
339 WidthSpecification = LayoutParamPolicies.MatchParent,
340 SizeHeight = borderInterface.BorderHeight,
341 BackgroundColor = Color.Transparent,
344 // // Gets the Border's UI.
345 if (borderInterface.CreateTopBorderView(topView) == true && topView != null)
347 borderView.Add(topView);
350 borderView.Add(contentsView);
351 if (borderInterface.CreateBottomBorderView(bottomView) == true && bottomView != null)
353 borderView.Add(bottomView);
356 rootView.Add(borderView);
358 return isTop || isBottom;
362 /// Calculates which direction to resize or to move.
364 /// <param name="xPosition">The X position.</param>
365 /// <param name="yPosition">The Y position.</param>
366 /// <returns>The BorderDirection</returns>
367 [EditorBrowsable(EditorBrowsableState.Never)]
368 public BorderDirection GetDirection(float xPosition, float yPosition)
370 BorderDirection direction = BorderDirection.None;
372 // check bottom left corner
373 if (xPosition < borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
375 direction = BorderDirection.BottomLeft;
377 // check bottom right corner
378 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
380 direction = BorderDirection.BottomRight;
382 // check top left corner
383 else if (xPosition < borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
385 direction = BorderDirection.TopLeft;
387 // check top right corner
388 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
390 direction = BorderDirection.TopRight;
393 else if (xPosition < borderInterface.TouchThickness)
395 direction = BorderDirection.Left;
398 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness)
400 direction = BorderDirection.Right;
403 else if (yPosition > WindowSize.Height + borderHeight + borderInterface.BorderLineThickness - borderInterface.TouchThickness)
405 direction = BorderDirection.Bottom;
408 else if (yPosition < borderInterface.TouchThickness)
410 direction = BorderDirection.Top;
413 else if ((yPosition > WindowSize.Height) || (isTop == true && yPosition < topView.SizeHeight))
415 direction = BorderDirection.Move;
421 private void DoOverlayMode(bool enable)
423 if (borderInterface.OverlayMode == true)
425 borderInterface.OnOverlayMode(enable);
426 borderView?.OverlayMode(enable);
429 GetBorderWindowBottomLayer().RaiseToTop();
433 GetBorderWindowBottomLayer().LowerToBottom();
438 // Called when the window position has changed.
439 private void OnBorderWindowMoved(object sender, WindowMovedEventArgs e)
441 Tizen.Log.Info("NUI", $"OnBorderWindowMoved {e.WindowPosition.X}, {e.WindowPosition.Y}\n");
442 borderInterface.OnMoved(e.WindowPosition.X, e.WindowPosition.X);
446 // Called when the window size has changed.
447 private void OnBorderWindowResized(object sender, Window.ResizedEventArgs e)
449 Tizen.Log.Info("NUI", $"OnBorderWindowResized {e.WindowSize.Width},{e.WindowSize.Height}\n");
450 int resizeWidth = e.WindowSize.Width;
451 int resizeHeight = e.WindowSize.Height;
453 borderInterface.OnResized(resizeWidth, resizeHeight);
455 // reset borderHeight
457 if (isTop) borderHeight += topView.SizeHeight;
458 if (isBottom) borderHeight += bottomView.SizeHeight;
460 if (borderInterface.OverlayMode == true && IsMaximized() == true)
462 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
463 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth, resizeHeight);
464 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, 0);
465 if (contentsView != null)
467 contentsView.SizeHeight = resizeHeight - borderHeight - (float)(borderInterface.BorderLineThickness * 2);
473 float height = (isTop == true) ? topView.SizeHeight : 0;
474 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
475 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth + (float)(borderInterface.BorderLineThickness * 2), resizeHeight + borderHeight + (float)(borderInterface.BorderLineThickness * 2));
476 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, height + borderInterface.BorderLineThickness);
477 if (contentsView != null)
479 contentsView.SizeHeight = resizeHeight;
481 DoOverlayMode(false);
484 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
487 internal Layer GetBorderWindowBottomLayer()
489 if (borderWindowBottomLayer == null)
491 borderWindowBottomLayer = new Layer();
492 borderWindowBottomLayer.Name = "BorderWindowBottomLayer";
493 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
494 Interop.ActorInternal.SetParentOrigin(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
495 Interop.Actor.SetAnchorPoint(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
496 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowBottomLayer.SwigCPtr);
497 Interop.ActorInternal.SetSize(borderWindowBottomLayer.SwigCPtr, WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2), WindowSize.Height + (float)(borderInterface.BorderLineThickness * 2));
498 borderWindowBottomLayer.SetWindow(this);
499 borderWindowBottomLayer.LowerToBottom();
501 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
503 return borderWindowBottomLayer;
506 internal Layer GetBorderWindowRootLayer()
508 if (borderWindowRootLayer == null)
510 borderWindowRootLayer = new Layer();
511 borderWindowRootLayer.Name = "RootLayer";
512 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
513 Interop.ActorInternal.SetParentOrigin(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
514 Interop.Actor.SetAnchorPoint(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
515 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowRootLayer.SwigCPtr);
516 Interop.ActorInternal.SetSize(borderWindowRootLayer.SwigCPtr, WindowSize.Width, WindowSize.Height - borderHeight - borderInterface.BorderLineThickness * 2);
517 float height = (isTop == true) ? topView.SizeHeight : 0;
518 Interop.ActorInternal.SetPosition(borderWindowRootLayer.SwigCPtr, 0, height + borderInterface.BorderLineThickness);
519 using PropertyValue propertyValue = new Tizen.NUI.PropertyValue((int)Tizen.NUI.ClippingModeType.ClipToBoundingBox);
520 Tizen.NUI.Object.SetProperty(borderWindowRootLayer.SwigCPtr, Tizen.NUI.BaseComponents.View.Property.ClippingMode, propertyValue);
522 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
525 return borderWindowRootLayer;
528 internal void DisposeBorder()
530 Resized -= OnBorderWindowResized;
531 FocusChanged -= OnWindowFocusChanged;
532 borderInterface.Dispose();
533 GetBorderWindowBottomLayer().Dispose();
536 private void convertBorderWindowSizeToRealWindowSize(Uint16Pair size)
538 if (isBorderWindow == true)
540 var height = (ushort)(size.GetHeight() + borderHeight + borderInterface.BorderLineThickness * 2);
541 var width = (ushort)(size.GetWidth() + borderInterface.BorderLineThickness * 2);
542 size.SetHeight(height);
543 size.SetWidth(width);
547 private void convertRealWindowSizeToBorderWindowSize(Uint16Pair size)
549 if (isBorderWindow == true && !(borderInterface.OverlayMode == true && IsMaximized() == true))
551 var height = (ushort)(size.GetHeight() - borderHeight - borderInterface.BorderLineThickness * 2);
552 var width = (ushort)(size.GetWidth() - borderInterface.BorderLineThickness * 2);
553 size.SetHeight(height);
554 size.SetWidth(width);
563 // View class for border view.
564 private class BorderView : View
566 private bool overlayEnabled = false;
569 /// Set whether or not it is in overlay mode.
570 /// The borderView's OverlayMode means that it is displayed at the top of the screen.
571 /// In this case, the borderView should pass the event so that lower layers can receive the event.
573 /// <param name="enabled">The true if borderView is Overlay mode. false otherwise.</param>
574 [EditorBrowsable(EditorBrowsableState.Never)]
575 public void OverlayMode(bool enabled)
577 overlayEnabled = enabled;
580 protected override bool HitTest(Touch touch)
582 // If borderView is in overlay mode, pass the hittest.
583 if (overlayEnabled == true)