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;
40 private View rootView = null;
41 private BorderView borderView = null;
42 private View topView = null;
43 private View contentsView = null;
44 private View bottomView = null;
45 private float borderHeight = 0;
46 private int screenWidth = 0;
47 private int screenHeight = 0;
49 private bool isBorderWindow = false;
50 private bool hasTopView = false;
51 private bool hasBottomView = false;
52 private bool isEnabledOverlayMode = false;
53 private bool isMaximized = false;
56 private Size2D minSize = null;
57 private Size2D maxSize = null;
58 private uint borderLineThickness = 0;
59 private BorderResizePolicyType borderResizePolicy = BorderResizePolicyType.Free;
63 #endregion //Constructors
66 #endregion //Distructors
69 internal delegate void BorderCloseDelegate();
70 private BorderCloseDelegate borderCloseDelegate = null;
72 #endregion //Delegates
79 /// This is an enum for the resize direction or move value when the border area is touched.
81 [EditorBrowsable(EditorBrowsableState.Never)]
82 public enum BorderDirection
84 None = ResizeDirection.None,
85 TopLeft = ResizeDirection.TopLeft,
86 Top = ResizeDirection.Top,
87 TopRight = ResizeDirection.TopRight,
88 Left = ResizeDirection.Left,
89 Right = ResizeDirection.Right,
90 BottomLeft = ResizeDirection.BottomLeft,
91 Bottom = ResizeDirection.Bottom,
92 BottomRight = ResizeDirection.BottomRight,
97 /// This enum is the policy when resizing the border window.
99 [EditorBrowsable(EditorBrowsableState.Never)]
100 public enum BorderResizePolicyType
103 /// The window can be resized freely.
107 /// The window is resized according to the ratio.
111 /// The window is not resized and is fixed.
118 #endregion //Interfaces
122 /// Whether the border is enabled.
124 [EditorBrowsable(EditorBrowsableState.Never)]
125 public bool IsBorderEnabled => isBorderWindow;
126 #endregion //Properties
129 #endregion //Indexers
134 /// Update BorderProperty
136 internal void UpdateProperty()
138 if (borderInterface != null)
140 bool isNeedResizeByLine = false;
141 bool isNeedResizeByBorder = false;
142 using var val = new Uint16Pair(Interop.Window.GetSize(SwigCPtr), true);
143 if (borderLineThickness != borderInterface.BorderLineThickness)
145 isNeedResizeByLine = true;
146 int diffBorderLine = (int)borderInterface.BorderLineThickness - (int)borderLineThickness;
147 borderLineThickness = borderInterface.BorderLineThickness;
149 if (borderView != null)
151 Extents extents = borderView.Padding;
152 ushort start = (extents.Start + diffBorderLine) > 0 ? (ushort)(extents.Start + diffBorderLine) : (ushort)0;
153 ushort end = (extents.End + diffBorderLine) > 0 ? (ushort)(extents.End + diffBorderLine) : (ushort)0;
154 ushort top = (extents.Top + diffBorderLine) > 0 ? (ushort)(extents.Top + diffBorderLine) : (ushort)0;
155 ushort bottom = (extents.Bottom + diffBorderLine) > 0 ? (ushort)(extents.Bottom + diffBorderLine) : (ushort)0;
156 borderView.Padding = new Extents(start, end, top, bottom);
157 if (IsMaximized() == true)
159 borderView.OnMaximize(true);
163 val.SetWidth((ushort)(val.GetWidth() + diffBorderLine * 2));
164 val.SetHeight((ushort)(val.GetHeight() + diffBorderLine * 2));
168 if (hasTopView) height += topView.SizeHeight;
169 if (hasBottomView) height += bottomView.SizeHeight;
170 if (height != borderHeight)
172 isNeedResizeByBorder = true;
173 float diff = height - borderHeight;
174 borderHeight = height;
175 val.SetHeight((ushort)(val.GetHeight() + diff));
178 if (isNeedResizeByLine == true || isNeedResizeByBorder == true)
180 Interop.Window.SetSize(SwigCPtr, Uint16Pair.getCPtr(val));
183 if (minSize != borderInterface.MinSize || (borderInterface.MinSize != null && isNeedResizeByLine == true))
185 using Size2D mimimumSize = new Size2D((borderInterface.MinSize?.Width + (int)borderLineThickness * 2 ?? 0), (borderInterface.MinSize?.Height ?? 0) + (int)(borderHeight + borderLineThickness * 2));
186 SetMimimumSize(mimimumSize);
187 minSize = borderInterface.MinSize;
190 if (maxSize != borderInterface.MaxSize || (borderInterface.MaxSize != null && isNeedResizeByLine == true))
192 using Size2D maximumSize = new Size2D((borderInterface.MaxSize?.Width + (int)borderLineThickness * 2 ?? 0), (borderInterface.MaxSize?.Height ?? 0) + (int)(borderHeight + borderLineThickness * 2));
193 SetMaximumSize(maximumSize);
194 maxSize = borderInterface.MaxSize;
197 if (borderResizePolicy != borderInterface.ResizePolicy)
199 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "0");
200 borderResizePolicy = borderInterface.ResizePolicy;
201 if (borderResizePolicy == BorderResizePolicyType.KeepRatio)
203 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
210 /// Called when the border is closed.
211 /// If the delegate is declared, the delegate is called, otherwise window is destroyed.
213 internal void BorderDestroy()
215 if (borderCloseDelegate != null)
217 borderCloseDelegate();
225 /// Enable the border window with IBorderInterface.
226 /// This adds a border area to the Window.
227 /// The border's UI is configured using IBorderInterface.
228 /// Users can reisze and move by touching the border area.
230 /// <param name="borderInterface">The IBorderInterface.</param>
231 /// <param name="borderCloseDelegate">The BorderCloseDelegate. When close, this delegate is called.</param>
232 /// <returns>Whether the border window is enabled</returns>
233 internal bool EnableBorder(IBorderInterface borderInterface, BorderCloseDelegate borderCloseDelegate = null)
235 if (isBorderWindow == true)
237 Tizen.Log.Error("NUI", $"Already EnableBorderWindow\n");
243 Information.TryGetValue<int>("http://tizen.org/feature/screen.width", out screenWidth);
244 Information.TryGetValue<int>("http://tizen.org/feature/screen.height", out screenHeight);
246 catch (DllNotFoundException e)
248 Tizen.Log.Fatal("NUI", $"{e}\n");
251 if (borderInterface == null)
253 borderInterface = new DefaultBorder();
255 this.borderInterface = borderInterface;
256 this.borderCloseDelegate = borderCloseDelegate;
258 GetDefaultLayer().Name = "OriginalRootLayer";
260 borderInterface.BorderWindow = this;
262 if (CreateBorder() == true)
264 using var realWindowSize = new Size2D(WindowSize.Width, WindowSize.Height);
266 isBorderWindow = true;
268 Resized += OnBorderWindowResized;
270 Moved += OnBorderWindowMoved;
272 borderInterface.OnCreated(borderView);
274 // Increase the window size as much as the border area.
276 if (hasTopView) borderHeight += topView.SizeHeight;
277 if (hasBottomView) borderHeight += bottomView.SizeHeight;
279 // Sets the minimum / maximum size to be resized by RequestResizeToServer.
280 if (borderInterface.MinSize != null)
282 using Size2D mimimumSize = new Size2D(borderInterface.MinSize.Width + (int)borderInterface.BorderLineThickness * 2, borderInterface.MinSize.Height + (int)(borderHeight + borderInterface.BorderLineThickness * 2));
283 SetMimimumSize(mimimumSize);
285 if (borderInterface.MaxSize != null)
287 using Size2D maximumSize = new Size2D(borderInterface.MaxSize.Width + (int)borderInterface.BorderLineThickness * 2, borderInterface.MaxSize.Height + (int)(borderHeight + borderInterface.BorderLineThickness * 2));
288 SetMaximumSize(maximumSize);
291 // When running the app for the first time, if it runs in full size, do Maximize(true).
292 if (screenWidth != 0 && screenHeight != 0 &&
293 realWindowSize.Width >= screenWidth && realWindowSize.Height >= screenHeight &&
294 IsMaximized() == false)
297 borderInterface.OnMaximize(true);
298 ResizedEventArgs e = new ResizedEventArgs();
299 e.WindowSize = WindowSize;
300 windowResizeEventHandler?.Invoke(this, e);
304 borderInterface.OnMaximize(IsMaximized());
305 if (borderHeight > 0)
307 borderLineThickness = borderInterface.BorderLineThickness;
308 WindowSize += new Size2D((int)borderLineThickness * 2, (int)(borderHeight + borderLineThickness * 2));
312 // If it is BorderResizePolicyType.KeepRatio type, it will be resized according to the ratio.
313 if (borderInterface.ResizePolicy == BorderResizePolicyType.KeepRatio)
315 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
318 // Add a view to the border layer.
319 GetBorderWindowBottomLayer().Add(rootView);
321 FocusChanged += OnWindowFocusChanged;
327 this.borderInterface.Dispose();
332 private void OnWindowFocusChanged(object sender, Window.FocusChangedEventArgs e)
334 if (e.FocusGained == true && IsMaximized() == false)
336 // Raises the window when the window is focused.
341 /// Create the border UI.
342 private bool CreateBorder()
344 rootView = new View()
346 WidthResizePolicy = ResizePolicyType.FillToParent,
347 HeightResizePolicy = ResizePolicyType.FillToParent,
348 BackgroundColor = Color.Transparent,
351 ushort padding = (ushort) borderLineThickness;
352 borderView = new BorderView()
354 GrabTouchAfterLeave = true,
355 WidthResizePolicy = ResizePolicyType.FillToParent,
356 HeightResizePolicy = ResizePolicyType.FillToParent,
357 BackgroundColor = Color.Transparent,
358 Layout = new LinearLayout() {
359 LinearOrientation = LinearLayout.Orientation.Vertical,
360 LinearAlignment = LinearLayout.Alignment.Top
362 Padding = new Extents(padding, padding, padding, padding),
364 borderInterface.CreateBorderView(borderView);
368 WidthSpecification = LayoutParamPolicies.MatchParent,
369 SizeHeight = borderInterface.BorderHeight,
370 BackgroundColor = Color.Transparent,
373 contentsView = new View()
375 BackgroundColor = Color.Transparent,
376 WidthSpecification = LayoutParamPolicies.MatchParent,
379 bottomView = new View()
381 WidthSpecification = LayoutParamPolicies.MatchParent,
382 SizeHeight = borderInterface.BorderHeight,
383 BackgroundColor = Color.Transparent,
386 // // Gets the Border's UI.
387 if (borderInterface.CreateTopBorderView(topView) == true && topView != null)
389 borderView.Add(topView);
392 borderView.Add(contentsView);
393 if (borderInterface.CreateBottomBorderView(bottomView) == true && bottomView != null)
395 borderView.Add(bottomView);
396 hasBottomView = true;
398 rootView.Add(borderView);
400 return hasTopView || hasBottomView;
404 /// Calculates which direction to resize or to move.
406 /// <param name="xPosition">The X position.</param>
407 /// <param name="yPosition">The Y position.</param>
408 /// <returns>The BorderDirection</returns>
409 [EditorBrowsable(EditorBrowsableState.Never)]
410 public BorderDirection GetDirection(float xPosition, float yPosition)
412 BorderDirection direction = BorderDirection.None;
414 // check bottom left corner
415 if (xPosition < borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
417 direction = BorderDirection.BottomLeft;
419 // check bottom right corner
420 else if (xPosition > WindowSize.Width + (float)(borderLineThickness * 2) - borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
422 direction = BorderDirection.BottomRight;
424 // check top left corner
425 else if (xPosition < borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
427 direction = BorderDirection.TopLeft;
429 // check top right corner
430 else if (xPosition > WindowSize.Width + (float)(borderLineThickness * 2) - borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
432 direction = BorderDirection.TopRight;
435 else if (xPosition < borderInterface.TouchThickness)
437 direction = BorderDirection.Left;
440 else if (xPosition > WindowSize.Width + (float)(borderLineThickness * 2) - borderInterface.TouchThickness)
442 direction = BorderDirection.Right;
445 else if (yPosition > WindowSize.Height + borderHeight + borderLineThickness - borderInterface.TouchThickness)
447 direction = BorderDirection.Bottom;
450 else if (yPosition < borderInterface.TouchThickness)
452 direction = BorderDirection.Top;
455 else if ((yPosition > WindowSize.Height) || (hasTopView == true && yPosition < topView.SizeHeight))
457 direction = BorderDirection.Move;
464 /// Gets position and size of the border window
466 /// <returns>The total window size including the border area in the default window.</returns>
467 [EditorBrowsable(EditorBrowsableState.Never)]
468 public Rectangle WindowPositionSizeWithBorder
472 using var position = GetPosition();
473 using var size = GetWindowSizeWithBorder();
474 Rectangle ret = new Rectangle(position?.X ?? 0, position?.Y ?? 0, size?.Width ?? 0, size?.Height ?? 0);
480 /// Gets size of the border window
482 /// <returns>The total window size including the border area in the default window.</returns>
483 [EditorBrowsable(EditorBrowsableState.Never)]
484 public Size2D WindowSizeWithBorder
488 return GetWindowSizeWithBorder();
492 private Size2D GetWindowSizeWithBorder()
494 var val = new Uint16Pair(Interop.Window.GetSize(SwigCPtr), true);
495 Size2D size = new Size2D(val.GetWidth(), val.GetHeight());
497 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
501 private void DoOverlayMode(bool enable)
503 if (borderInterface.OverlayMode == true)
505 if (isEnabledOverlayMode != enable)
507 borderView?.OverlayMode(enable);
508 borderInterface.OnOverlayMode(enable);
511 GetBorderWindowBottomLayer().RaiseToTop();
515 GetBorderWindowBottomLayer().LowerToBottom();
517 isEnabledOverlayMode = enable;
522 private void DoMaximize(bool isMaximized)
524 if (this.isMaximized != isMaximized)
526 borderView?.OnMaximize(isMaximized);
527 borderInterface.OnMaximize(isMaximized);
529 this.isMaximized = isMaximized;
532 // Called when the window position has changed.
533 private void OnBorderWindowMoved(object sender, WindowMovedEventArgs e)
535 Tizen.Log.Info("NUI", $"OnBorderWindowMoved {e.WindowPosition.X}, {e.WindowPosition.Y}\n");
536 borderInterface.OnMoved(e.WindowPosition.X, e.WindowPosition.X);
540 // Called when the window size has changed.
541 private void OnBorderWindowResized(object sender, Window.ResizedEventArgs e)
543 Tizen.Log.Info("NUI", $"OnBorderWindowResized {e.WindowSize.Width},{e.WindowSize.Height}\n");
544 int resizeWidth = e.WindowSize.Width;
545 int resizeHeight = e.WindowSize.Height;
547 borderInterface.OnResized(resizeWidth, resizeHeight);
549 // reset borderHeight
551 if (hasTopView) borderHeight += topView.SizeHeight;
552 if (hasBottomView) borderHeight += bottomView.SizeHeight;
554 bool isMaximized = IsMaximized();
555 bool isEnabledOverlay = (borderInterface.OverlayMode == true && isMaximized == true);
558 float height = isEnabledOverlay ? 0 : borderHeight;
559 float y = isEnabledOverlay ? 0 : ((hasTopView == true) ? topView.SizeHeight : 0);
561 if (isMaximized == false)
563 width = (float)(borderLineThickness * 2);
564 height += (float)(borderLineThickness * 2);
565 y += borderLineThickness;
568 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
569 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth + width, resizeHeight + height);
570 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, y);
572 if (contentsView != null)
574 contentsView.SizeHeight = resizeHeight - (isEnabledOverlay ? borderHeight : 0);
576 DoMaximize(isMaximized);
578 DoOverlayMode(isEnabledOverlay);
580 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
583 internal Layer GetBorderWindowBottomLayer()
585 if (borderWindowBottomLayer == null)
587 borderWindowBottomLayer = new Layer();
588 borderWindowBottomLayer.Name = "BorderWindowBottomLayer";
589 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
590 Interop.ActorInternal.SetParentOrigin(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
591 Interop.Actor.SetAnchorPoint(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
592 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowBottomLayer.SwigCPtr);
593 Interop.ActorInternal.SetSize(borderWindowBottomLayer.SwigCPtr, WindowSize.Width + (float)(borderLineThickness * 2), WindowSize.Height + (float)(borderLineThickness * 2));
594 borderWindowBottomLayer.SetWindow(this);
595 borderWindowBottomLayer.LowerToBottom();
597 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
599 return borderWindowBottomLayer;
602 internal Layer GetBorderWindowRootLayer()
604 if (borderWindowRootLayer == null)
606 borderWindowRootLayer = new Layer();
607 borderWindowRootLayer.Name = "RootLayer";
608 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
609 Interop.ActorInternal.SetParentOrigin(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
610 Interop.Actor.SetAnchorPoint(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
611 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowRootLayer.SwigCPtr);
612 Interop.ActorInternal.SetSize(borderWindowRootLayer.SwigCPtr, WindowSize.Width, WindowSize.Height - borderHeight - borderLineThickness * 2);
613 float height = (hasTopView == true) ? topView.SizeHeight : 0;
614 Interop.ActorInternal.SetPosition(borderWindowRootLayer.SwigCPtr, 0, height + borderLineThickness);
615 using PropertyValue propertyValue = new Tizen.NUI.PropertyValue((int)Tizen.NUI.ClippingModeType.ClipToBoundingBox);
616 Tizen.NUI.Object.SetProperty(borderWindowRootLayer.SwigCPtr, Tizen.NUI.BaseComponents.View.Property.ClippingMode, propertyValue);
618 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
621 return borderWindowRootLayer;
624 internal void DisposeBorder()
626 Resized -= OnBorderWindowResized;
627 FocusChanged -= OnWindowFocusChanged;
628 borderInterface.Dispose();
629 GetBorderWindowBottomLayer().Dispose();
632 private void convertBorderWindowSizeToRealWindowSize(Uint16Pair size)
634 if (isBorderWindow == true)
636 var height = (ushort)(size.GetHeight() + borderHeight + borderLineThickness * 2);
637 var width = (ushort)(size.GetWidth() + borderLineThickness * 2);
638 size.SetHeight(height);
639 size.SetWidth(width);
643 private void convertRealWindowSizeToBorderWindowSize(Uint16Pair size)
645 if (isBorderWindow == true && !(borderInterface.OverlayMode == true && IsMaximized() == true))
647 var borderLine = IsMaximized() == true ? 0 : borderLineThickness * 2;
648 var height = (ushort)(size.GetHeight() - borderHeight - borderLine);
649 var width = (ushort)(size.GetWidth() - borderLine);
650 size.SetHeight(height);
651 size.SetWidth(width);
660 // View class for border view.
661 private class BorderView : View
663 private bool isEnabledOverlay = false;
664 private Extents prePadding = new Extents(0, 0, 0, 0);
666 internal BorderView() : base()
668 // BorderView will use custom HitTest function.
669 RegisterHitTestCallback();
673 /// Set whether or not it is in overlay mode.
674 /// The borderView's OverlayMode means that it is displayed at the top of the screen.
675 /// In this case, the borderView should pass the event so that lower layers can receive the event.
677 /// <param name="enabled">The true if borderView is Overlay mode. false otherwise.</param>
678 [EditorBrowsable(EditorBrowsableState.Never)]
679 public void OverlayMode(bool enabled)
681 isEnabledOverlay = enabled;
685 /// Called when the window is maximized.
687 /// <param name="isMaximized">If window is maximized or unmaximized.</param>
688 [EditorBrowsable(EditorBrowsableState.Never)]
689 public void OnMaximize(bool isMaximized)
691 // When maximized, it is displayed in full without border lines.
692 if (isMaximized == true)
694 prePadding = Padding;
695 Padding = new Extents(0, 0, 0, 0);
699 Padding = prePadding;
704 protected override bool HitTest(Touch touch)
706 // If borderView is in overlay mode, pass the hittest.
707 return (isEnabledOverlay == false);