Example #1
0
        static IControl MakeHittable(IControl control, Space space, Pointer.Callbacks callbacks)
        {
            return(Control.Create(location =>
            {
                control.Mount(
                    new MountLocation.Mutable
                {
                    AvailableSize = location.AvailableSize,
                    IsRooted = location.IsRooted,
                    NativeFrame = ObservableMath.RectangleWithSize(location.NativeFrame.Size),
                });

                var nativeHandle = control.NativeHandle as NSView;

                var view = new HittableView(space, callbacks);
                if (nativeHandle != null)
                {
                    view.AddSubview(nativeHandle);
                }

                location.BindNativeDefaults(view, _dispatcher);

                return view;
            }).WithSize(control.DesiredSize));
        }
Example #2
0
        static IControl Transform(IControl ctrl, IObservable <Matrix> matrix)
        {
            return(Control.Create(location =>
            {
                ctrl.Mount(
                    new MountLocation.Mutable
                {
                    AvailableSize = location.AvailableSize,
                    IsRooted = location.IsRooted,
                    NativeFrame = ObservableMath.RectangleWithSize(ctrl.DesiredSize),
                });

                var nativeHandle = ctrl.NativeHandle as NSView;
                var containerView = new TransformerView();
                containerView.AddSubview(nativeHandle);

                location.BindNativeDefaults(containerView, Fusion.Application.MainThread);
                location.BindNativeProperty(Fusion.Application.MainThread, "transformation", matrix,
                                            mat =>
                {
                    containerView.ApplyTransformation(mat);
                });

                return containerView;
            })
                   .WithSize(
                       ObservableMath
                       .RectangleWithSize(ctrl.DesiredSize)
                       .Transpose()
                       .Transform(matrix)
                       .Transpose()
                       .Size));
        }
Example #3
0
        public static void Initialize(IScheduler dispatcher)
        {
            Dragging.Implementation.OnDragOver = (control, canDrop, drop, enter, leave) =>
            {
                control = Layout.Layer(control).WithSize(control.DesiredSize);
                return(Control.Create(location =>
                {
                    control.Mount(
                        new MountLocation.Mutable
                    {
                        AvailableSize = location.AvailableSize,
                        IsRooted = location.IsRooted,
                        NativeFrame = ObservableMath.RectangleWithSize(location.NativeFrame.Size),
                    });

                    Func <object, bool> canDropNow = o => false;
                    control.BindNativeProperty(Fusion.Application.MainThread, "canDrop", canDrop, c => canDropNow = c);

                    var nativeHandle = (NSView)control.NativeHandle;

                    var view = new DragDestinationView(o => canDropNow(o))
                    {
                        OnDrop = drop,
                        OnEnter = enter,
                        OnExit = leave
                    };

                    view.AddSubview(nativeHandle);

                    location.BindNativeDefaults(view, dispatcher);

                    return view;
                }).WithSize(control.DesiredSize));
            };
        }
Example #4
0
        public static object Initialize(
            IObservable <IBinaryMessage> messagesFrom,
            IObserver <IBinaryMessage> messagesTo,
            IScheduler dispatcher,
            IObservable <double> perFrame,
            Size <IObservable <Points> > size)
        {
            var pluginManager = new PluginManager(
                new PluginContext
            {
                Input      = messagesFrom,
                PerFrame   = perFrame,
                Dispatcher = dispatcher
            });

            pluginManager.Overlay.Mount(
                new MountLocation.Mutable
            {
                AvailableSize = size,
                NativeFrame   = ObservableMath.RectangleWithSize(size),
                IsRooted      = Observable.Return(true),
            });

            pluginManager.Output.Subscribe(messagesTo);

            return(pluginManager.Overlay.NativeHandle);
        }
        public static void MountRoot(this IControl control)
        {
            var size = Size.Create(Observable.Return(new Points(4)), Observable.Return(new Points(4)));

            control.Mount(
                new MountLocation.Mutable
            {
                IsRooted      = Observable.Return(true),
                AvailableSize = size,
                NativeFrame   = ObservableMath.RectangleWithSize(size)
            });
        }
