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.
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
25 /// This class creates a border UI.
27 [EditorBrowsable(EditorBrowsableState.Never)]
28 public class DefaultBorder : IBorderInterface
30 #region Constant Fields
31 private static readonly string ResourcePath = FrameworkInformation.ResourcePath;
32 private static readonly string MinimalizeIcon = ResourcePath + "minimalize.png";
33 private static readonly string MaximalizeIcon = ResourcePath + "maximalize.png";
34 private static readonly string CloseIcon = ResourcePath + "close.png";
35 private static readonly string LeftCornerIcon = ResourcePath + "leftCorner.png";
36 private static readonly string RightCornerIcon = ResourcePath + "rightCorner.png";
38 private static readonly string DarkMinimalizeIcon = ResourcePath + "dark_minimalize.png";
39 private static readonly string DarkPreviousIcon = ResourcePath + "dark_smallwindow.png";
40 private static readonly string DarkCloseIcon = ResourcePath + "dark_close.png";
41 private static readonly string DarkLeftCornerIcon = ResourcePath + "dark_leftCorner.png";
42 private static readonly string DarkRightCornerIcon = ResourcePath + "dark_rightCorner.png";
45 private const uint DefaultHeight = 50;
46 private const uint DefaultLineThickness = 5;
47 private const uint DefaultTouchThickness = 20;
48 private static readonly Color DefaultBackgroundColor = new Color(1, 1, 1, 0.3f);
49 private static readonly Color DefaultClickedBackgroundColor = new Color(1, 1, 1, 0.4f);
50 private static readonly Size2D DefaultMinSize = new Size2D(100, 0);
51 #endregion //Constant Fields
55 private Color backgroundColor;
56 private View rootView;
57 private View borderView;
59 private ImageView minimalizeIcon;
60 private ImageView maximalizeIcon;
61 private ImageView closeIcon;
62 private ImageView leftCornerIcon;
63 private ImageView rightCornerIcon;
65 private Window.BorderDirection direction = Window.BorderDirection.None;
66 private float preScale = 0;
68 private View windowView = null;
69 private bool isWinGestures = false;
72 private CurrentGesture currentGesture = CurrentGesture.None;
73 private bool disposed = false;
77 private PanGestureDetector borderPanGestureDetector;
78 private PinchGestureDetector borderPinchGestureDetector;
79 private PanGestureDetector winPanGestureDetector;
80 private TapGestureDetector winTapGestureDetector;
84 private enum CurrentGesture
96 /// The thickness of the border.
98 [EditorBrowsable(EditorBrowsableState.Never)]
99 public uint BorderLineThickness {get; set;}
102 /// The thickness of the border's touch area.
104 [EditorBrowsable(EditorBrowsableState.Never)]
105 public uint TouchThickness {get; set;}
108 /// The height of the border.
110 [EditorBrowsable(EditorBrowsableState.Never)]
111 public uint BorderHeight {get; set;}
114 /// The minimum size by which the window will small.
116 [EditorBrowsable(EditorBrowsableState.Never)]
117 public Size2D MinSize {get; set;}
120 /// The maximum size by which the window will big.
122 [EditorBrowsable(EditorBrowsableState.Never)]
123 public Size2D MaxSize {get; set;}
126 /// The window with borders added.
128 [EditorBrowsable(EditorBrowsableState.Never)]
129 public Window BorderWindow {get; set;}
132 /// Whether overlay mode.
133 /// If overlay mode is true, the border area is hidden when the window is maximized.
134 /// And if you touched at screen, the border area is shown on the screen.
135 /// Default value is false;
137 [EditorBrowsable(EditorBrowsableState.Never)]
138 public bool OverlayMode {get; set;}
140 [EditorBrowsable(EditorBrowsableState.Never)]
141 public DefaultBorder()
143 BorderLineThickness = DefaultLineThickness;
144 TouchThickness = DefaultTouchThickness;
145 BorderHeight = DefaultHeight;
146 MinSize = DefaultMinSize;
152 /// Create border UI. Users can override this method to draw border UI.
154 [EditorBrowsable(EditorBrowsableState.Never)]
155 public virtual void CreateBorderView(View rootView)
157 if (rootView == null)
161 this.rootView = rootView;
162 rootView.BackgroundColor = DefaultBackgroundColor;
163 rootView.CornerRadius = new Vector4(0.03f, 0.03f, 0.03f, 0.03f);
164 rootView.CornerRadiusPolicy = VisualTransformPolicyType.Relative;
166 borderView = new View()
168 Layout = new LinearLayout()
170 LinearAlignment = LinearLayout.Alignment.End,
171 LinearOrientation = LinearLayout.Orientation.Horizontal,
173 WidthSpecification = LayoutParamPolicies.MatchParent,
174 HeightSpecification = LayoutParamPolicies.MatchParent,
177 minimalizeIcon = new ImageView()
179 ResourceUrl = MinimalizeIcon,
180 PositionUsesPivotPoint = true,
181 PivotPoint = PivotPoint.BottomLeft,
182 ParentOrigin = ParentOrigin.BottomLeft,
185 maximalizeIcon = new ImageView()
187 ResourceUrl = MaximalizeIcon,
188 PositionUsesPivotPoint = true,
189 PivotPoint = PivotPoint.BottomLeft,
190 ParentOrigin = ParentOrigin.BottomLeft,
193 closeIcon = new ImageView()
195 ResourceUrl = CloseIcon,
196 PositionUsesPivotPoint = true,
197 PivotPoint = PivotPoint.BottomLeft,
198 ParentOrigin = ParentOrigin.BottomLeft,
201 leftCornerIcon = new ImageView()
203 ResourceUrl = LeftCornerIcon,
204 PositionUsesPivotPoint = true,
205 PivotPoint = PivotPoint.BottomLeft,
206 ParentOrigin = ParentOrigin.BottomLeft,
209 rightCornerIcon = new ImageView()
211 ResourceUrl = RightCornerIcon,
212 PositionUsesPivotPoint = true,
213 PivotPoint = PivotPoint.BottomLeft,
214 ParentOrigin = ParentOrigin.BottomLeft,
217 rootView.Add(leftCornerIcon);
218 borderView.Add(minimalizeIcon);
219 borderView.Add(maximalizeIcon);
220 borderView.Add(closeIcon);
221 borderView.Add(rightCornerIcon);
222 rootView.Add(borderView);
224 minimalizeIcon.TouchEvent += OnMinimizeIconTouched;
225 maximalizeIcon.TouchEvent += OnMaximizeIconTouched;
226 closeIcon.TouchEvent += OnCloseIconTouched;
227 leftCornerIcon.TouchEvent += OnLeftCornerIconTouched;
228 rightCornerIcon.TouchEvent += OnRightCornerIconTouched;
231 /// Determines the behavior of pinch gesture.
232 private void OnPinchGestureDetected(object source, PinchGestureDetector.DetectedEventArgs e)
238 if (e.PinchGesture.State == Gesture.StateType.Started)
240 preScale = e.PinchGesture.Scale;
242 else if (e.PinchGesture.State == Gesture.StateType.Finished || e.PinchGesture.State == Gesture.StateType.Cancelled)
244 if (preScale > e.PinchGesture.Scale)
246 if (BorderWindow.IsMaximized())
248 BorderWindow.Maximize(false);
252 BorderWindow.Minimize(true);
257 BorderWindow.Maximize(true);
262 /// Determines the behavior of borders.
263 private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
269 PanGesture panGesture = e.PanGesture;
271 if (panGesture.State == Gesture.StateType.Started)
273 direction = BorderWindow.GetDirection(panGesture.Position.X, panGesture.Position.Y);
274 if (direction == Window.BorderDirection.Move)
276 if (BorderWindow.IsMaximized() == true)
278 BorderWindow.Maximize(false);
282 BorderWindow.RequestMoveToServer();
285 else if (direction != Window.BorderDirection.None)
288 BorderWindow.RequestResizeToServer((Window.ResizeDirection)direction);
291 else if (panGesture.State == Gesture.StateType.Continuing)
293 if (direction == Window.BorderDirection.BottomLeft || direction == Window.BorderDirection.BottomRight || direction == Window.BorderDirection.TopLeft || direction == Window.BorderDirection.TopRight)
295 BorderWindow.WindowSize += new Size2D((int)panGesture.ScreenDisplacement.X, (int)panGesture.ScreenDisplacement.Y);
297 else if (direction == Window.BorderDirection.Left || direction == Window.BorderDirection.Right)
299 BorderWindow.WindowSize += new Size2D((int)panGesture.ScreenDisplacement.X, 0);
301 else if (direction == Window.BorderDirection.Bottom || direction == Window.BorderDirection.Top)
303 BorderWindow.WindowSize += new Size2D(0, (int)panGesture.ScreenDisplacement.Y);
305 else if (direction == Window.BorderDirection.Move)
307 BorderWindow.WindowPosition += new Position2D((int)panGesture.ScreenDisplacement.X, (int)panGesture.ScreenDisplacement.Y);
310 else if (panGesture.State == Gesture.StateType.Finished || panGesture.State == Gesture.StateType.Cancelled)
312 direction = Window.BorderDirection.None;
313 ClearWindowGesture();
319 /// This is an event callback when the left corner icon is touched.
321 [EditorBrowsable(EditorBrowsableState.Never)]
322 public virtual bool OnLeftCornerIconTouched(object sender, View.TouchEventArgs e)
328 if (e.Touch.GetState(0) == PointStateType.Down)
330 ClearWindowGesture();
332 BorderWindow.RequestResizeToServer(Window.ResizeDirection.BottomLeft);
338 ///This is an event callback when the right corner icon is touched.
340 [EditorBrowsable(EditorBrowsableState.Never)]
341 public virtual bool OnRightCornerIconTouched(object sender, View.TouchEventArgs e)
347 if (e.Touch.GetState(0) == PointStateType.Down)
349 ClearWindowGesture();
351 BorderWindow.RequestResizeToServer(Window.ResizeDirection.BottomRight);
358 /// This is an event callback when the minimize button is touched.
360 [EditorBrowsable(EditorBrowsableState.Never)]
361 public virtual bool OnMinimizeIconTouched(object sender, View.TouchEventArgs e)
367 if (e.Touch.GetState(0) == PointStateType.Up)
369 ClearWindowGesture();
370 BorderWindow.Minimize(true);
376 /// This is an event callback when the maximum button is touched.
378 [EditorBrowsable(EditorBrowsableState.Never)]
379 public virtual bool OnMaximizeIconTouched(object sender, View.TouchEventArgs e)
385 if (e.Touch.GetState(0) == PointStateType.Up)
387 ClearWindowGesture();
388 if (BorderWindow.IsMaximized())
390 BorderWindow.Maximize(false);
394 BorderWindow.Maximize(true);
401 /// This is an event callback when the close button is touched.
403 [EditorBrowsable(EditorBrowsableState.Never)]
404 public virtual bool OnCloseIconTouched(object sender, View.TouchEventArgs e)
410 if (e.Touch.GetState(0) == PointStateType.Up)
412 BorderWindow.Destroy();
419 private void UpdateIcons()
421 if (BorderWindow != null && rootView != null)
423 if (BorderWindow.IsMaximized() == true)
425 if (maximalizeIcon != null)
427 maximalizeIcon.ResourceUrl = DarkPreviousIcon;
429 if (minimalizeIcon != null)
431 minimalizeIcon.ResourceUrl = DarkMinimalizeIcon;
433 if (closeIcon != null)
435 closeIcon.ResourceUrl = DarkCloseIcon;
437 if (leftCornerIcon != null)
439 leftCornerIcon.ResourceUrl = DarkLeftCornerIcon;
441 if (rightCornerIcon != null)
443 rightCornerIcon.ResourceUrl = DarkRightCornerIcon;
445 rootView.CornerRadius = new Vector4(0, 0, 0, 0);
446 rootView.CornerRadiusPolicy = VisualTransformPolicyType.Relative;
447 BorderWindow.SetTransparency(false);
451 if (maximalizeIcon != null)
453 maximalizeIcon.ResourceUrl = MaximalizeIcon;
455 if (minimalizeIcon != null)
457 minimalizeIcon.ResourceUrl = MinimalizeIcon;
459 if (closeIcon != null)
461 closeIcon.ResourceUrl = CloseIcon;
463 if (leftCornerIcon != null)
465 leftCornerIcon.ResourceUrl = LeftCornerIcon;
467 if (rightCornerIcon != null)
469 rightCornerIcon.ResourceUrl = RightCornerIcon;
471 rootView.CornerRadius = new Vector4(0.03f, 0.03f, 0.03f, 0.03f);
472 rootView.CornerRadiusPolicy = VisualTransformPolicyType.Relative;
473 BorderWindow.SetTransparency(true);
480 /// Called after the border UI is created.
482 /// <param name="rootView">The root view on which the border.</param>
483 [EditorBrowsable(EditorBrowsableState.Never)]
484 public virtual void OnCreated(View rootView)
486 if (rootView == null)
490 // Register to resize and move through pan gestures.
491 borderPanGestureDetector = new PanGestureDetector();
492 borderPanGestureDetector.Attach(rootView);
493 borderPanGestureDetector.Detected += OnPanGestureDetected;
495 // Register touch event for effect when border is touched.
496 rootView.LeaveRequired = true;
497 rootView.TouchEvent += (s, e) =>
499 if (e.Touch.GetState(0) == PointStateType.Started)
501 backgroundColor = new Color(rootView.BackgroundColor);
502 rootView.BackgroundColor = DefaultClickedBackgroundColor;
504 else if (e.Touch.GetState(0) == PointStateType.Finished ||
505 e.Touch.GetState(0) == PointStateType.Leave ||
506 e.Touch.GetState(0) == PointStateType.Interrupted)
508 rootView.BackgroundColor = backgroundColor;
513 borderPinchGestureDetector = new PinchGestureDetector();
514 borderPinchGestureDetector.Attach(rootView);
515 borderPinchGestureDetector.Detected += OnPinchGestureDetected;
517 AddInterceptGesture();
521 // Register an intercept touch event on the window.
522 private void AddInterceptGesture()
524 isWinGestures = false;
525 BorderWindow.InterceptTouchEvent += OnWinInterceptedTouch;
528 // Intercept touch on window.
529 private bool OnWinInterceptedTouch(object sender, Window.TouchEventArgs e)
531 if (e.Touch.GetState(0) == PointStateType.Stationary && e.Touch.GetPointCount() == 2)
533 if (isWinGestures == false && timer == null)
535 timer = new Timer(300);
536 timer.Tick += OnTick;
542 currentGesture = CurrentGesture.None;
553 // If two finger long press is done, create a windowView.
554 // then, Register a gesture on the windowView to do a resize or move.
555 private bool OnTick(object o, Timer.TickEventArgs e)
557 windowView = new View()
559 WidthResizePolicy = ResizePolicyType.FillToParent,
560 HeightResizePolicy = ResizePolicyType.FillToParent,
561 BackgroundColor = new Color(1, 1, 1, 0.5f),
563 windowView.TouchEvent += (s, e) =>
567 BorderWindow.Add(windowView);
569 winTapGestureDetector = new TapGestureDetector();
570 winTapGestureDetector.Attach(windowView);
571 winTapGestureDetector.SetMaximumTapsRequired(3);
572 winTapGestureDetector.Detected += OnWinTapGestureDetected;
574 winPanGestureDetector = new PanGestureDetector();
575 winPanGestureDetector.Attach(windowView);
576 winPanGestureDetector.Detected += OnWinPanGestureDetected;
578 BorderWindow.InterceptTouchEvent -= OnWinInterceptedTouch;
579 isWinGestures = true;
583 // Behavior when the window is tapped.
584 private void OnWinTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e)
586 if (currentGesture <= CurrentGesture.TapGesture)
588 currentGesture = CurrentGesture.TapGesture;
589 if (e.TapGesture.NumberOfTaps == 2)
591 if (BorderWindow.IsMaximized() == false)
593 BorderWindow.Maximize(true);
597 BorderWindow.Maximize(false);
602 ClearWindowGesture();
607 // Window moves through pan gestures.
608 private void OnWinPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
610 if (currentGesture <= CurrentGesture.PanGesture /*&& panGesture.NumberOfTouches == 1*/)
612 PanGesture panGesture = e.PanGesture;
614 if (panGesture.State == Gesture.StateType.Started)
616 currentGesture = CurrentGesture.PanGesture;
617 if (BorderWindow.IsMaximized() == true)
619 BorderWindow.Maximize(false);
623 BorderWindow.RequestMoveToServer();
626 else if (panGesture.State == Gesture.StateType.Finished || panGesture.State == Gesture.StateType.Cancelled)
628 currentGesture = CurrentGesture.None;
629 ClearWindowGesture();
634 private void ClearWindowGesture()
638 winPanGestureDetector.Dispose();
639 winTapGestureDetector.Dispose();
641 isWinGestures = false;
642 BorderWindow.Remove(windowView);
643 BorderWindow.InterceptTouchEvent += OnWinInterceptedTouch;
648 /// Called when requesting a resize
650 [EditorBrowsable(EditorBrowsableState.Never)]
651 public virtual void OnRequestResize()
656 /// Called when the window is resized.
658 /// <param name="width">The width of the resized window</param>
659 /// <param name="height">The height of the resized window</param>
660 [EditorBrowsable(EditorBrowsableState.Never)]
661 public virtual void OnResized(int width, int height)
666 [EditorBrowsable(EditorBrowsableState.Never)]
667 protected virtual void Dispose(bool disposing)
675 ClearWindowGesture();
676 if (BorderWindow != null)
678 BorderWindow.InterceptTouchEvent -= OnWinInterceptedTouch;
680 borderPanGestureDetector?.Dispose();
681 borderPinchGestureDetector?.Dispose();
682 backgroundColor?.Dispose();
683 minimalizeIcon?.Dispose();
684 maximalizeIcon?.Dispose();
685 closeIcon?.Dispose();
686 leftCornerIcon?.Dispose();
687 rightCornerIcon?.Dispose();
689 windowView?.Dispose();
690 borderView?.Dispose();
699 [EditorBrowsable(EditorBrowsableState.Never)]
700 public void Dispose()
703 global::System.GC.SuppressFinalize(this);