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 BorderResizePolicyType borderResizePolicy = BorderResizePolicyType.Free;
62 #endregion //Constructors
65 #endregion //Distructors
68 internal delegate void BorderCloseDelegate();
69 private BorderCloseDelegate borderCloseDelegate = null;
71 #endregion //Delegates
78 /// This is an enum for the resize direction or move value when the border area is touched.
80 [EditorBrowsable(EditorBrowsableState.Never)]
81 public enum BorderDirection
83 None = ResizeDirection.None,
84 TopLeft = ResizeDirection.TopLeft,
85 Top = ResizeDirection.Top,
86 TopRight = ResizeDirection.TopRight,
87 Left = ResizeDirection.Left,
88 Right = ResizeDirection.Right,
89 BottomLeft = ResizeDirection.BottomLeft,
90 Bottom = ResizeDirection.Bottom,
91 BottomRight = ResizeDirection.BottomRight,
96 /// This enum is the policy when resizing the border window.
98 [EditorBrowsable(EditorBrowsableState.Never)]
99 public enum BorderResizePolicyType
102 /// The window can be resized freely.
106 /// The window is resized according to the ratio.
110 /// The window is not resized and is fixed.
117 #endregion //Interfaces
121 /// Whether the border is enabled.
123 [EditorBrowsable(EditorBrowsableState.Never)]
124 public bool IsBorderEnabled => isBorderWindow;
125 #endregion //Properties
128 #endregion //Indexers
133 /// Update BorderProperty
135 internal void UpdateProperty()
137 if (borderInterface != null)
140 if (hasTopView) height += topView.SizeHeight;
141 if (hasBottomView) height += bottomView.SizeHeight;
143 if (height != borderHeight)
145 float diff = height - borderHeight;
146 borderHeight = height;
147 WindowSize = new Size2D(WindowSize.Width, WindowSize.Height + (int)(diff));
150 if (minSize != borderInterface.MinSize)
152 using Size2D mimimumSize = new Size2D((borderInterface.MinSize?.Width ?? 0), (borderInterface.MinSize?.Height ?? 0) + (int)borderHeight);
153 SetMimimumSize(mimimumSize);
154 minSize = borderInterface.MinSize;
156 if (maxSize != borderInterface.MaxSize)
158 using Size2D maximumSize = new Size2D((borderInterface.MaxSize?.Width ?? 0), (borderInterface.MaxSize?.Height ?? 0) + (int)borderHeight);
159 SetMaximumSize(maximumSize);
160 maxSize = borderInterface.MaxSize;
162 if (borderResizePolicy != borderInterface.ResizePolicy)
164 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "0");
165 borderResizePolicy = borderInterface.ResizePolicy;
166 if (borderResizePolicy == BorderResizePolicyType.KeepRatio)
168 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
174 /// Called when the border is closed.
175 /// If the delegate is declared, the delegate is called, otherwise window is destroyed.
177 internal void BorderDestroy()
179 if (borderCloseDelegate != null)
181 borderCloseDelegate();
189 /// Enable the border window with IBorderInterface.
190 /// This adds a border area to the Window.
191 /// The border's UI is configured using IBorderInterface.
192 /// Users can reisze and move by touching the border area.
194 /// <param name="borderInterface">The IBorderInterface.</param>
195 /// <param name="borderCloseDelegate">The BorderCloseDelegate. When close, this delegate is called.</param>
196 /// <returns>Whether the border window is enabled</returns>
197 internal bool EnableBorder(IBorderInterface borderInterface, BorderCloseDelegate borderCloseDelegate = null)
199 if (isBorderWindow == true)
201 Tizen.Log.Error("NUI", $"Already EnableBorderWindow\n");
207 Information.TryGetValue<int>("http://tizen.org/feature/screen.width", out screenWidth);
208 Information.TryGetValue<int>("http://tizen.org/feature/screen.height", out screenHeight);
210 catch (DllNotFoundException e)
212 Tizen.Log.Fatal("NUI", $"{e}\n");
215 if (borderInterface == null)
217 borderInterface = new DefaultBorder();
219 this.borderInterface = borderInterface;
220 this.borderCloseDelegate = borderCloseDelegate;
222 GetDefaultLayer().Name = "OriginalRootLayer";
224 SetTransparency(true);
225 BackgroundColor = Color.Transparent;
226 borderInterface.BorderWindow = this;
228 if (CreateBorder() == true)
230 using var realWindowSize = new Size2D(WindowSize.Width, WindowSize.Height);
232 isBorderWindow = true;
234 Resized += OnBorderWindowResized;
236 Moved += OnBorderWindowMoved;
238 borderInterface.OnCreated(borderView);
240 // Increase the window size as much as the border area.
242 if (hasTopView) borderHeight += topView.SizeHeight;
243 if (hasBottomView) borderHeight += bottomView.SizeHeight;
245 // Sets the minimum / maximum size to be resized by RequestResizeToServer.
246 if (borderInterface.MinSize != null)
248 using Size2D mimimumSize = new Size2D(borderInterface.MinSize.Width, borderInterface.MinSize.Height + (int)borderHeight);
249 SetMimimumSize(mimimumSize);
251 if (borderInterface.MaxSize != null)
253 using Size2D maximumSize = new Size2D(borderInterface.MaxSize.Width, borderInterface.MaxSize.Height + (int)borderHeight);
254 SetMaximumSize(maximumSize);
257 // When running the app for the first time, if it runs in full size, do Maximize(true).
258 if (screenWidth != 0 && screenHeight != 0 &&
259 realWindowSize.Width >= screenWidth && realWindowSize.Height >= screenHeight &&
260 IsMaximized() == false)
263 borderInterface.OnMaximize(true);
264 ResizedEventArgs e = new ResizedEventArgs();
265 e.WindowSize = WindowSize;
266 OnBorderWindowResized(this, e);
270 WindowSize += new Size2D((int)borderInterface.BorderLineThickness * 2, (int)(borderHeight + borderInterface.BorderLineThickness * 2));
273 // If it is BorderResizePolicyType.KeepRatio type, it will be resized according to the ratio.
274 if (borderInterface.ResizePolicy == BorderResizePolicyType.KeepRatio)
276 AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
279 // Add a view to the border layer.
280 GetBorderWindowBottomLayer().Add(rootView);
282 FocusChanged += OnWindowFocusChanged;
288 this.borderInterface.Dispose();
293 private void OnWindowFocusChanged(object sender, Window.FocusChangedEventArgs e)
295 if (e.FocusGained == true && IsMaximized() == false)
297 // Raises the window when the window is focused.
302 /// Create the border UI.
303 private bool CreateBorder()
305 rootView = new View()
307 WidthResizePolicy = ResizePolicyType.FillToParent,
308 HeightResizePolicy = ResizePolicyType.FillToParent,
309 BackgroundColor = Color.Transparent,
312 ushort padding = (ushort) borderInterface.BorderLineThickness;
313 borderView = new BorderView()
315 GrabTouchAfterLeave = true,
316 WidthResizePolicy = ResizePolicyType.FillToParent,
317 HeightResizePolicy = ResizePolicyType.FillToParent,
318 BackgroundColor = Color.Transparent,
319 Layout = new LinearLayout() {
320 LinearOrientation = LinearLayout.Orientation.Vertical,
321 LinearAlignment = LinearLayout.Alignment.Top
323 Padding = new Extents(padding, padding, padding, padding),
325 borderInterface.CreateBorderView(borderView);
329 WidthSpecification = LayoutParamPolicies.MatchParent,
330 SizeHeight = borderInterface.BorderHeight,
331 BackgroundColor = Color.Transparent,
334 contentsView = new View()
336 BackgroundColor = Color.Transparent,
337 WidthSpecification = LayoutParamPolicies.MatchParent,
340 bottomView = new View()
342 WidthSpecification = LayoutParamPolicies.MatchParent,
343 SizeHeight = borderInterface.BorderHeight,
344 BackgroundColor = Color.Transparent,
347 // // Gets the Border's UI.
348 if (borderInterface.CreateTopBorderView(topView) == true && topView != null)
350 borderView.Add(topView);
353 borderView.Add(contentsView);
354 if (borderInterface.CreateBottomBorderView(bottomView) == true && bottomView != null)
356 borderView.Add(bottomView);
357 hasBottomView = true;
359 rootView.Add(borderView);
361 return hasTopView || hasBottomView;
365 /// Calculates which direction to resize or to move.
367 /// <param name="xPosition">The X position.</param>
368 /// <param name="yPosition">The Y position.</param>
369 /// <returns>The BorderDirection</returns>
370 [EditorBrowsable(EditorBrowsableState.Never)]
371 public BorderDirection GetDirection(float xPosition, float yPosition)
373 BorderDirection direction = BorderDirection.None;
375 // check bottom left corner
376 if (xPosition < borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
378 direction = BorderDirection.BottomLeft;
380 // check bottom right corner
381 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
383 direction = BorderDirection.BottomRight;
385 // check top left corner
386 else if (xPosition < borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
388 direction = BorderDirection.TopLeft;
390 // check top right corner
391 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
393 direction = BorderDirection.TopRight;
396 else if (xPosition < borderInterface.TouchThickness)
398 direction = BorderDirection.Left;
401 else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness)
403 direction = BorderDirection.Right;
406 else if (yPosition > WindowSize.Height + borderHeight + borderInterface.BorderLineThickness - borderInterface.TouchThickness)
408 direction = BorderDirection.Bottom;
411 else if (yPosition < borderInterface.TouchThickness)
413 direction = BorderDirection.Top;
416 else if ((yPosition > WindowSize.Height) || (hasTopView == true && yPosition < topView.SizeHeight))
418 direction = BorderDirection.Move;
424 private void DoOverlayMode(bool enable)
426 if (borderInterface.OverlayMode == true)
428 if (isEnabledOverlayMode != enable)
430 borderView?.OverlayMode(enable);
431 borderInterface.OnOverlayMode(enable);
434 GetBorderWindowBottomLayer().RaiseToTop();
438 GetBorderWindowBottomLayer().LowerToBottom();
440 isEnabledOverlayMode = enable;
445 private void DoMaximize(bool isMaximized)
447 if (this.isMaximized != isMaximized)
449 borderView?.OnMaximize(isMaximized);
450 borderInterface.OnMaximize(isMaximized);
452 this.isMaximized = isMaximized;
455 // Called when the window position has changed.
456 private void OnBorderWindowMoved(object sender, WindowMovedEventArgs e)
458 Tizen.Log.Info("NUI", $"OnBorderWindowMoved {e.WindowPosition.X}, {e.WindowPosition.Y}\n");
459 borderInterface.OnMoved(e.WindowPosition.X, e.WindowPosition.X);
463 // Called when the window size has changed.
464 private void OnBorderWindowResized(object sender, Window.ResizedEventArgs e)
466 Tizen.Log.Info("NUI", $"OnBorderWindowResized {e.WindowSize.Width},{e.WindowSize.Height}\n");
467 int resizeWidth = e.WindowSize.Width;
468 int resizeHeight = e.WindowSize.Height;
470 borderInterface.OnResized(resizeWidth, resizeHeight);
472 // reset borderHeight
474 if (hasTopView) borderHeight += topView.SizeHeight;
475 if (hasBottomView) borderHeight += bottomView.SizeHeight;
477 bool isMaximized = IsMaximized();
478 bool isEnabledOverlay = (borderInterface.OverlayMode == true && isMaximized == true);
481 float height = isEnabledOverlay ? 0 : borderHeight;
482 float y = isEnabledOverlay ? 0 : ((hasTopView == true) ? topView.SizeHeight : 0);
484 if (isMaximized == false)
486 width = (float)(borderInterface.BorderLineThickness * 2);
487 height += (float)(borderInterface.BorderLineThickness * 2);
488 y += borderInterface.BorderLineThickness;
491 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
492 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth + width, resizeHeight + height);
493 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, y);
495 if (contentsView != null)
497 contentsView.SizeHeight = resizeHeight - (isEnabledOverlay ? borderHeight : 0);
499 DoMaximize(isMaximized);
501 DoOverlayMode(isEnabledOverlay);
503 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
506 internal Layer GetBorderWindowBottomLayer()
508 if (borderWindowBottomLayer == null)
510 borderWindowBottomLayer = new Layer();
511 borderWindowBottomLayer.Name = "BorderWindowBottomLayer";
512 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
513 Interop.ActorInternal.SetParentOrigin(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
514 Interop.Actor.SetAnchorPoint(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
515 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowBottomLayer.SwigCPtr);
516 Interop.ActorInternal.SetSize(borderWindowBottomLayer.SwigCPtr, WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2), WindowSize.Height + (float)(borderInterface.BorderLineThickness * 2));
517 borderWindowBottomLayer.SetWindow(this);
518 borderWindowBottomLayer.LowerToBottom();
520 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
522 return borderWindowBottomLayer;
525 internal Layer GetBorderWindowRootLayer()
527 if (borderWindowRootLayer == null)
529 borderWindowRootLayer = new Layer();
530 borderWindowRootLayer.Name = "RootLayer";
531 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
532 Interop.ActorInternal.SetParentOrigin(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
533 Interop.Actor.SetAnchorPoint(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
534 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowRootLayer.SwigCPtr);
535 Interop.ActorInternal.SetSize(borderWindowRootLayer.SwigCPtr, WindowSize.Width, WindowSize.Height - borderHeight - borderInterface.BorderLineThickness * 2);
536 float height = (hasTopView == true) ? topView.SizeHeight : 0;
537 Interop.ActorInternal.SetPosition(borderWindowRootLayer.SwigCPtr, 0, height + borderInterface.BorderLineThickness);
538 using PropertyValue propertyValue = new Tizen.NUI.PropertyValue((int)Tizen.NUI.ClippingModeType.ClipToBoundingBox);
539 Tizen.NUI.Object.SetProperty(borderWindowRootLayer.SwigCPtr, Tizen.NUI.BaseComponents.View.Property.ClippingMode, propertyValue);
541 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
544 return borderWindowRootLayer;
547 internal void DisposeBorder()
549 Resized -= OnBorderWindowResized;
550 FocusChanged -= OnWindowFocusChanged;
551 borderInterface.Dispose();
552 GetBorderWindowBottomLayer().Dispose();
555 private void convertBorderWindowSizeToRealWindowSize(Uint16Pair size)
557 if (isBorderWindow == true)
559 var height = (ushort)(size.GetHeight() + borderHeight + borderInterface.BorderLineThickness * 2);
560 var width = (ushort)(size.GetWidth() + borderInterface.BorderLineThickness * 2);
561 size.SetHeight(height);
562 size.SetWidth(width);
566 private void convertRealWindowSizeToBorderWindowSize(Uint16Pair size)
568 if (isBorderWindow == true && !(borderInterface.OverlayMode == true && IsMaximized() == true))
570 var height = (ushort)(size.GetHeight() - borderHeight - borderInterface.BorderLineThickness * 2);
571 var width = (ushort)(size.GetWidth() - borderInterface.BorderLineThickness * 2);
572 size.SetHeight(height);
573 size.SetWidth(width);
582 // View class for border view.
583 private class BorderView : View
585 private bool isEnabledOverlay = false;
586 private Extents prePadding = new Extents(0, 0, 0, 0);
589 /// Set whether or not it is in overlay mode.
590 /// The borderView's OverlayMode means that it is displayed at the top of the screen.
591 /// In this case, the borderView should pass the event so that lower layers can receive the event.
593 /// <param name="enabled">The true if borderView is Overlay mode. false otherwise.</param>
594 [EditorBrowsable(EditorBrowsableState.Never)]
595 public void OverlayMode(bool enabled)
597 isEnabledOverlay = enabled;
601 /// Called when the window is maximized.
603 /// <param name="isMaximized">If window is maximized or unmaximized.</param>
604 [EditorBrowsable(EditorBrowsableState.Never)]
605 public void OnMaximize(bool isMaximized)
607 // When maximized, it is displayed in full without border lines.
608 if (isMaximized == true)
610 prePadding = Padding;
611 Padding = new Extents(0, 0, 0, 0);
615 Padding = prePadding;
620 protected override bool HitTest(Touch touch)
622 // If borderView is in overlay mode, pass the hittest.
623 return (isEnabledOverlay == false);