Example #6
0
        static IControl Transform(IControl ctrl, IObservable <Matrix> matrix)
        {
            return(Control.Create(
                       self =>
            {
                ctrl.Mount(
                    new MountLocation.Mutable
                {
                    AvailableSize = self.AvailableSize,
                    IsRooted = self.IsRooted,
                    NativeFrame = ObservableMath.RectangleWithSize(ctrl.DesiredSize),
                });

                var nativeThing = ctrl.NativeHandle as FrameworkElement;
                if (nativeThing == null)
                {
                    return null;
                }

                var wrapper = new Canvas
                {
                    Children =
                    {
                        nativeThing
                    }
                };

                self.BindNativeDefaults(wrapper, _dispatcher);
                self.BindNativeProperty(
                    Fusion.Application.MainThread,
                    "transform",
                    matrix,
                    mat =>
                {
                    //element.RenderTransformOrigin = new System.Windows.Point(element.Width / 2, element.Height / 2);
                    nativeThing.LayoutTransform = mat.ToWpfScaleRotation();
                });



                return wrapper;
            }).WithSize(
                       Rectangle
                       .FromPositionSize(
                           Point.Create(ctrl.DesiredSize.Width.Div(-2), ctrl.DesiredSize.Height.Div(-2)),
                           ctrl.DesiredSize)
                       .Transpose()
                       .Transform(matrix)
                       .Transpose()
                       .Size));
        }
        public static void Initialize(IScheduler dispatcher)
        {
            Layout.Implementation.LayerControls = (childFactory) =>
                                                  Control.Create(ctrl =>
            {
                var element    = new NSView();
                element.Hidden = true;

                ctrl.BindNativeDefaults(element, dispatcher);

                childFactory(ctrl)
                // IMPORTANT: ConnectWhile has to be done first since else we'll lose all changes done to the children list while the ctrl is unrooted.
                // which breaks diffing, and we get dangling views (views that aren't removed).
                .ConnectWhile(ctrl.IsRooted)
                .DiffSequence()
                .ObserveOn(Fusion.Application.MainThread)
                .Subscribe(children =>
                {
                    element.Hidden = children.Current.Count == 0;
                    foreach (var child in children.Removed)
                    {
                        var nativeChild = child.NativeHandle as NSView;
                        if (nativeChild != null)
                        {
                            nativeChild.RemoveFromSuperview();
                        }

                        child.Mount(MountLocation.Unmounted);
                    }

                    foreach (var child in children.Added)
                    {
                        child.Mount(
                            new MountLocation.Mutable
                        {
                            IsRooted      = ctrl.IsRooted,
                            AvailableSize = ctrl.AvailableSize,
                            NativeFrame   = ObservableMath.RectangleWithSize(ctrl.NativeFrame.Size),
                        });

                        var nativeChild = child.NativeHandle as NSView;
                        if (nativeChild == null)
                        {
                            continue;
                        }

                        if (nativeChild.Superview != null)
                        {
                            nativeChild.RemoveFromSuperview();
                        }

                        NSView nativeChildBelow = null;
                        for (int i = children.Current.IndexOf(child) - 1; i >= 0; i--)
                        {
                            nativeChildBelow = children.Current[i].NativeHandle as NSView;
                            if (nativeChildBelow != null)
                            {
                                break;
                            }
                        }

                        if (nativeChildBelow != null)
                        {
                            element.AddSubview(nativeChild, NSWindowOrderingMode.Above, nativeChildBelow);
                        }
                        else
                        {
                            element.AddSubview(nativeChild);
                        }
                    }
                });

                return(element);
            });
        }
        public static void Initialize(IScheduler dispatcher)
        {
            Scrolling.Implementation.Factory = (content, darkTheme, supportsOpenGL, zoomAttribs, onBoundsChanged, scrollToRectangle, verticalScrollBarVisible, horizontalScrollBarVisible) =>
                                               Control.Create(self =>
            {
                var documentView = supportsOpenGL
                                                ? new DocumentViewForOpenGL()
                                                : new NSView();

                var view = new NSScrollView
                {
                    ContentView           = new NSFlippedClipView(),
                    DocumentView          = documentView,
                    DrawsBackground       = false,
                    HasVerticalScroller   = verticalScrollBarVisible,
                    HasHorizontalScroller = horizontalScrollBarVisible,
                    AutohidesScrollers    = true
                };

                var scrollBounds =
                    Observable.Create <Unit>(
                        observer =>
                {
                    int recursionDetector = 0;
                    Action updateAction   = () =>
                    {
                        try
                        {
                            // NOTE: At some point while implementing this I got problems with infinite recursion
                            // I managed to avoid that; however to be on the safe side I'm keeping this check here
                            // just in case.
                            //
                            // - karsten
                            recursionDetector++;

                            if (recursionDetector > 10)
                            {
                                Console.WriteLine("ERROR: Aborting recursion in ScrollingImplementation to avoid stack overflow.");
                                return;
                            }

                            observer.OnNext(Unit.Default);
                        }
                        finally
                        {
                            recursionDetector--;
                        }
                    };

                    return(Disposable.Combine(
                               NSView.Notifications.ObserveBoundsChanged(view.ContentView, (s, e) => updateAction()),
                               NSView.Notifications.ObserveFrameChanged(view.ContentView, (s, e) => updateAction()),
                               NSView.Notifications.ObserveBoundsChanged(documentView, (s, e) => updateAction()),
                               NSView.Notifications.ObserveFrameChanged(documentView, (s, e) => updateAction()),
                               NSView.Notifications.ObserveFrameChanged(view, (s, e) => updateAction())
                               ));
                })
                    .StartWith(Unit.Default)
                    .Select(_ => new ScrollBounds(ConvertRectangle(view.DocumentVisibleRect), ConvertRectangle(documentView.Frame)))
                    .DistinctUntilChanged()
                    .Replay(1)
                    .RefCount();

                var documentVisibleSize = scrollBounds.Select(x => x.Visible.Size).Transpose();
                var contentNativeFrame  = ObservableMath.RectangleWithSize(content.DesiredSize.Max(documentVisibleSize));

                content.Mount(new MountLocation.Mutable
                {
                    IsRooted      = self.IsRooted,
                    NativeFrame   = contentNativeFrame,
                    AvailableSize = documentVisibleSize
                });

                self.BindNativeDefaults(view, dispatcher);

                var subview = content.NativeHandle as NSView;


                self.BindNativeProperty(dispatcher, "theme", darkTheme, isDark =>
                                        view.ScrollerKnobStyle = isDark ? NSScrollerKnobStyle.Light : NSScrollerKnobStyle.Dark);

                // HACK:
                // To work around layout feedback problems we set the size of the view to 1 pixel less than the
                // actual available space. See https://github.com/fusetools/Fuse/pull/4703 for details
                self.BindNativeProperty(dispatcher, "containerSize", contentNativeFrame.Size.Transpose(), size =>
                                        documentView.SetFrameSize(new CGSize(Math.Max(0.0, size.Width - 1), Math.Max(0.0, size.Height - 1))));

                if (subview != null)
                {
                    dispatcher.Schedule(() => documentView.AddSubview(subview));
                }

                self.BindNativeProperty(
                    dispatcher,
                    "scrollTarget",
                    scrollToRectangle,
                    r =>
                {
                    var nsrect = new CGRect(r.Position.X, r.Position.Y, r.Size.Width, r.Size.Height);
                    documentView.ScrollRectToVisible(nsrect);
                });

                onBoundsChanged.Do(
                    handler =>
                {
                    scrollBounds
                    .ConnectWhile(self.IsRooted)
                    .Subscribe(handler);
                });

                // Zoom is disabled until we have more time to implement it properly (with zoom controls and shortcuts etc)
                // See https://github.com/fusetools/Fuse/issues/2643
                //zoomAttribs.Do(attribs => EnableMagnification(view, attribs));

                return(view);
            });
        }
        public static void Initialize(Dispatcher dispatcher)
        {
            Button.Implementation.Factory = (command, contentFactory, text, isDefault) =>
            {
                if (contentFactory != null)
                {
                    var states = new BehaviorSubject <ButtonStates>(ButtonStates.Unrooted);

                    var content = contentFactory(states.Switch());

                    return(Control.Create(self =>
                    {
                        Action action = () => { };
                        var button = new CustomButton();
                        button.Click += (s, a) => action();

                        states.OnNext(new ButtonStates(
                                          isPressed: button.ObserveDependencyProperty(instance => instance.IsPressed, ButtonBase.IsPressedProperty),
                                          isHovered: button.ObserveDependencyProperty(instance => instance.IsMouseOver, ButtonBase.IsMouseOverProperty),
                                          isEnabled: button.ObserveDependencyProperty(instance => instance.IsEnabled, ButtonBase.IsEnabledProperty)));

                        content.Mount(
                            new MountLocation.Mutable
                        {
                            NativeFrame = ObservableMath.RectangleWithSize(self.NativeFrame.Size),
                            AvailableSize = self.AvailableSize,
                            IsRooted = self.IsRooted,
                        });

                        var child = content.NativeHandle as UIElement;
                        if (child != null)
                        {
                            var grid = new Grid();
                            grid.Children.Add(
                                new System.Windows.Shapes.Rectangle()
                            {
                                Fill = new SolidColorBrush(Colors.Transparent)
                            });
                            grid.Children.Add(child);
                            button.Content = grid;
                        }
                        ;


                        self.BindNativeProperty(dispatcher, "command", command.Action, cmd =>
                        {
                            button.IsEnabled = cmd.HasValue;
                            action = () => cmd.Do(x => x());
                        });

                        self.BindNativeDefaults(button, dispatcher);

                        return button;
                    })
                           .WithSize(content.DesiredSize));
                }
                else
                {
                    var width = new ReplaySubject <Points>(1);

                    return(Control.Create(self =>
                    {
                        Action action = () => { };
                        var button = new System.Windows.Controls.Button();
                        button.Click += (s, a) => action();


                        command
                        .Action
                        .CombineLatest(text)
                        .Take(1)
                        .ObserveOn(Fusion.Application.MainThread)
                        .Subscribe(
                            cmdText =>
                        {
                            UpdateButtonFields(button, cmdText.Item1.HasValue, cmdText.Item2, width);
                        });

                        self.BindNativeProperty(dispatcher, "command", command.Action.CombineLatest(text), cmdText =>
                        {
                            UpdateButtonFields(button, cmdText.Item1.HasValue, cmdText.Item2, width);
                            action = () => cmdText.Item1.Do(x => x());
                        });

                        self.BindNativeDefaults(button, dispatcher);

                        return button;
                    })
                           .WithHeight(20)
                           .WithWidth(width)
                           .WithPadding(new Thickness <Points>(6)));
                }
            };
        }
