aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Touch/Controls/TouchNotificationBar.cs
blob: ccd27333ab76156afeac4df246e9311ff60c102d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
generated by cgit v1.3.1 (git 2.54.0) at 2026-08-02 15:05:19 +0000
 


9'>359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Tango.Core.Commands;
using Tango.Core.EventArguments;
using Tango.Core.ExtensionMethods;

namespace Tango.Touch.Controls
{
    public class TouchNotificationBar : ContentControl
    {
        private const double HEIGHT_RATIO = 0.75;

        private bool _isMouseDown;
        private Point _mouseDownLocation;
        private bool _isMoving;
        private Point _downLocation;
        private TouchDevice _touchDevice;
        private double _last_manipulation_delta;
        private DateTime _last_delta_measure_time;
        private double _last_mouse_y;



        private Border _border_notifications;
        private Border _border_drag;
        private Grid _grid_container;
        private Grid _grid_mask;
        private ItemsControl _items_control;
        private Grid _notification_counter_grid;
        private LightTouchScrollViewer _scrollViewer;

        static TouchNotificationBar()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TouchNotificationBar), new FrameworkPropertyMetadata(typeof(TouchNotificationBar)));
        }

        public String ItemExpandedPropertyPath
        {
            get { return (String)GetValue(ItemExpandedPropertyPathProperty); }
            set { SetValue(ItemExpandedPropertyPathProperty, value); }
        }
        public static readonly DependencyProperty ItemExpandedPropertyPathProperty =
            DependencyProperty.Register("ItemExpandedPropertyPath", typeof(String), typeof(TouchNotificationBar), new PropertyMetadata(null));

        public bool HasNotifications
        {
            get { return (bool)GetValue(HasNotificationsProperty); }
            set { SetValue(HasNotificationsProperty, value); }
        }
        public static readonly DependencyProperty HasNotificationsProperty =
            DependencyProperty.Register("HasNotifications", typeof(bool), typeof(TouchNotificationBar), new PropertyMetadata(false, (d, e) => (d as TouchNotificationBar).OnHasNotificationsChanged()));

        public IList Notifications
        {
            get { return (IList)GetValue(NotificationsProperty); }
            set { SetValue(NotificationsProperty, value); }
        }
        public static readonly DependencyProperty NotificationsProperty =
            DependencyProperty.Register("Notifications", typeof(IList), typeof(TouchNotificationBar), new PropertyMetadata(null, (d, e) => (d as TouchNotificationBar).OnNotificationsChanged()));

        public DataTemplate NotificationTemplate
        {
            get { return (DataTemplate)GetValue(NotificationTemplateProperty); }
            set { SetValue(NotificationTemplateProperty, value); }
        }
        public static readonly DependencyProperty NotificationTemplateProperty =
            DependencyProperty.Register("NotificationTemplate", typeof(DataTemplate), typeof(TouchNotificationBar), new PropertyMetadata(null));

        public double CurrentMinHeight
        {
            get { return (double)GetValue(CurrentMinHeightProperty); }
            set { SetValue(CurrentMinHeightProperty, value); }
        }
        public static readonly DependencyProperty CurrentMinHeightProperty =
            DependencyProperty.Register("CurrentMinHeight", typeof(double), typeof(TouchNotificationBar), new PropertyMetadata(0.0));

        public bool IsExpanded
        {
            get { return (bool)GetValue(IsExpandedProperty); }
            set { SetValue(IsExpandedProperty, value); }
        }
        public static readonly DependencyProperty IsExpandedProperty =
            DependencyProperty.Register("IsExpanded", typeof(bool), typeof(TouchNotificationBar), new PropertyMetadata(false));

        public RelayCommand NotificationPressedCommand
        {
            get { return (RelayCommand)GetValue(NotificationPressedCommandProperty); }
            set { SetValue(NotificationPressedCommandProperty, value); }
        }
        public static readonly DependencyProperty NotificationPressedCommandProperty =
            DependencyProperty.Register("NotificationPressedCommand", typeof(RelayCommand), typeof(TouchNotificationBar), new PropertyMetadata(null));

        public Visibility NotificationBarVisibility
        {
            get { return (Visibility)GetValue(NotificationBarVisibilityProperty); }
            set { SetValue(NotificationBarVisibilityProperty, value); }
        }
        public static readonly DependencyProperty NotificationBarVisibilityProperty =
            DependencyProperty.Register("NotificationBarVisibility", typeof(Visibility), typeof(TouchNotificationBar), new PropertyMetadata(Visibility.Visible));
        public TouchNotificationBar()
        {
            Loaded += TouchNotificationBar_Loaded;

            NotificationPressedCommand = new RelayCommand(() =>
            {
                if (IsExpanded && !_scrollViewer.IsAfterScrolling)
                {
                    CloseNotifications();
                }
            });
        }

        private void TouchNotificationBar_Loaded(object sender, RoutedEventArgs e)
        {
            _border_drag.MaxHeight = ActualHeight;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _border_notifications = GetTemplateChild("PART_BorderNotifications") as Border;
            _border_drag = GetTemplateChild("PART_BorderDrag") as Border;
            _grid_container = GetTemplateChild("PART_Grid_Container") as Grid;
            _grid_mask = GetTemplateChild("PART_GridMask") as Grid;
            _items_control = GetTemplateChild("PART_ItemsControl") as ItemsControl;
            _notification_counter_grid = GetTemplateChild("PART_NotificationCounterGrid") as Grid;
            _scrollViewer = GetTemplateChild("PART_ScrollViewer") as LightTouchScrollViewer;

            //border_notifications.IsManipulationEnabled = true;
            _border_drag.RegisterForPreviewMouseOrTouchDown(GridNotificationMouseDown);
            _border_drag.RegisterForMouseOrTouchMove(GridNotificationMouseMove);
            _border_drag.RegisterForPreviewMouseOrTouchUp(GridNotificationMouseUp);

            _border_drag.SizeChanged += Drag_Border_SizeChanged;
            _items_control.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
        }

        private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            if (_items_control.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
            {
                if (_items_control.Items.Count > 0)
                {
                    var element = _items_control.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement;

                    if (element != null)
                    {
                        element.RegisterForLoadedOrNow((x, y) =>
                        {
                            CurrentMinHeight = element.FindChild<UserControl>().MinHeight;
                        });
                    }
                }
                else
                {
                    CurrentMinHeight = 0;
                }
            }
        }

        private FrameworkElement GetLastNotification()
        {
            return _items_control.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement;
        }

        private void Drag_Border_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            _grid_mask.Opacity = _border_drag.ActualHeight / ActualHeight;

            ResizeNotifications();

            if (e.NewSize.Height > CurrentMinHeight && CurrentMinHeight > 0)
            {
                _notification_counter_grid.Visibility = Visibility.Hidden;
            }
            else
            {
                _notification_counter_grid.Visibility = Visibility.Visible;
            }
        }

        private void ResizeNotifications()
        {
            for (int i = 0; i < _items_control.Items.Count; i++)
            {
                FrameworkElement element = _items_control.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
                ResizeNotification(element);
            }
        }

        private void ResizeNotification(FrameworkElement element)
        {
            var control = element.FindChild<UserControl>();

            if (control != null && !double.IsNaN(_border_drag.Height))
            {
                control.Height = ((_border_drag.Height - CurrentMinHeight) / ActualHeight) * (control.MaxHeight - control.MinHeight) + control.MinHeight;
            }
        }

        private void OnHasNotificationsChanged()
        {
            DoubleAnimation ani = new DoubleAnimation();
            ani.Duration = TimeSpan.FromSeconds(0.2);

            _grid_container.EnsureHeight();

            if (HasNotifications)
            {
                var last = GetLastNotification();

                last.RegisterForLoadedOrNow((x, e) =>
                {
                    ani.To = last.ActualHeight;
                    _grid_container.BeginAnimation(Grid.HeightProperty, ani);
                    _border_notifications.Height = last.ActualHeight;
                });
            }
            else
            {
                CurrentMinHeight = 0;
                ani.To = 0;
                CloseNotifications();
                _grid_container.BeginAnimation(Grid.HeightProperty, ani);
            }
        }

        private void OnNotificationsChanged()
        {
            UpdateExpanded(IsExpanded);

            if (Notifications is INotifyCollectionChanged)
            {
                (Notifications as INotifyCollectionChanged).CollectionChanged += (_, e) =>
                {
                    this.BeginInvoke(() =>
                    {
                        if (e.NewItems != null && e.NewItems.Count > 0)
                        {
                            foreach (var item in e.NewItems.Cast<Object>().ToList())
                            {
                                var element = _items_control.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
                                if (element != null)
                                {
                                    element.RegisterForLoadedOrNow((___, ____) =>
                                    {
                                        ResizeNotification(element);
                                    });
                                }
                            }
                        }

                        UpdateExpanded(IsExpanded);
                    });
                };
            }
        }

        private void UpdateExpanded(bool expanded)
        {
            if (ItemExpandedPropertyPath != null)
            {
                foreach (var item in Notifications.Cast<Object>().ToList())
                {
                    item.SetPropertyValueByPath(ItemExpandedPropertyPath, expanded);
                }
            }
        }

        private void OpenNotifications()
        {
            _border_drag.EnsureHeight();
            _border_notifications.EnsureHeight();

            _border_drag.StartDoubleAnimation(Border.HeightProperty, TimeSpan.FromSeconds(0.2), _border_drag.MaxHeight, null, null, 1, () =>
             {
                 IsExpanded = true;
             });
            _border_notifications.StartDoubleAnimation(Border.HeightProperty, TimeSpan.FromSeconds(0.2), ActualHeight * HEIGHT_RATIO, null, null, 1);

            UpdateExpanded(true);
        }

        private void CloseNotifications()
        {
            _border_drag.EnsureHeight();
            _border_notifications.EnsureHeight();

            _border_notifications.StartDoubleAnimation(Border.HeightProperty, TimeSpan.FromSeconds(0.2), CurrentMinHeight, null, 1, null, () =>
              {
                  _border_notifications.BeginAnimation(Border.HeightProperty, null);
                  _border_notifications.Height = CurrentMinHeight;

                  for (int i = 0; i < _items_control.Items.Count; i++)
                  {
                      FrameworkElement element = _items_control.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;

                      var control = element.FindChild<UserControl>();

                      if (control != null)
                      {
                          control.Height = control.MinHeight;
                      }
                  }
              });

            _border_drag.StartDoubleAnimation(Border.HeightProperty, TimeSpan.FromSeconds(0.2), CurrentMinHeight, null, 1, null, () =>
            {
                _border_drag.BeginAnimation(Border.HeightProperty, null);
                _border_drag.Height = double.NaN;
            });

            ThicknessAnimation tAni = new ThicknessAnimation();
            tAni.Duration = TimeSpan.FromSeconds(0.3);
            tAni.To = new Thickness(0);
            _border_notifications.BeginAnimation(Border.BorderThicknessProperty, tAni);

            UpdateExpanded(false);

            IsExpanded = false;

            Task.Delay(400).ContinueWith((x) =>
            {
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    _scrollViewer.ScrollToTop();
                }));
            });
        }

        private void GridNotificationMouseUp(object sender, MouseOrTouchEventArgs e)
        {
            if (_isMouseDown)
            {
                _isMouseDown = false;

                if (_isMoving)
                {
                    if (_touchDevice != null)
                    {
                        _border_drag.ReleaseTouchCapture(_touchDevice);
                        _touchDevice = null;
                    }
                    else
                    {
                        _border_drag.ReleaseMouseCapture();
                    }

                    e.Handled = true;
                    _isMoving = false;

                    if ((e.GetPosition(this).Y > ActualHeight / 2 && _last_manipulation_delta > 0) || _last_manipulation_delta > 20)
                    {
                        OpenNotifications();
                    }
                    else if ((e.GetPosition(this).Y < ActualHeight / 2 && _last_manipulation_delta < 0) || _last_manipulation_delta < 20)
                    {
                        CloseNotifications();
                    }
                }

                _scrollViewer.DisableScrolling = false;
            }
        }

        private void GridNotificationMouseMove(object sender, MouseOrTouchEventArgs e)
        {
            if (_isMouseDown)
            {
                if (Math.Abs(e.Location.Y - _downLocation.Y) > 5)
                {
                    _isMoving = true;
                }

                if (_isMoving)
                {
                    DateTime curTime = DateTime.Now;

                    if (curTime > _last_delta_measure_time.AddMilliseconds(10))
                    {
                        _last_delta_measure_time = curTime;

                        double delta = e.Location.Y - _last_mouse_y;

                        if (delta != 0)
                        {
                            _last_manipulation_delta = delta;
                        }

                        _last_mouse_y = e.Location.Y;
                    }

                    if (e.TouchDevice != null)
                    {
                        _border_drag.CaptureTouch(e.TouchDevice);
                    }
                    else
                    {
                        Mouse.Capture(_border_drag);
                    }

                    _border_drag.Height = Math.Max(e.Location.Y + _mouseDownLocation.Y, CurrentMinHeight);
                    _border_notifications.Height = Math.Min(_border_drag.Height * HEIGHT_RATIO, ActualHeight * HEIGHT_RATIO);

                    _border_drag.BeginAnimation(Border.HeightProperty, null);
                    _border_notifications.BeginAnimation(Border.HeightProperty, null);

                    _border_notifications.BeginAnimation(Border.BorderThicknessProperty, null);

                    if (_border_notifications.ActualHeight > CurrentMinHeight)
                    {
                        _border_notifications.BorderThickness = new Thickness(0, 0, 0, 20);
                    }
                    else
                    {
                        _border_notifications.BorderThickness = new Thickness(0);
                    }
                }
            }
        }

        private void GridNotificationMouseDown(object sender, MouseOrTouchEventArgs e)
        {
            if (!IsExpanded || e.OriginalSource == _border_drag || e.OriginalSource == _border_notifications)
            {
                _mouseDownLocation = new Point(e.Location.X, _border_drag.ActualHeight - e.Location.Y);
                _downLocation = e.Location;
                _last_mouse_y = _downLocation.Y;
                _last_delta_measure_time = DateTime.Now;
                _last_manipulation_delta = 0;
                _scrollViewer.DisableScrolling = true;
                _isMouseDown = true;
            }
        }
    }
}