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)
136 if (minSize != borderInterface.MinSize)
138 using Size2D mimimumSize = new Size2D(borderInterface.MinSize.Width, borderInterface.MinSize.Height + (int)borderHeight);
139 SetMimimumSize(mimimumSize);
140 minSize = borderInterface.MinSize;
142 if (maxSize != borderInterface.MaxSize)
144 using Size2D maximumSize = new Size2D(borderInterface.MaxSize.Width, borderInterface.MaxSize.Height + (int)borderHeight);
145 SetMaximumSize(maximumSize);
146 maxSize = borderInterface.MaxSize;
148 if (borderResizePolicy != borderInterface.ResizePolicy)
150 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "0");
151 borderResizePolicy = borderInterface.ResizePolicy;
152 if (borderResizePolicy == BorderResizePolicyType.KeepRatio)
154 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
160 /// Called when the border is closed.
161 /// If the delegate is declared, the delegate is called, otherwise window is destroyed.
163 internal void BorderDestroy()
165 if (borderCloseDelegate != null)
167 borderCloseDelegate();
175 /// Enable the border window with IBorderInterface.
176 /// This adds a border area to the Window.
177 /// The border's UI is configured using IBorderInterface.
178 /// Users can reisze and move by touching the border area.
180 /// <param name="borderInterface">The IBorderInterface.</param>
181 /// <param name="borderCloseDelegate">The BorderCloseDelegate. When close, this delegate is called.</param>
182 /// <returns>Whether the border window is enabled</returns>
183 internal bool EnableBorder(IBorderInterface borderInterface, BorderCloseDelegate borderCloseDelegate = null)
185 if (isBorderWindow == true)
187 Tizen.Log.Error("NUI", $"Already EnableBorderWindow\n");
193 Information.TryGetValue<int>("http://tizen.org/feature/screen.width", out screenWidth);
194 Information.TryGetValue<int>("http://tizen.org/feature/screen.height", out screenHeight);
196 catch (DllNotFoundException e)
198 Tizen.Log.Fatal("NUI", $"{e}\n");
201 if (borderInterface == null)
203 borderInterface = new DefaultBorder();
205 this.borderInterface = borderInterface;
206 this.borderCloseDelegate = borderCloseDelegate;
208 GetDefaultLayer().Name = "OriginalRootLayer";
210 SetTransparency(true);
211 BackgroundColor = Color.Transparent;
212 borderInterface.BorderWindow = this;
214 if (CreateBorder() == true)
216 using var realWindowSize = new Size2D(WindowSize.Width, WindowSize.Height);
218 isBorderWindow = true;
220 Resized += OnBorderWindowResized;
222 borderInterface.OnCreated(borderView);
224 // Increase the window size as much as the border area.
226 if (isTop) borderHeight += topView.SizeHeight;
227 if (isBottom) borderHeight += bottomView.SizeHeight;
229 // Sets the minimum / maximum size to be resized by RequestResizeToServer.
230 if (borderInterface.MinSize != null)
232 using Size2D mimimumSize = new Size2D(borderInterface.MinSize.Width, borderInterface.MinSize.Height + (int)borderHeight);
233 SetMimimumSize(mimimumSize);
235 if (borderInterface.MaxSize != null)
237 using Size2D maximumSize = new Size2D(borderInterface.MaxSize.Width, borderInterface.MaxSize.Height + (int)borderHeight);
238 SetMaximumSize(maximumSize);
241 // When running the app for the first time, if it runs in full size, do Maximize(true).
242 if (screenWidth != 0 && screenHeight != 0 &&
243 realWindowSize.Width >= screenWidth && realWindowSize.Height >= screenHeight &&
244 IsMaximized() == false)
247 borderInterface.OnMaximize(true);
248 ResizedEventArgs e = new ResizedEventArgs();
249 e.WindowSize = WindowSize;
250 OnBorderWindowResized(this, e);
254 WindowSize += new Size2D((int)borderInterface.BorderLineThickness * 2, (int)(borderHeight + borderInterface.BorderLineThickness * 2));
257 // If it is BorderResizePolicyType.KeepRatio type, it will be resized according to the ratio.
258 if (borderInterface.ResizePolicy == BorderResizePolicyType.KeepRatio)
260 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
263 // Add a view to the border layer.
264 GetBorderWindowBottomLayer().Add(rootView);
266 FocusChanged += OnWindowFocusChanged;
272 this.borderInterface.Dispose();
277 private void OnWindowFocusChanged(object sender, Window.FocusChangedEventArgs e)
279 if (e.FocusGained == true && IsMaximized() == false)
281 // Raises the window when the window is focused.
286 /// Create the border UI.
287 private bool CreateBorder()
289 rootView = new View()
291 WidthResizePolicy = ResizePolicyType.FillToParent,
292 HeightResizePolicy = ResizePolicyType.FillToParent,
293 BackgroundColor = Color.Transparent,
296 ushort padding = (ushort) borderInterface.BorderLineThickness;
297 borderView = new BorderView()
299 GrabTouchAfterLeave = true,
300 WidthResizePolicy = ResizePolicyType.FillToParent,
301 HeightResizePolicy = ResizePolicyType.FillToParent,
302 BackgroundColor = Color.Transparent,
303 Layout = new LinearLayout() {
304 LinearOrientation = LinearLayout.Orientation.Vertical,
305 LinearAlignment = LinearLayout.Alignment.Top
307 Padding = new Extents(padding, padding, padding, padding),
309 borderInterface.CreateBorderView(borderView);
313 WidthSpecification = LayoutParamPolicies.MatchParent,
314 SizeHeight = borderInterface.BorderHeight,
315 BackgroundColor = Color.Transparent,
318 contentsView = new View()
320 BackgroundColor = Color.Transparent,
321 WidthSpecification = LayoutParamPolicies.MatchParent,
324 bottomView = new View()
326 WidthSpecification = LayoutParamPolicies.MatchParent,
327 SizeHeight = borderInterface.BorderHeight,
328 BackgroundColor = Color.Transparent,
331 // // Gets the Border's UI.
332 if (borderInterface.CreateTopBorderView(topView) == true && topView != null)
334 borderView.Add(topView);
337 borderView.Add(contentsView);
338 if (borderInterface.CreateBottomBorderView(bottomView) == true && bottomView != null)
340 borderView.Add(bottomView);
343 rootView.Add(borderView);
345 return isTop || isBottom;
349 /// Calculates which direction to resize or to move.
351 /// <param name="xPosition">The X position.</param>
352 /// <param name="yPosition">The Y position.</param>
353 /// <returns>The BorderDirection</returns>
354 [EditorBrowsable(EditorBrowsableState.Never)]
355 public BorderDirection GetDirection(float xPosition, float yPosition)
357 BorderDirection direction = BorderDirection.None;
359 // check bottom left corner
360 if (xPosition < borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
362 direction = BorderDirection.BottomLeft;
364 // check bottom right corner
365 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
367 direction = BorderDirection.BottomRight;
369 // check top left corner
370 else if (xPosition < borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
372 direction = BorderDirection.TopLeft;
374 // check top right corner
375 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
377 direction = BorderDirection.TopRight;
380 else if (xPosition < borderInterface.TouchThickness)
382 direction = BorderDirection.Left;
385 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness)
387 direction = BorderDirection.Right;
390 else if (yPosition > WindowSize.Height + borderHeight + borderInterface.BorderLineThickness - borderInterface.TouchThickness)
392 direction = BorderDirection.Bottom;
395 else if (yPosition < borderInterface.TouchThickness)
397 direction = BorderDirection.Top;
400 else if ((yPosition > WindowSize.Height) || (isTop == true && yPosition < topView.SizeHeight))
402 direction = BorderDirection.Move;
408 private void DoOverlayMode(bool enable)
410 if (borderInterface.OverlayMode == true)
412 borderInterface.OnOverlayMode(enable);
413 borderView?.OverlayMode(enable);
416 GetBorderWindowBottomLayer().RaiseToTop();
420 GetBorderWindowBottomLayer().LowerToBottom();
426 // Called when the window size has changed.
427 private void OnBorderWindowResized(object sender, Window.ResizedEventArgs e)
429 Tizen.Log.Info("NUI", $"OnBorderWindowResized {e.WindowSize.Width},{e.WindowSize.Height}\n");
430 int resizeWidth = e.WindowSize.Width;
431 int resizeHeight = e.WindowSize.Height;
433 borderInterface.OnResized(resizeWidth, resizeHeight);
435 // reset borderHeight
437 if (isTop) borderHeight += topView.SizeHeight;
438 if (isBottom) borderHeight += bottomView.SizeHeight;
440 if (borderInterface.OverlayMode == true && IsMaximized() == true)
442 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
443 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth, resizeHeight);
444 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, 0);
445 if (contentsView != null)
447 contentsView.SizeHeight = resizeHeight - borderHeight - (float)(borderInterface.BorderLineThickness * 2);
453 float height = (isTop == true) ? topView.SizeHeight : 0;
454 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
455 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth + (float)(borderInterface.BorderLineThickness * 2), resizeHeight + borderHeight + (float)(borderInterface.BorderLineThickness * 2));
456 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, height + borderInterface.BorderLineThickness);
457 if (contentsView != null)
459 contentsView.SizeHeight = resizeHeight;
461 DoOverlayMode(false);
464 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
467 internal Layer GetBorderWindowBottomLayer()
469 if (borderWindowBottomLayer == null)
471 borderWindowBottomLayer = new Layer();
472 borderWindowBottomLayer.Name = "BorderWindowBottomLayer";
473 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
474 Interop.ActorInternal.SetParentOrigin(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
475 Interop.Actor.SetAnchorPoint(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
476 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowBottomLayer.SwigCPtr);
477 Interop.ActorInternal.SetSize(borderWindowBottomLayer.SwigCPtr, WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2), WindowSize.Height + (float)(borderInterface.BorderLineThickness * 2));
478 borderWindowBottomLayer.SetWindow(this);
479 borderWindowBottomLayer.LowerToBottom();
481 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
483 return borderWindowBottomLayer;
486 internal Layer GetBorderWindowRootLayer()
488 if (borderWindowRootLayer == null)
490 borderWindowRootLayer = new Layer();
491 borderWindowRootLayer.Name = "RootLayer";
492 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
493 Interop.ActorInternal.SetParentOrigin(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
494 Interop.Actor.SetAnchorPoint(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
495 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowRootLayer.SwigCPtr);
496 Interop.ActorInternal.SetSize(borderWindowRootLayer.SwigCPtr, WindowSize.Width, WindowSize.Height - borderHeight - borderInterface.BorderLineThickness * 2);
497 float height = (isTop == true) ? topView.SizeHeight : 0;
498 Interop.ActorInternal.SetPosition(borderWindowRootLayer.SwigCPtr, 0, height + borderInterface.BorderLineThickness);
499 using PropertyValue propertyValue = new Tizen.NUI.PropertyValue((int)Tizen.NUI.ClippingModeType.ClipToBoundingBox);
500 Tizen.NUI.Object.SetProperty(borderWindowRootLayer.SwigCPtr, Tizen.NUI.BaseComponents.View.Property.ClippingMode, propertyValue);
502 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
505 return borderWindowRootLayer;
508 internal void DisposeBorder()
510 Resized -= OnBorderWindowResized;
511 borderInterface.Dispose();
512 GetBorderWindowBottomLayer().Dispose();
515 private void convertBorderWindowSizeToRealWindowSize(Uint16Pair size)
517 if (isBorderWindow == true)
519 var height = (ushort)(size.GetHeight() + borderHeight + borderInterface.BorderLineThickness * 2);
520 var width = (ushort)(size.GetWidth() + borderInterface.BorderLineThickness * 2);
521 size.SetHeight(height);
522 size.SetWidth(width);
526 private void convertRealWindowSizeToBorderWindowSize(Uint16Pair size)
528 if (isBorderWindow == true && !(borderInterface.OverlayMode == true && IsMaximized() == true))
530 var height = (ushort)(size.GetHeight() - borderHeight - borderInterface.BorderLineThickness * 2);
531 var width = (ushort)(size.GetWidth() - borderInterface.BorderLineThickness * 2);
532 size.SetHeight(height);
533 size.SetWidth(width);
542 // View class for border view.
543 private class BorderView : View
545 private bool overlayEnabled = false;
548 /// Set whether or not it is in overlay mode.
549 /// The borderView's OverlayMode means that it is displayed at the top of the screen.
550 /// In this case, the borderView should pass the event so that lower layers can receive the event.
552 /// <param name="enabled">The true if borderView is Overlay mode. false otherwise.</param>
553 [EditorBrowsable(EditorBrowsableState.Never)]
554 public void OverlayMode(bool enabled)
556 overlayEnabled = enabled;
559 protected override bool HitTest(Touch touch)
561 // If borderView is in overlay mode, pass the hittest.
562 if (overlayEnabled == true)