Example #10
0
        public static System.Windows.Window Initialize(Window model)
        {
            var dispatcher = Fusion.Application.MainThread;

            var windowSubject = new BehaviorSubject <Optional <WindowWithOverlays> >(Optional.None());

            var menu = new System.Windows.Controls.Menu();

            DockPanel.SetDock(menu, Dock.Top);

            var contentContainer = new Canvas()
            {
                // This is needed for native overlays to work
                ClipToBounds = true,
            };
            var panel = new DockPanel()
            {
                LastChildFill = true,
                Children      =
                {
                    menu,
                    contentContainer
                }
            };

            var ret = new FancyWindow(model.Style == WindowStyle.Regular || model.Style == WindowStyle.Fat)
            {
                Content = panel
            };

            var focused = new Subject <Unit>();

            model.Focused.Execute.Sample(focused).Subscribe(c => c());
            ret.Activated += (sender, args) => focused.OnNext(Unit.Default);

            var availableSize = DataBinding
                                .ObservableFromNativeEvent <SizeChangedEventArgs>(contentContainer, "SizeChanged")
                                .Select(e => e.NewSize.ToFusion())
                                .Replay(1);

            availableSize.Connect();

            var content = model.Content;
            //.SetNativeWindow(windowSubject)
            //.SetDensity(ret.DensityFactor);
            var transize = availableSize.Transpose();

            content.Mount(
                new MountLocation.Mutable
            {
                AvailableSize = transize,
                NativeFrame   = ObservableMath.RectangleWithSize(transize),
                IsRooted      = Observable
                                .FromEventPattern <DependencyPropertyChangedEventArgs>(ret, "IsVisibleChanged")
                                .Select(arg => ret.IsVisible)
                                .Replay(1).RefCount(),
            });

            content.BindNativeProperty(dispatcher, "Background", model.Background, color =>
                                       menu.Background = ret.Background = new SolidColorBrush(color.ToColor()));

            content.BindNativeProperty(dispatcher, "Foreground", model.Foreground, color =>
                                       menu.Foreground = ret.Foreground = new SolidColorBrush(color.ToColor()));

            content.BindNativeProperty(dispatcher, "BorderBrush", model.Border.Brush, color =>
                                       ret.BorderBrush = new SolidColorBrush(color.ToColor()));

            content.BindNativeProperty(dispatcher, "BorderThickness", model.Border.Thickness, thickness =>
                                       ret.BorderThickness = new System.Windows.Thickness(thickness));


            Fusion.Application.MainThread
            .InvokeAsync(() => content.NativeHandle)
            .ToObservable().OfType <UIElement>()
            .Subscribe(nativeContent =>
                       contentContainer.Children.Add(nativeContent));

            var windowState =
                model.State
                .Or(Property.Default <Optional <WindowState> >())
                .Or(WindowState.Normal)
                .AutoInvalidate()
                .PreventFeedback();

            windowState.Subscribe(state => dispatcher.InvokeAsync(() =>
                                                                  ret.WindowState = state == WindowState.Normal ? System.Windows.WindowState.Normal : System.Windows.WindowState.Maximized));

            ret.StateChanged += (sender, args) =>
                                windowState.Write(
                ret.WindowState == System.Windows.WindowState.Maximized
                                                ? WindowState.Maximized
                                                : WindowState.Normal);

            model.Size.Do(
                maybeSize =>
            {
                var size = maybeSize
                           .Or(Size.Create <Points>(Double.NaN, Double.NaN))
                           .AutoInvalidate(TimeSpan.FromSeconds(2))
                           .PreventFeedback();

                bool setByUs = false;

                size.Subscribe(s => dispatcher.Schedule(() =>
                {
                    setByUs       = true;
                    ret.Width     = (float)s.Width;
                    var newHeight = s.Height + (TitleBarHeight + menu.ActualHeight);
                    ret.Height    = (float)newHeight;
                    setByUs       = false;
                }));

                ret.SizeChanged += (s, a) =>
                {
                    if (setByUs)
                    {
                        return;
                    }

                    var contentSize = new Size <Points>(a.NewSize.Width, a.NewSize.Height - (TitleBarHeight + menu.ActualHeight));
                    size.Write(contentSize);
                };
            });

            model.Position.Do(
                maybePosition =>
            {
                var pos = maybePosition
                          .Or(new Point <Points>(double.NaN, double.NaN))
                          .AutoInvalidate(TimeSpan.FromSeconds(2))
                          .PreventFeedback();
                // TODO: ConnectWhile() ?

                bool setByUs = false;

                pos.Subscribe(p => dispatcher.Schedule(() =>
                {
                    setByUs  = true;
                    ret.Left = p.X;
                    ret.Top  = p.Y;
                    setByUs  = false;
                }));

                Fusion.Application.MainThread.Schedule(() =>
                {
                    ret.LocationChanged += (s, args) =>
                    {
                        if (setByUs)
                        {
                            return;
                        }

                        var sender = s as FancyWindow;
                        if (sender == null)
                        {
                            return;
                        }

                        pos.Write(new Point <Points>(sender.Left, sender.Top));
                    };
                });
            });

            model.TopMost.Do(topMost =>
                             topMost.Subscribe(t => dispatcher.Schedule(() =>
            {
                ret.Topmost = t;
                ret.Show();
            })));

            model.Size.Do(s =>
                          s.IsReadOnly.Subscribe(isReadOnly => dispatcher.Schedule(() =>
            {
                if (isReadOnly)
                {
                    ret.ResizeMode            = ResizeMode.NoResize;
                    ret.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                }
            })));


            var closed = new Subject <Unit>();

            ret.Closed += (s, a) => closed.OnNext(Unit.Default);
            model.Closed.Execute.Sample(closed).Subscribe(e => e());

            model.Title.Subscribe(title => dispatcher.Schedule(() =>
            {
                ret.Title = model.HideTitle ? "" : title;
            }));

            model.Menu.Do(m => WindowsMenuBuilder.Populate(m, menu, ret, dispatcher));

            DataBinding
            .ObservableFromNativeEvent <EventArgs>(ret, "Loaded")
            .Subscribe(_ => windowSubject.OnNext(ret));

            DataBinding
            .ObservableFromNativeEvent <EventArgs>(ret, "LayoutUpdated")
            .Select(a => PresentationSource.FromVisual(ret))
            .DistinctUntilChanged()
            .Subscribe(
                presentation =>
            {
                if (presentation == null)
                {
                    return;
                }

                var winHandle = new WindowInteropHelper(ret).Handle;
                WindowPlacement.SetPlacement(winHandle, WindowPlacement.GetPlacement(winHandle));
            });

            ret.Closing += (s, a) =>
            {
                model.Closed.Execute.Take(1).Subscribe(e => e());
                ExitIfLastVisibleWindow(ret);
            };

            ret.IsVisibleChanged += (s, a) =>
            {
                if (ret.IsVisible == false)
                {
                    ExitIfLastVisibleWindow(ret);
                }
            };

            return(ret);
        }
		public static void Initialize(IScheduler mainThreadDispatcher, AbsoluteFilePath userData, dynamic reflection)
		{
			UserSettings.Settings = PersistentSettings.Load(usersettingsfile: userData);

			Axis2DExtensions.ShouldFlip = false;
			Application.MainThread = mainThreadDispatcher;
			_reflection = reflection;
			

			Pointer.Implementation.MakeHittable = (control, space, callbacks) =>
			{
				var o = control.NativeHandle;

				reflection.CallStatic("Outracks.UnoHost.FusionInterop", "OnPointerPressed", o, new Action<Float2>(pos =>
					callbacks.OnPressed(new Pointer.OnPressedArgs(new Point<Points>(pos.X, pos.Y), 1))));

				reflection.CallStatic("Outracks.UnoHost.FusionInterop", "OnPointerMoved", o, new Action<Float2>(pos =>
					callbacks.OnMoved(new Point<Points>(pos.X, pos.Y))));

				return control;
			};

			Layout.Implementation.LayerControls = (childFactory) =>
				Control.Create(self =>
				{
					var control = reflection.Instantiate("Fuse.Controls.Panel");

					BindNativeDefaults(self, control);

					var element = control as dynamic;

					self.BindList(
						list: childFactory(self).Select(Enumerable.Reverse),
						add: child =>
						{
							child.Mount(
								new MountLocation.Mutable
								{
									IsRooted = self.IsRooted,
									AvailableSize = self.AvailableSize,
									NativeFrame = ObservableMath.RectangleWithSize(self.NativeFrame.Size),
								});

							var nativeChild = child.NativeHandle as dynamic;
							if (nativeChild != null)
								element.Children.Add(nativeChild);
						},
						remove: child =>
						{
							var nativeChild = child.NativeHandle as dynamic;
							if (nativeChild != null)
								element.Children.Remove(nativeChild);

							child.Mount(MountLocation.Unmounted);
						});

					return control;
				});

			Shapes.Implementation.RectangleFactory = (stroke, brush, cornerRadius) =>
				Control.Create(self =>
				{
					var control = _reflection.Instantiate("Fuse.Controls.Rectangle");

					BindNativeDefaults(self, control);
					BindShapeProperties(self, control, brush, stroke);

					return control;
				});
		}
        public static void Initialize(Dispatcher dispatcher)
        {
            Scrolling.Implementation.Factory = (content, darkTheme, supportsOpenGL, zooomAttribs, onBoundsChanged, scrollToRectangle, verticalScrollBarVisible, horizontalScrollBarVisible) =>
                                               Control.Create(self =>
            {
                var contentNativeFrame = ObservableMath.RectangleWithSize(content.DesiredSize.Max(self.NativeFrame.Size));

                content.Mount(
                    new MountLocation.Mutable
                {
                    IsRooted      = self.IsRooted,
                    NativeFrame   = contentNativeFrame,
                    AvailableSize = self.NativeFrame.Size,
                });

                var dummyControl = new Canvas();
                var view         = new ScrollViewer()
                {
                    ClipToBounds = true,
                    Content      = new ContentControl()
                    {
                        VerticalAlignment   = VerticalAlignment.Top,
                        HorizontalAlignment = HorizontalAlignment.Left,
                        Content             = dummyControl,
                    },

                    HorizontalScrollBarVisibility = horizontalScrollBarVisible ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden,
                    VerticalScrollBarVisibility   = verticalScrollBarVisible ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden,
                };

                self.BindNativeProperty(dispatcher, "containerWidth", contentNativeFrame.Size.Width, width => dummyControl.Width     = Math.Max(0.0, width));
                self.BindNativeProperty(dispatcher, "containerHeight", contentNativeFrame.Size.Height, height => dummyControl.Height = Math.Max(0.0, height));

                self.BindNativeProperty(dispatcher, "scrollTarget", scrollToRectangle,
                                        r =>
                {
                    dummyControl.BringIntoView(new Rect(r.Left(), r.Top(), r.Width, r.Height));
                });

                onBoundsChanged.Do(handler =>
                {
                    DataBinding.ObservableFromNativeEvent <EventArgs>(view, "ScrollChanged")
                    .CombineLatest(
                        contentNativeFrame.Transpose().DistinctUntilChanged(),
                        (_, contentFrame) => new ScrollBounds(
                            Rectangle.FromPositionSize <Points>(
                                view.HorizontalOffset,
                                view.VerticalOffset,
                                view.ViewportWidth,
                                view.ViewportHeight),
                            contentFrame))
                    .DistinctUntilChanged()
                    .Subscribe(handler);
                });

                self.BindNativeDefaults(view, dispatcher);

                var child = (FrameworkElement)content.NativeHandle;

                dummyControl.Children.Add(child);

                return(view);
            });
        }
