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)); }
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)); }
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)); }; }
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) }); }
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))); } }; }
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); }); }
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); }
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)); }); }