[NUI] Fixed SVACE issues
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Window / BorderWindow.cs
1 /*
2  * Copyright(c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 extern alias TizenSystemInformation;
18 using TizenSystemInformation.Tizen.System;
19
20 using System;
21 using System.Collections.Generic;
22 using System.ComponentModel;
23 using System.Linq;
24 using System.Threading;
25 using Tizen.NUI.BaseComponents;
26
27 namespace Tizen.NUI
28 {
29     public partial class Window
30     {
31         #region Constant Fields
32         #endregion //Constant Fields
33
34         #region Fields
35         private IBorderInterface borderInterface = null;
36         private Layer borderWindowRootLayer = null;
37         private Layer borderWindowBottomLayer = null;
38         private bool isBorderWindow = false;
39
40         // for border area
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;
51         #endregion //Fields
52
53         #region Constructors
54         #endregion //Constructors
55
56         #region Distructors
57         #endregion //Distructors
58
59         #region Delegates
60         internal delegate void BorderCloseDelegate();
61         private BorderCloseDelegate borderCloseDelegate = null;
62
63         #endregion //Delegates
64
65         #region Events
66         #endregion //Events
67
68         #region Enums
69         /// <summary>
70         /// This is an enum for the resize direction or move value when the border area is touched.
71         /// </summary>
72         [EditorBrowsable(EditorBrowsableState.Never)]
73         public enum BorderDirection
74         {
75             None        = ResizeDirection.None,
76             TopLeft     = ResizeDirection.TopLeft,
77             Top         = ResizeDirection.Top,
78             TopRight    = ResizeDirection.TopRight,
79             Left        = ResizeDirection.Left,
80             Right       = ResizeDirection.Right,
81             BottomLeft  = ResizeDirection.BottomLeft,
82             Bottom      = ResizeDirection.Bottom,
83             BottomRight = ResizeDirection.BottomRight,
84             Move,
85         }
86
87         /// <summary>
88         /// This enum is the policy when resizing the border window.
89         /// </summary>
90         [EditorBrowsable(EditorBrowsableState.Never)]
91         public enum BorderResizePolicyType
92         {
93           /// <summary>
94           /// The window can be resized freely.
95           /// </summary>
96           Free = 0,
97           /// <summary>
98           /// The window is resized according to the ratio.
99           /// </summary>
100           KeepRatio = 1,
101           /// <summary>
102           /// The window is not resized and is fixed.
103           /// </summary>
104           Fixed = 2,
105         }
106         #endregion //Enums
107
108         #region Interfaces
109         #endregion //Interfaces
110
111         #region Properties
112         /// <summary>
113         /// Whether the border is enabled.
114         /// </summary>
115         [EditorBrowsable(EditorBrowsableState.Never)]
116         public bool IsBorderEnabled => isBorderWindow;
117         #endregion //Properties
118
119         #region Indexers
120         #endregion //Indexers
121
122         #region Methods
123         /// <summary>
124         /// Called when the border is closed.
125         /// If the delegate is declared, the delegate is called, otherwise window is destroyed.
126         /// </summary>
127         internal void BorderDestroy()
128         {
129             if (borderCloseDelegate != null)
130             {
131                 borderCloseDelegate();
132             }
133             else
134             {
135                 Destroy();
136             }
137         }
138         /// <summary>
139         /// Enable the border window with IBorderInterface.
140         /// This adds a border area to the Window.
141         /// The border's UI is configured using IBorderInterface.
142         /// Users can reisze and move by touching the border area.
143         /// </summary>
144         /// <param name="borderInterface">The IBorderInterface.</param>
145         /// <param name="borderCloseDelegate">The BorderCloseDelegate. When close, this delegate is called.</param>
146         /// <returns>Whether the border window is enabled</returns>
147         internal bool EnableBorder(IBorderInterface borderInterface, BorderCloseDelegate borderCloseDelegate = null)
148         {
149             if (isBorderWindow == true)
150             {
151                 Tizen.Log.Error("NUI", $"Already EnableBorderWindow\n");
152                 return false;
153             }
154
155             try
156             {
157                 Information.TryGetValue<int>("http://tizen.org/feature/screen.width", out screenWidth);
158                 Information.TryGetValue<int>("http://tizen.org/feature/screen.height", out screenHeight);
159             }
160             catch (DllNotFoundException e)
161             {
162                 Tizen.Log.Fatal("NUI", $"{e}\n");
163             }
164
165             if (borderInterface == null)
166             {
167                 borderInterface = new DefaultBorder();
168             }
169             this.borderInterface = borderInterface;
170             this.borderCloseDelegate = borderCloseDelegate;
171
172             GetDefaultLayer().Name = "OriginalRootLayer";
173
174             SetTransparency(true);
175             BackgroundColor = Color.Transparent;
176             borderInterface.BorderWindow = this;
177
178             if (CreateBorder() == true)
179             {
180                 using var realWindowSize = new Size2D(WindowSize.Width, WindowSize.Height);
181
182                 isBorderWindow = true;
183
184                 Resized += OnBorderWindowResized;
185
186                 borderInterface.OnCreated(borderView);
187
188                 // Increase the window size as much as the border area.
189                 borderHeight = 0;
190                 if (isTop) borderHeight += topView.SizeHeight;
191                 if (isBottom) borderHeight += bottomView.SizeHeight;
192
193                 // Sets the minimum / maximum size to be resized by RequestResizeToServer.
194                 if (borderInterface.MinSize != null)
195                 {
196                     using Size2D mimimumSize = new Size2D(borderInterface.MinSize.Width, borderInterface.MinSize.Height + (int)borderHeight);
197                     SetMimimumSize(mimimumSize);
198                 }
199                 if (borderInterface.MaxSize != null)
200                 {
201                     using Size2D maximuSize = new Size2D(borderInterface.MaxSize.Width, borderInterface.MaxSize.Height + (int)borderHeight);
202                     SetMaximumSize(maximuSize);
203                 }
204
205                 // When running the app for the first time, if it runs in full size, do Maximize(true).
206                 if (screenWidth != 0 && screenHeight != 0 &&
207                     realWindowSize.Width >= screenWidth && realWindowSize.Height >= screenHeight &&
208                     IsMaximized() == false)
209                 {
210                     Maximize(true);
211                     borderInterface.OnMaximize(true);
212                     ResizedEventArgs e = new ResizedEventArgs();
213                     e.WindowSize = WindowSize;
214                     OnBorderWindowResized(this, e);
215                 }
216                 else
217                 {
218                     WindowSize += new Size2D((int)borderInterface.BorderLineThickness * 2, (int)(borderHeight + borderInterface.BorderLineThickness * 2));
219                 }
220
221                 // If it is BorderResizePolicyType.KeepRatio type, it will be resized according to the ratio.
222                 if (borderInterface.ResizePolicy == BorderResizePolicyType.KeepRatio)
223                 {
224                     AddAuxiliaryHint("wm.policy.win.resize_aspect_ratio", "1");
225                 }
226
227                 // Add a view to the border layer.
228                 GetBorderWindowBottomLayer().Add(rootView);
229
230                 FocusChanged += OnWindowFocusChanged;
231
232                 return true;
233             }
234             else
235             {
236                 this.borderInterface.Dispose();
237                 return false;
238             }
239         }
240
241         private void OnWindowFocusChanged(object sender, Window.FocusChangedEventArgs e)
242         {
243             if (e.FocusGained == true && IsMaximized() == false)
244             {
245                 // Raises the window when the window is focused.
246                 Raise();
247             }
248         }
249
250         /// Create the border UI.
251         private bool CreateBorder()
252         {
253             rootView = new View()
254             {
255                 WidthResizePolicy = ResizePolicyType.FillToParent,
256                 HeightResizePolicy = ResizePolicyType.FillToParent,
257                 BackgroundColor = Color.Transparent,
258             };
259
260             ushort padding = (ushort) borderInterface.BorderLineThickness;
261             borderView = new BorderView()
262             {
263                 GrabTouchAfterLeave = true,
264                 WidthResizePolicy = ResizePolicyType.FillToParent,
265                 HeightResizePolicy = ResizePolicyType.FillToParent,
266                 BackgroundColor = Color.Transparent,
267                 Layout = new LinearLayout() {
268                     LinearOrientation = LinearLayout.Orientation.Vertical,
269                     LinearAlignment = LinearLayout.Alignment.Top
270                 },
271                 Padding = new Extents(padding, padding, padding, padding),
272             };
273             borderInterface.CreateBorderView(borderView);
274
275             topView = new View()
276             {
277                 WidthSpecification = LayoutParamPolicies.MatchParent,
278                 SizeHeight = borderInterface.BorderHeight,
279                 BackgroundColor = Color.Transparent,
280             };
281
282             contentsView = new View()
283             {
284                 BackgroundColor = Color.Transparent,
285                 WidthSpecification = LayoutParamPolicies.MatchParent,
286             };
287
288             bottomView = new View()
289             {
290                 WidthSpecification = LayoutParamPolicies.MatchParent,
291                 SizeHeight = borderInterface.BorderHeight,
292                 BackgroundColor = Color.Transparent,
293             };
294
295             // // Gets the Border's UI.
296             if (borderInterface.CreateTopBorderView(topView) == true && topView != null)
297             {
298                 borderView.Add(topView);
299                 isTop = true;
300             }
301             borderView.Add(contentsView);
302             if (borderInterface.CreateBottomBorderView(bottomView) == true && bottomView != null)
303             {
304                 borderView.Add(bottomView);
305                 isBottom = true;
306             }
307             rootView.Add(borderView);
308
309             return isTop || isBottom;
310         }
311
312         /// <summary>
313         /// Calculates which direction to resize or to move.
314         /// </summary>
315         /// <param name="xPosition">The X position.</param>
316         /// <param name="yPosition">The Y position.</param>
317         /// <returns>The BorderDirection</returns>
318         [EditorBrowsable(EditorBrowsableState.Never)]
319         public BorderDirection GetDirection(float xPosition, float yPosition)
320         {
321             BorderDirection direction = BorderDirection.None;
322
323             // check bottom left corner
324             if (xPosition < borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
325             {
326                 direction = BorderDirection.BottomLeft;
327             }
328             // check bottom right corner
329             else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition > WindowSize.Height + borderHeight - borderInterface.TouchThickness)
330             {
331                 direction = BorderDirection.BottomRight;
332             }
333             // check top left corner
334             else if (xPosition < borderInterface.TouchThickness && yPosition <  borderInterface.TouchThickness)
335             {
336                 direction = BorderDirection.TopLeft;
337             }
338             // check top right corner
339             else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness && yPosition < borderInterface.TouchThickness)
340             {
341                 direction = BorderDirection.TopRight;
342             }
343             // check left side
344             else if (xPosition < borderInterface.TouchThickness)
345             {
346                 direction = BorderDirection.Left;
347             }
348             // check right side
349             else if (xPosition > WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2) - borderInterface.TouchThickness)
350             {
351                 direction = BorderDirection.Right;
352             }
353             // check bottom side
354             else if (yPosition > WindowSize.Height + borderHeight + borderInterface.BorderLineThickness - borderInterface.TouchThickness)
355             {
356                 direction = BorderDirection.Bottom;
357             }
358             // check top side
359             else if (yPosition < borderInterface.TouchThickness)
360             {
361                 direction = BorderDirection.Top;
362             }
363             // check move
364             else if ((yPosition > WindowSize.Height) || (isTop == true && yPosition < topView.SizeHeight))
365             {
366                 direction = BorderDirection.Move;
367             }
368
369             return direction;
370         }
371
372         private void DoOverlayMode(bool enable)
373         {
374             if (borderInterface.OverlayMode == true)
375             {
376                 borderInterface.OnOverlayMode(enable);
377                 borderView?.OverlayMode(enable);
378                 if (enable == true)
379                 {
380                     GetBorderWindowBottomLayer().RaiseToTop();
381                 }
382                 else
383                 {
384                     GetBorderWindowBottomLayer().LowerToBottom();
385                 }
386             }
387         }
388
389
390         // Called when the window size has changed.
391         private void OnBorderWindowResized(object sender, Window.ResizedEventArgs e)
392         {
393             Tizen.Log.Info("NUI", $"OnBorderWindowResized {e.WindowSize.Width},{e.WindowSize.Height}\n");
394             int resizeWidth = e.WindowSize.Width;
395             int resizeHeight = e.WindowSize.Height;
396
397             borderInterface.OnResized(resizeWidth, resizeHeight);
398
399              // reset borderHeight
400             borderHeight = 0;
401             if (isTop) borderHeight += topView.SizeHeight;
402             if (isBottom) borderHeight += bottomView.SizeHeight;
403
404             if (borderInterface.OverlayMode == true && IsMaximized() == true)
405             {
406                 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
407                 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth, resizeHeight);
408                 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, 0);
409                 if (contentsView != null)
410                 {
411                     contentsView.SizeHeight = resizeHeight - borderHeight - (float)(borderInterface.BorderLineThickness * 2);
412                 }
413                 DoOverlayMode(true);
414             }
415             else
416             {
417                 float height = (isTop == true) ? topView.SizeHeight : 0;
418                 Interop.ActorInternal.SetSize(GetBorderWindowRootLayer().SwigCPtr, resizeWidth, resizeHeight);
419                 Interop.ActorInternal.SetSize(GetBorderWindowBottomLayer().SwigCPtr, resizeWidth + (float)(borderInterface.BorderLineThickness * 2), resizeHeight + borderHeight + (float)(borderInterface.BorderLineThickness * 2));
420                 Interop.ActorInternal.SetPosition(GetBorderWindowRootLayer().SwigCPtr, 0, height + borderInterface.BorderLineThickness);
421                 if (contentsView != null)
422                 {
423                     contentsView.SizeHeight = resizeHeight;
424                 }
425                 DoOverlayMode(false);
426             }
427
428             if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
429         }
430
431         internal Layer GetBorderWindowBottomLayer()
432         {
433             if (borderWindowBottomLayer == null)
434             {
435                 borderWindowBottomLayer = new Layer();
436                 borderWindowBottomLayer.Name = "BorderWindowBottomLayer";
437                 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
438                 Interop.ActorInternal.SetParentOrigin(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
439                 Interop.Actor.SetAnchorPoint(borderWindowBottomLayer.SwigCPtr, topCentor.SwigCPtr);
440                 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowBottomLayer.SwigCPtr);
441                 Interop.ActorInternal.SetSize(borderWindowBottomLayer.SwigCPtr, WindowSize.Width + (float)(borderInterface.BorderLineThickness * 2), WindowSize.Height + (float)(borderInterface.BorderLineThickness * 2));
442                 borderWindowBottomLayer.SetWindow(this);
443                 borderWindowBottomLayer.LowerToBottom();
444
445                 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
446             }
447             return borderWindowBottomLayer;
448         }
449
450         internal Layer GetBorderWindowRootLayer()
451         {
452             if (borderWindowRootLayer == null)
453             {
454                 borderWindowRootLayer = new Layer();
455                 borderWindowRootLayer.Name = "RootLayer";
456                 using Vector3 topCentor = new Vector3(0.5f, 0.0f, 0.5f);
457                 Interop.ActorInternal.SetParentOrigin(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
458                 Interop.Actor.SetAnchorPoint(borderWindowRootLayer.SwigCPtr, topCentor.SwigCPtr);
459                 Interop.Actor.Add(rootLayer.SwigCPtr, borderWindowRootLayer.SwigCPtr);
460                 Interop.ActorInternal.SetSize(borderWindowRootLayer.SwigCPtr, WindowSize.Width, WindowSize.Height - borderHeight - borderInterface.BorderLineThickness * 2);
461                 float height = (isTop == true) ? topView.SizeHeight : 0;
462                 Interop.ActorInternal.SetPosition(borderWindowRootLayer.SwigCPtr, 0, height + borderInterface.BorderLineThickness);
463                 using PropertyValue propertyValue = new Tizen.NUI.PropertyValue((int)Tizen.NUI.ClippingModeType.ClipToBoundingBox);
464                 Tizen.NUI.Object.SetProperty(borderWindowRootLayer.SwigCPtr, Tizen.NUI.BaseComponents.View.Property.ClippingMode, propertyValue);
465
466                 if (NDalicPINVOKE.SWIGPendingException.Pending) { throw NDalicPINVOKE.SWIGPendingException.Retrieve(); }
467             }
468
469             return borderWindowRootLayer;
470         }
471
472         internal void DisposeBorder()
473         {
474             Resized -= OnBorderWindowResized;
475             borderInterface.Dispose();
476             GetBorderWindowBottomLayer().Dispose();
477         }
478
479         private void convertBorderWindowSizeToRealWindowSize(Uint16Pair size)
480         {
481             if (isBorderWindow == true)
482             {
483                 var height = (ushort)(size.GetHeight() + borderHeight + borderInterface.BorderLineThickness * 2);
484                 var width = (ushort)(size.GetWidth() + borderInterface.BorderLineThickness * 2);
485                 size.SetHeight(height);
486                 size.SetWidth(width);
487             }
488         }
489
490         private void convertRealWindowSizeToBorderWindowSize(Uint16Pair size)
491         {
492             if (isBorderWindow == true && !(borderInterface.OverlayMode == true && IsMaximized() == true))
493             {
494                 var height = (ushort)(size.GetHeight() - borderHeight - borderInterface.BorderLineThickness * 2);
495                 var width = (ushort)(size.GetWidth() - borderInterface.BorderLineThickness * 2);
496                 size.SetHeight(height);
497                 size.SetWidth(width);
498             }
499         }
500         #endregion //Methods
501
502         #region Structs
503         #endregion //Structs
504
505         #region Classes
506         // View class for border view.
507         private class BorderView : View
508         {
509             private bool overlayEnabled = false;
510
511             /// <summary>
512             /// Set whether or not it is in overlay mode.
513             /// The borderView's OverlayMode means that it is displayed at the top of the screen.
514             /// In this case, the borderView should pass the event so that lower layers can receive the event.
515             /// </summary>
516             /// <param name="enabled">The true if borderView is Overlay mode. false otherwise.</param>
517             [EditorBrowsable(EditorBrowsableState.Never)]
518             public void OverlayMode(bool enabled)
519             {
520                 overlayEnabled = enabled;
521             }
522             
523             protected override bool HitTest(Touch touch)
524             {
525                 // If borderView is in overlay mode, pass the hittest.
526                 if (overlayEnabled == true)
527                 {
528                     return false;
529                 }
530                 return true;
531             }
532         }
533         #endregion //Classes
534     }
535 }