Esempio n. 1
0
            public IControl CreatePopover(
                RectangleEdge prefferedContentEdge,
                Func <PopoverState, IControl> content,
                Func <PopoverState, IControl> popover)
            {
                var state = new PopoverState
                {
                    IsVisible = new BehaviorSubject <bool>(false),
                };

                var contentFrameState = new ReplaySubject <Rectangle <Points> >(1);
                var contentFrame      = contentFrameState.Transpose();

                var center = contentFrame.Center();

                var arrow = Shapes.Rectangle(fill: Theme.PanelBackground)
                            .WithSize(new Size <Points>(13, 13))
                            .Rotate(Math.PI / 4)
                ;

                var popoverContent = Control.Lazy(() => popover(state));

                var desiredContentEdge = contentFrame.GetEdge(prefferedContentEdge)
                                         .Add(popoverContent.DesiredSize.Height);
                var desiredHostEdge = HostFrame.Switch().GetEdge(prefferedContentEdge);

                var adjustedEdge = desiredContentEdge
                                   .CombineLatest(desiredHostEdge, (a, b) => a >= b)
                                   // Put popover on the opposite edge if there are not enough room for it on the preffered edge.
                                   .Select(opposite => opposite ? prefferedContentEdge.Opposite() : prefferedContentEdge);

                var arrowStartOffsetY = adjustedEdge
                                        .Select(e =>
                                                e.IsMinimal()
                                                ? contentFrame.GetEdge(e).Sub(arrow.DesiredSize.Height)
                                                : contentFrame.GetEdge(e).Sub(4))
                                        .Switch();

                var contentStartOffsetY = adjustedEdge
                                          .Select(e =>
                                                  e.IsMinimal()
                                                ? contentFrame.GetEdge(e).Sub(popoverContent.DesiredSize.Height).Sub(arrow.DesiredSize.Height.Div(2))
                                                : contentFrame.GetEdge(e).Add(arrow.DesiredSize.Height.Div(2)).Sub(4))
                                          .Switch();

                var close = Command.Enabled(() => state.IsVisible.OnNext(false));

                var popoverControl = Control.Lazy(() =>
                                                  Layout.Layer(self =>
                {
                    var widthInterval = Observable.CombineLatest(
                        self.NativeFrame.Width,
                        popoverContent.DesiredSize.Width,
                        center.X,
                        (availableWidth, desiredWidth, desiredCenter) =>
                    {
                        desiredWidth += 8;                                        // dropshadow compensation
                        var halfWidth = desiredWidth / 2;

                        return(Interval.FromOffsetLength(
                                   offset:
                                   desiredCenter - halfWidth <0
                                                                                        ? 0
                                                                                        : desiredCenter + halfWidth> availableWidth
                                                                                                ? availableWidth - desiredWidth
                                                                                                : desiredCenter - halfWidth,
                                   length:
                                   availableWidth.Min(desiredWidth)));
                    });

                    return(Observable.Return(
                               new[]
                    {
                        Shapes.Rectangle(fill: Color.AlmostTransparent).OnMouse(pressed: close),
                        arrow
                        .WithFixedPosition(
                            Rectangle.FromIntervals(
                                Interval.FromOffsetLength(center.X.Sub(arrow.DesiredSize.Width.Div(2)).Sub(4), arrow.DesiredSize.Width),
                                Interval.FromOffsetLength(arrowStartOffsetY, arrow.DesiredSize.Height))),
                        popoverContent
                        .WithBackground(
                            Shapes.Rectangle(fill: Theme.PanelBackground, cornerRadius: Observable.Return(new CornerRadius(2))))
                        .MakeHittable().Control
                        .WithFixedPosition(
                            Rectangle.FromIntervals(
                                widthInterval.Transpose(),
                                Interval.FromOffsetLength(contentStartOffsetY, popoverContent.DesiredSize.Height)))
                    }));
                })
                                                  .DropShadow(radius: Observable.Return(new Points(4)), distance: Observable.Return(new Points(0.5))));

                state.IsVisible.SubscribeUsing(isVisible =>
                                               isVisible ? Popovers.AddTemporarily(popoverControl) : Disposable.Empty);

                return(LayoutTracker.TrackVisualBounds(contentFrameState.OnNext, content(state)));
            }