Example #13
0
        public static CustomWindow Create(Window model, Optional <ObservableNSDocument> document)
        {
            var dispatcher = Fusion.Application.MainThread;

            model.Title = model.Title.Select(title => model.HideTitle ? "" : title);

            var sizeFeedback = model.Size
                               .Or(Property.Create(Optional.None <Size <Points> >()))
                               .Or(Size.Create <Points>(800, 600))
                               .AutoInvalidate(TimeSpan.FromSeconds(2));

            var size = sizeFeedback.PreventFeedback();

            var content = new NSDefaultView();

            if (model.DragOperation.HasValue)
            {
                content.RegisterForDraggedTypes(new string[] { NSPasteboard.NSFilenamesType });
                content.AddDropOperation(model.DragOperation.Value);
            }

            var window = new CustomWindow(model.Focused)
            {
                BackgroundColor   = Color.FromBytes(0x31, 0x34, 0x3a).ToNSColor(),
                ContentView       = content,
                StyleMask         = NSWindowStyle.TexturedBackground | /*NSWindowStyle.Utility |*/ NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable,
                HidesOnDeactivate = false,
                Restorable        = false,
            };

            window.IsOpaque    = false;
            content.WantsLayer = true;

            var fusionContent         = model.Content;
            var desiredTitleBarHeight = new ReplaySubject <IObservable <Points> >(1);

            switch (model.Style)
            {
            case WindowStyle.Regular:
                // Render window content beneath title bar area
                window.StyleMask |= NSWindowStyle.FullSizeContentView;

                // Make title bar transparent
                window.TitlebarAppearsTransparent = true;

                // Build custom title bar content and dock it on top of existing fusionContent
                var titleTextColor = Color.White;

                var titleBarContent = document.MatchWith(_ =>
                {
                    // If we have a document, we'll create a container to hijack the window's DocumentIconButton later
                    var titleText = Label.Create(text: window.DocumentTitle.AsText(), color: titleTextColor);
                    var documentIconButtonContainer = DocumentIconButtonContainer.Create(window).WithPadding(right: Optional.Some <Points>(4));
                    return(Layout.StackFromLeft(documentIconButtonContainer, titleText));
                },
                                                         () => Label.Create(text: model.Title.AsText(), color: titleTextColor));

                Action zoom = () =>
                              Fusion.Application.MainThread.Schedule(() => window.Zoom(window));

                titleBarContent = titleBarContent
                                  .WithPadding(top: Optional.Some <Points>(2), bottom: Optional.Some <Points>(3))
                                  .CenterHorizontally();

                desiredTitleBarHeight.OnNext(titleBarContent.DesiredSize.Height);

                fusionContent = Layout.DockTop(
                    titleBarContent,
                    fusionContent
                    ).OnMouse(doubleClicked: Command.Enabled(zoom));
                break;

            case WindowStyle.Fat:
                // Render window content beneath title bar area
                window.StyleMask |= NSWindowStyle.FullSizeContentView;

                //Create a toolbar
                window.Toolbar = new NSToolbar("toolbar");

                // Make title bar transparent
                window.TitlebarAppearsTransparent = true;

                window.Toolbar.ShowsBaselineSeparator = false;

                window.WillUseFullScreenPresentationOptions = (nsWindow, options) => options | NSApplicationPresentationOptions.AutoHideToolbar;

                // Build custom title bar content and dock it on top of existing fusionContent
                var titleTextColorFat = model.Foreground;

                var titleBarContentFat = document.MatchWith(_ =>
                {
                    // If we have a document, we'll create a container to hijack the window's DocumentIconButton later
                    var titleText = Label.Create(
                        text: window.DocumentTitle.AsText(),
                        color: titleTextColorFat,
                        font: Font.SystemDefault(11),
                        lineBreakMode: LineBreakMode.TruncateTail)
                                    .WithWidth(140)
                                    .Center();                            // Ensures the doc name can never run over the controls in compact mode

                    return(titleText);
                },
                                                            () => Label.Create(text: model.Title.AsText(), color: titleTextColorFat));

                // For some reason the toolbar sometimes causes double zoom events, this is a workaround
                bool   zoomExpected = false;
                Action zoomFat      = () =>
                                      Fusion.Application.MainThread.Schedule(() =>
                {
                    try
                    {
                        zoomExpected = true;
                        window.Zoom(window);
                    }
                    finally
                    {
                        zoomExpected = false;
                    }
                });

                window.ShouldZoom = (_, __) => zoomExpected;


                titleBarContentFat = Layout.StackFromLeft(
                    Control.Empty
                    .WithWidth(80)
                    .HideOnWindows(),
                    Control.Empty
                    .WithWidth(16)
                    .HideOnMac(),
                    titleBarContentFat)
                                     .WithPadding(top: Optional.Some <Points>(12))
                                     .DockTopLeft();

                desiredTitleBarHeight.OnNext(Observable.Return <Points>(0.0));

                fusionContent = fusionContent.OnMouse(doubleClicked: Command.Enabled(zoomFat)).WithOverlay(titleBarContentFat);
                break;

            case WindowStyle.None:
                window.StyleMask = NSWindowStyle.TexturedBackground | NSWindowStyle.Borderless;
                window.MovableByWindowBackground = true;
                window.BackgroundColor           = NSColor.Clear;

                desiredTitleBarHeight.OnNext(Observable.Return <Points>(0.0));

                content.Layer.Frame         = content.Frame;
                content.Layer.CornerRadius  = 5.0f;
                content.Layer.MasksToBounds = true;
                break;

            case WindowStyle.Sheet:
                desiredTitleBarHeight.OnNext(Observable.Return <Points>(0.0));
                break;

            default:
                throw new NotImplementedException();
            }

            model.Size.Do(s =>
                          s.IsReadOnly.ObserveOn(dispatcher).Subscribe(isReadOnly =>
            {
                if (isReadOnly)
                {
                    window.StyleMask &= ~NSWindowStyle.Resizable;
                }
                else
                {
                    window.StyleMask |= NSWindowStyle.Resizable;
                }
            }));

            var sizeFeedbackObservable = sizeFeedback.AsObservable();
            var sizeObservable         = size.AsObservable();

            model.Title.ObserveOn(dispatcher).Subscribe(title => window.Title = title);
            sizeObservable.CombineLatest(
                desiredTitleBarHeight.Switch(),
                (s, h) => new Size <Points>(s.Width, s.Height + h)).ObserveOn(dispatcher).Subscribe(s => window.SetContentSize(s.ToSize()));

            window.WillClose += (sender, args) =>
            {
                model.Closed.ExecuteOnce();
            };             // Window closed by user

            var observer = new WindowObserver();

            observer.DangerousRetain();
            window.AddObserver(observer, new NSString("visible"), NSKeyValueObservingOptions.New, IntPtr.Zero);

            window.DidResize += (s, a) => desiredTitleBarHeight.Switch()
                                .Subscribe(titleBarHeight =>
                                           size.Write((content.Frame.Size - new CGSize(0, (float)titleBarHeight)).ToFusion()));

            var transize = sizeFeedbackObservable.CombineLatest(
                desiredTitleBarHeight.Switch(),
                (s, h) => new Size <Points>(s.Width, s.Height + h)).Transpose();

            fusionContent.Mount(new MountLocation.Mutable
            {
                AvailableSize = transize,
                NativeFrame   = ObservableMath.RectangleWithSize(transize),
                IsRooted      = window.IsShowing
            });

            Fusion.Application.MainThread.Schedule(() =>
            {
                var nativeContent = fusionContent.NativeHandle as NSView;
                if (nativeContent != null)
                {
                    content.AddSubview(nativeContent);
                }
            });

            var systemId = SystemGuidLoader.LoadOrCreateOrEmpty();

            model.Menu.Do(menu =>
                          MenuBuilder
                          .CreateMenu(menu, ReportFactory.GetReporter(systemId, Guid.NewGuid(), "Menu"))
                          .ToObservable()
                          .Subscribe(m =>
                                     window.Menu = m));

            window.Center();
            var centerposition = new Point <Points>((double)window.Frame.X, (double)window.Frame.Y);
            var position       = model.Position
                                 .Or(Property.Create(Optional.None <Point <Points> >()))
                                 .Or(centerposition)
                                 .AutoInvalidate(TimeSpan.FromSeconds(2))
                                 .PreventFeedback();

            model.TopMost.Do(topMost =>
                             topMost.Subscribe(t =>
            {
                window.Level = t ? NSWindowLevel.Floating : NSWindowLevel.Normal;
            }));

            position.ObserveOn(dispatcher).Subscribe(p =>
            {
                window.SetFrameOrigin(new CGPoint(p.X, p.Y));
            });
            window.DidMove += (s, a) =>
            {
                position.Write(new Point <Points>((double)window.Frame.Left, (double)window.Frame.Top));
            };

            window.DidBecomeMain += (s, a) => {
                if (window.Menu != null)
                {
                    NSApplication.SharedApplication.MainMenu = window.Menu;
                }
                else
                {
                    NSApplication.SharedApplication.MainMenu = new NSMenu();
                }
            };

            return(window);
        }
