public static IObservable <Optional <T> > GetWindow <T>(this FrameworkElement dummyElement) where T : System.Windows.Window { // TODO: this might be expensive return(DataBinding.ObservableFromNativeEvent <object>(dummyElement, "LayoutUpdated") .StartWith(new object()) .Select(_ => Fusion.Application.MainThread.InvokeAsync(() => { var hwndSource = PresentationSource.FromVisual(dummyElement); if (hwndSource == null) { return Optional.None(); } var window = hwndSource.RootVisual as T; if (window == null) { return Optional.None(); } return Optional.Some(window); })) .Switch() .DistinctUntilChanged()); }
public static IObservable <Tuple <Rectangle <Points>, Rectangle <Points> > > ScreenRect(this FrameworkElement self, Dispatcher dispatcher) { return(DataBinding .ObservableFromNativeEvent <EventArgs>(self, "LayoutUpdated") .Select(_ => Unit.Default) .StartWith(Unit.Default) .SubscribeOn(self.Dispatcher) .Select(_ => dispatcher.InvokeAsync(() => self.GetScreenRect2())) .Switch()); //.ObserveOn(self.Dispatcher); }
// Main thread public IDisposable BindTo(IntPtr unoHostHwnd, IntPtr controlHwnd, IControl control, IObservable <Ratio <Pixels, Points> > density) { var element = control.NativeHandle as FrameworkElement; if (element == null) { throw new InvalidOperationException("Can't bind to an empty control"); } var frames = DataBinding .ObservableFromNativeEvent <object>(element, "LayoutUpdated") .StartWith(new object()) .Select(_ => { var r = element.GetScreenRect(); return(new ClipResult { BoundsInIntersection = r.Item1.RelativeTo(r.Item2.Position), IntersectionInParent = r.Item2 }); }); WinApi.SetWindowParent(unoHostHwnd, Handle); WinApi.SetWindowParent(Handle, controlHwnd); WinApi.ShowWindow(unoHostHwnd, WinApi.ShowWindowEnum.ShowNoActivate); var flags = WinApi.SWP_NOZORDER | WinApi.SWP_NOACTIVATE; return(Disposable.Combine ( Disposable.Create(() => Invoke(new Action(Hide))), frames.BufferPrevious().WithLatestFromBuffered(density, Tuple.Create).Subscribe( (f, d) => Fusion.Application.MainThread.Schedule(() => { WinApi.SetWindowPos( Handle, f.Current.IntersectionInParent.Position * d, // Bug in Window chrome, where position must be set once more, even though it has been set. f.ChangesTo(fr => fr.IntersectionInParent.Size * d), f.Current.IntersectionInParent.Size.HasZeroArea(), flags); WinApi.SetWindowPos( unoHostHwnd, f.ChangesTo(fr => fr.BoundsInIntersection.Position * d), f.ChangesTo(fr => fr.BoundsInIntersection.Size * d), f.Current.BoundsInIntersection.Size.HasZeroArea(), flags | WinApi.SWP_ASYNCWINDOWPOS); })) )); }
public WindowsTrayApplication(IObservable <string> title, Menu menu, IObservable <Icon> icon) { System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); var container = new Container(); _notifyIcon = new NotifyIcon(container) { ContextMenu = new System.Windows.Forms.ContextMenu() }; _scheduler = System.Windows.Threading.Dispatcher.CurrentDispatcher; _notifier = new BalloonNotifier(_notifyIcon, _scheduler); UserClicked = Observable.Merge( DataBinding.ObservableFromNativeEvent <MouseEventArgs>(_notifyIcon, "MouseDoubleClick"), DataBinding.ObservableFromNativeEvent <MouseEventArgs>(_notifyIcon, "MouseClick")) .Where(a => a.Button != MouseButtons.Right) .Select(a => a.Clicks); _notifyIcon.Visible = true; if (menu != default(Menu)) { _notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu(); WinFormMenuBuilder.Populate(menu, _notifyIcon.ContextMenu); } if (icon != null) { icon.ObserveOn(_scheduler).Subscribe(i => { using (var stream = i.GetStream()) _notifyIcon.Icon = new System.Drawing.Icon(stream); }); } if (title != null) { title.ObserveOn(_scheduler).Subscribe(t => _notifyIcon.Text = t); } }
public BalloonNotifier(NotifyIcon icon, System.Windows.Threading.Dispatcher scheduler) { _icon = icon; var clicked = DataBinding.ObservableFromNativeEvent <EventArgs>(_icon, "BalloonTipClicked"); var closed = DataBinding.ObservableFromNativeEvent <EventArgs>(_icon, "BalloonTipClosed"); var current = _notifications.Select(n => n.LastOrNone()).NotNone(); _subscriptions = new[] { current .ObserveOn(scheduler) .Subscribe(n => { var timeout = n.Notification.Timeout.Or(TimeSpan.FromHours(3)); _icon.ShowBalloonTip( (int)timeout.TotalMilliseconds, n.Notification.Title, n.Notification.Description, n.Notification.Type == NotifyType.Error ? ToolTipIcon.Error : n.Notification.Type == NotifyType.Info ? ToolTipIcon.Info : ToolTipIcon.None); }), clicked.WithLatestFromBuffered(current, (_, c) => c) .ObserveOn(TaskPoolScheduler.Default) .Subscribe(c => { _notifications.OnNext(_notifications.Value.Remove(c)); c.TaskCompletionSource.TrySetResult(NotificationFeedback.PrimaryActionTaken); }), closed.WithLatestFromBuffered(current, (_, c) => c) .ObserveOn(TaskPoolScheduler.Default) .Subscribe(c => { _notifications.OnNext(_notifications.Value.Remove(c)); c.TaskCompletionSource.TrySetResult(NotificationFeedback.Dismissed); }), }; }
public static void Initialize(IScheduler scheduler) { WebView.Implementation.UrlFactory += (uri, onNavigating) => Control.Create(control => { var webBrowser = new WebBrowser(); webBrowser.Navigating += (sender, args) => { var navigatingArgs = new Navigating(args.Uri); onNavigating(navigatingArgs); args.Cancel = navigatingArgs.Cancel; }; DataBinding.ObservableFromNativeEvent <EventArgs>(webBrowser, "LoadCompleted") .CombineLatest(webBrowser.GetDpi(), Tuple.Create) .Subscribe((_, density) => webBrowser.SetZoom(density)); scheduler.Schedule(() => webBrowser.Navigate(uri)); control.BindNativeDefaults(webBrowser, scheduler); return(webBrowser); }); WebView.Implementation.StringFactory += (content) => Control.Create(control => { var webBrowser = new WebBrowser(); content.Subscribe(c => { if (!string.IsNullOrEmpty(c)) { scheduler.Schedule(() => webBrowser.NavigateToString(c)); } }); control.BindNativeDefaults(webBrowser, scheduler); return(webBrowser); }); }
public static void Initialize(Dispatcher dispatcher) { Overlay.Initialize((background, foreground) => Control.Create(self => { var canvas = (FrameworkElement)foreground.NativeHandle; Fusion.Application.MainThread.Schedule(() => { if (canvas.Parent != null) { ((Canvas)canvas.Parent).Children.Remove(canvas); } }); var foregroundWindow = new OverlayWindow { Content = new Canvas { Children = { canvas } }, }; var backgroundElement = background.NativeHandle as FrameworkElement; if (backgroundElement != null) { background.Mount(self); } else { var dummy = Shapes.Rectangle(); dummy.Mount(self); backgroundElement = (FrameworkElement)dummy.NativeHandle; } Fusion.Application.MainThread.Schedule(() => { foregroundWindow.KeyDown += (sender, args) => { var visual = PresentationSource.FromVisual(backgroundElement); if (visual == null) { return; } if (args.Key == System.Windows.Input.Key.DeadCharProcessed) { return; } backgroundElement.RaiseEvent(new KeyEventArgs(System.Windows.Input.Keyboard.PrimaryDevice, visual, 0, args.Key) { RoutedEvent = System.Windows.Input.Keyboard.PreviewKeyDownEvent, Source = backgroundElement, }); }; foregroundWindow.KeyUp += (sender, args) => { var visual = PresentationSource.FromVisual(backgroundElement); if (visual == null) { return; } if (args.Key == System.Windows.Input.Key.DeadCharProcessed) { return; } backgroundElement.RaiseEvent(new KeyEventArgs(System.Windows.Input.Keyboard.PrimaryDevice, visual, 0, args.Key) { RoutedEvent = System.Windows.Input.Keyboard.PreviewKeyUpEvent, Source = backgroundElement, }); }; }); var windowFrames = backgroundElement.ScreenRect(dispatcher); var intersection = windowFrames.Select(f => f.Item2); var frame = windowFrames.Select(f => f.Item1); var bounds = frame.RelativeTo(intersection.Position()).Replay(1).RefCount(); foreground.Mount( new MountLocation.Mutable { IsRooted = self.IsRooted, AvailableSize = self.AvailableSize, NativeFrame = bounds.Transpose(), }); var parentWindow = self.IsRooted.Switch(isRooted => isRooted == false ? Observable.Return(Optional.None <WindowWithOverlays>()) : backgroundElement.GetWindow <WindowWithOverlays>()); parentWindow.SubscribeUsing(tmp => tmp.MatchWith( none: () => Disposable.Empty, some: backgroundWindow => { var windowLocation = DataBinding.ObservableFromNativeEvent <object>(backgroundWindow, "LocationChanged") .StartWith(new object()) .Select(_ => dispatcher.InvokeAsync(() => new Point <Points>(backgroundWindow.Left, backgroundWindow.Top))) .Switch(); dispatcher.Schedule(() => { foregroundWindow.ShowActivated = false; foregroundWindow.Show(); foregroundWindow.Owner = backgroundWindow; }); return(Disposable.Combine( backgroundWindow.AddOverlay("name", foregroundWindow), Disposable.Create(() => dispatcher.Schedule(foregroundWindow.Hide)), intersection.MoveTo(windowLocation) /*.Sample(Fusion.Application.PerFrame)*/ .Subscribe(s => dispatcher.Schedule(() => { if (!double.IsInfinity(s.Left())) { foregroundWindow.Left = s.Left(); } if (!double.IsInfinity(s.Top())) { foregroundWindow.Top = s.Top(); } if (!double.IsInfinity(s.Width)) { foregroundWindow.Width = s.Width.Max(0); } if (!double.IsInfinity(s.Height)) { foregroundWindow.Height = s.Height.Max(0); } })))); })); return(backgroundElement); })); }
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 IUnoHostControl Create( AbsoluteFilePath assemblyPath, Command onFocused, Menu contextMenu, AbsoluteFilePath userDataPath, Action <IUnoHostControl> initialize, Action <OpenGlVersion> gotVersion, params string[] arguments) { UnoHostProcess.Application = ExternalApplication.FromNativeExe(typeof(UnoHostControlFactory).Assembly.GetCodeBaseFilePath()); var dispatcher = Fusion.Application.MainThread; var messagesToHost = new Subject <IBinaryMessage>(); var unoHost = UnoHostProcess.Spawn(assemblyPath, messagesToHost, userDataPath, /*TODO*/ new List <string>()); unoHost.Process.Subscribe(process => new Job().AddProcess(process.Id)); var windowCreated = unoHost.Receieve(WindowCreatedMessage.TryParse).Replay(1); windowCreated.Connect(); var control = new UnoHostControlImplementation() { _disposable = unoHost, Messages = unoHost.Messages, MessagesTo = messagesToHost, Process = unoHost.Process, }; control.Control = Fusion.Control.Create(location => { var dummyControl = Shapes.Rectangle(); dummyControl.Mount(location); var dummyElement = (FrameworkElement)dummyControl.NativeHandle; location.IsRooted .ObserveOn(dispatcher) .SubscribeUsing(rooted => rooted ? ContextMenuImplementation.AddMenuTemporarily(dummyElement, contextMenu, dispatcher) : Disposable.Empty); var mainWindow = DataBinding .ObservableFromNativeEvent <object>(dummyElement, "LayoutUpdated") .StartWith(new object()) .Select(_ => { var hwndSource1 = PresentationSource.FromVisual(dummyElement); if (hwndSource1 == null) { return(Optional.None <System.Windows.Window>()); } var window = hwndSource1.RootVisual as System.Windows.Window; if (window == null) { return(Optional.None <System.Windows.Window>()); } return(Optional.Some(window)); }) .DistinctUntilChanged() .NotNone() .Replay(1).RefCount(); var mainWindowHandle = mainWindow.Select(currentWindow => { var hwndSource = (HwndSource)PresentationSource.FromVisual(currentWindow); if (hwndSource == null) { return(Optional.None()); } return(Optional.Some(hwndSource.Handle)); }) .NotNone() .Replay(1); mainWindowHandle.Connect(); var focused = unoHost.Receieve(WindowFocusMessage.TryParse) .Where(focusState => focusState == FocusState.Focused); focused.CombineLatest(mainWindowHandle, (a, b) => b) .ObserveOn(dispatcher) .Subscribe(t => WinApi.ShowWindow(t, WinApi.ShowWindowEnum.ShowNoActivate)); unoHost.Receieve(WindowContextMenuMessage.TryParse) .ObserveOn(dispatcher) .Subscribe(t => dummyElement.ContextMenu.IsOpen = t); unoHost.Receieve(WindowMouseScrollMessage.TryParse) .ObserveOn(dispatcher) .Subscribe(deltaWheel => { var delta = deltaWheel; var routedEvent = Mouse.MouseWheelEvent; dummyElement.RaiseEvent( new MouseWheelEventArgs( Mouse.PrimaryDevice, Environment.TickCount, delta) { RoutedEvent = routedEvent, Source = dummyElement }); }); // This is a fix for a commit that "fixed" handling the shortcuts, but broke just // plain typing in a TextInput. // See https://github.com/fusetools/Fuse/issues/3342 // and https://github.com/fusetools/Fuse/issues/3887 // Workaround to fix a problem with handling shortcut keys in the application while // the viewport has focus and the user is typing in an TextInput in the app. // We have give the main window focus to handle the shortcut keys, but then // the viewport will lose focus. Current work around is to only do this when // the user presses down Ctrl. unoHost.Receieve(WindowKeyDown.TryParse) .WithLatestFromBuffered(mainWindow, (t, w) => new { Keys = t, Window = w }) .ObserveOn(dispatcher) .Subscribe(t => { var modifiers = System.Windows.Input.Keyboard.PrimaryDevice.Modifiers; var alt = modifiers.HasFlag(System.Windows.Input.ModifierKeys.Alt); // Activate the main window if the Ctrl-key was pressed. // Ctrl + Alt means AltGr which we want to keep in the Uno host e.g. for the '@' key on Nordic keyboards. if (t.Keys == Keys.ControlKey && !alt) { t.Window.Activate(); } }); focused .WithLatestFromBuffered(onFocused.Execute.ConnectWhile(dummyControl.IsRooted), (_, c) => c) .Subscribe(c => c()); var overlayForm = new OverlayForm(); Observable .CombineLatest(windowCreated, mainWindowHandle, (viewportHwnd, mainHwnd) => new { ViewportHwnd = viewportHwnd, MainHwnd = mainHwnd, }) .ObserveOn(dispatcher) .SubscribeUsing(t => overlayForm.BindTo(t.ViewportHwnd, t.MainHwnd, dummyControl, dummyElement.GetDpi())); unoHost.Messages .SelectMany(m => m.TryParse(Ready.MessageType, Ready.ReadDataFrom)) .Take(1) .Subscribe(_ => initialize(control)); unoHost.Messages .SelectSome(OpenGlVersionMessage.TryParse) .Subscribe(gotVersion); unoHost.Messages.Connect(); return(dummyElement); }); 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); }); }
static void BindText( this System.Windows.Controls.TextBox control, IProperty <string> value, IObservable <bool> isRooted, bool isMultiline) { bool valueSetByUser = true; bool hasUnsavedChanges = false; value = value .ConnectWhile(isRooted) .DistinctUntilChangedOrSet(); DataBinding .ObservableFromNativeEvent <EventArgs>(control, "TextChanged") .Subscribe(_ => { if (!valueSetByUser) { return; } hasUnsavedChanges = true; value.Write(control.Text, save: false); }); value.Subscribe(v => Fusion.Application.MainThread.Schedule(() => { valueSetByUser = false; try { control.Text = v; } finally { valueSetByUser = true; } })); control.LostKeyboardFocus += (s, a) => { if (!hasUnsavedChanges) { return; } value.Write(control.Text, save: true); hasUnsavedChanges = false; }; if (isMultiline == false) { control.KeyDown += (s, a) => { if (a.Key != System.Windows.Input.Key.Return) { return; } if (!hasUnsavedChanges) { return; } value.Write(control.Text, save: true); hasUnsavedChanges = false; }; } }