Example #14
0
        static IControl MakeHittable(IControl control, Space space, Pointer.Callbacks callbacks)
        {
            return(Control.Create(location =>
            {
                var view = new Canvas()
                {
                    Background = Brushes.Transparent,
                };

                view.MouseDown += (s, a) =>
                {
                    view.CaptureMouse();
                    callbacks.OnPressed(new Pointer.OnPressedArgs(ToPoint(a, view, space), a.ClickCount));
                    a.Handled = true;
                };

                view.MouseUp += (s, a) =>
                {
                    callbacks.OnReleased();
                    a.Handled = true;
                    view.ReleaseMouseCapture();

                    if (a.ChangedButton == MouseButton.Right)
                    {
                        if (view.ContextMenu == null)
                        {
                            return;
                        }
                        view.ContextMenu.IsOpen = true;
                    }
                };

                view.MouseMove += (s, a) =>
                {
                    callbacks.OnMoved(ToPoint(a, view, space));
                    a.Handled = true;

                    if (view.IsMouseCaptured && a.LeftButton == MouseButtonState.Pressed)
                    {
                        callbacks.OnDragged().Do(data =>
                                                 DragDrop.DoDragDrop(view, new DraggingImplementation.DragData(data), DragDropEffects.All));
                    }
                };

                view.MouseEnter += (s, a) =>
                {
                    callbacks.OnEntered(ToPoint(a, view, space));
                    a.Handled = true;
                };

                view.MouseLeave += (s, a) =>
                {
                    callbacks.OnExited(ToPoint(a, view, space));
                    a.Handled = true;
                };

                view.GotMouseCapture += (s, a) =>
                {
                    callbacks.OnGotFocus();
                    a.Handled = true;
                };

                view.LostMouseCapture += (s, a) =>
                {
                    callbacks.OnLostFocus();
                    a.Handled = true;
                };

                location.BindNativeDefaults(view, _dispatcher);

                control.Mount(
                    new MountLocation.Mutable
                {
                    AvailableSize = location.AvailableSize,
                    IsRooted = location.IsRooted,
                    NativeFrame = ObservableMath.RectangleWithSize(location.NativeFrame.Size),
                });

                var contentHandle = control.NativeHandle as FrameworkElement;
                if (contentHandle != null)
                {
                    _dispatcher.Enqueue(() => view.Children.Add(contentHandle));
                }

                return view;
            }).WithSize(control.DesiredSize));
        }
        public static void Initialize(Dispatcher dispatcher)
        {
            Layout.Implementation.LayerControls = (childFactory) =>
                                                  Control.Create(
                ctrl =>
            {
                var element = new Canvas();

                ctrl.BindNativeDefaults(element, dispatcher);

                childFactory(ctrl)
                // IMPORTANT: ConnectWhile has to be done first since else we'll lose all changes done to the children list while the ctrl is unrooted.
                // which breaks diffing, and we get dangling views (views that aren't removed).
                .ConnectWhile(ctrl.IsRooted)
                .DiffSequence()
                .ObserveOn(Fusion.Application.MainThread)
                .Subscribe(children =>
                {
                    foreach (var child in children.Removed)
                    {
                        var nativeChild = child.NativeHandle as FrameworkElement;
                        if (nativeChild != null)
                        {
                            element.Children.Remove(nativeChild);
                        }

                        child.Mount(MountLocation.Unmounted);
                    }

                    foreach (var child in children.Added)
                    {
                        child.Mount(new MountLocation.Mutable
                        {
                            IsRooted      = ctrl.IsRooted,
                            AvailableSize = ctrl.AvailableSize,
                            NativeFrame   = ObservableMath.RectangleWithSize(ctrl.NativeFrame.Size),
                        });

                        var nativeChild = child.NativeHandle as FrameworkElement;
                        if (nativeChild == null)
                        {
                            continue;
                        }

                        var parent = nativeChild.Parent as Canvas;
                        if (parent != null)
                        {
                            parent.Children.Remove(nativeChild);
                        }

                        element.Children.Add(nativeChild);
                    }
                });

                return(element);
            });

            Clipping.Initialize(
                (content, clipToBounds) =>
            {
                var container = Layout.Layer(content);

                Fusion.Application.MainThread.Schedule(() =>
                {
                    var elm          = (FrameworkElement)container.NativeHandle;
                    elm.ClipToBounds = clipToBounds;
                });

                return(container.WithSize(content.DesiredSize));
            });
        }