顺滑的组件放置与调整
This commit is contained in:
lincube
2026-03-22 15:21:29 +08:00
parent def21c79b1
commit 685323e057
8 changed files with 585 additions and 7 deletions

View File

@@ -0,0 +1,60 @@
using Avalonia;
using LanMountainDesktop.DesktopEditing;
using Xunit;
namespace LanMountainDesktop.Tests;
public sealed class ComponentLibraryCollapseStateTests
{
[Fact]
public void CreateExpanded_InitializesExpandedStateAndHidesChip()
{
var margin = new Thickness(24, 24, 24, 100);
var state = ComponentLibraryCollapseState.CreateExpanded(margin, 0.75);
Assert.Equal(ComponentLibraryCollapseVisualState.Expanded, state.VisualState);
Assert.Equal(margin, state.ExpandedMargin);
Assert.Equal(0.75, state.ExpandedOpacity, 3);
Assert.False(state.IsChipVisible);
}
[Fact]
public void WithVisualState_PreservesStableExpandedSnapshotAcrossTransitions()
{
var margin = new Thickness(20, 18, 20, 96);
var expanded = ComponentLibraryCollapseState.CreateExpanded(margin, 1);
var collapsing = expanded.WithVisualState(ComponentLibraryCollapseVisualState.Collapsing, isChipVisible: true);
var collapsed = collapsing.WithVisualState(ComponentLibraryCollapseVisualState.Collapsed, isChipVisible: true);
var restoring = collapsed.WithVisualState(ComponentLibraryCollapseVisualState.Restoring, isChipVisible: false);
Assert.Equal(ComponentLibraryCollapseVisualState.Collapsing, collapsing.VisualState);
Assert.Equal(ComponentLibraryCollapseVisualState.Collapsed, collapsed.VisualState);
Assert.Equal(ComponentLibraryCollapseVisualState.Restoring, restoring.VisualState);
Assert.Equal(margin, collapsing.ExpandedMargin);
Assert.Equal(margin, collapsed.ExpandedMargin);
Assert.Equal(margin, restoring.ExpandedMargin);
Assert.Equal(1, collapsing.ExpandedOpacity, 3);
Assert.Equal(1, collapsed.ExpandedOpacity, 3);
Assert.Equal(1, restoring.ExpandedOpacity, 3);
Assert.True(collapsing.IsChipVisible);
Assert.True(collapsed.IsChipVisible);
Assert.False(restoring.IsChipVisible);
}
[Fact]
public void CreateExpanded_ProducesRestorableSnapshotEvenWhenOriginalOpacityIsLow()
{
var margin = new Thickness(18, 22, 18, 88);
var expanded = ComponentLibraryCollapseState.CreateExpanded(margin, 0.15);
var restored = expanded.WithVisualState(ComponentLibraryCollapseVisualState.Expanded, isChipVisible: false);
Assert.Equal(margin, restored.ExpandedMargin);
Assert.Equal(0.15, restored.ExpandedOpacity, 3);
Assert.Equal(ComponentLibraryCollapseVisualState.Expanded, restored.VisualState);
Assert.False(restored.IsChipVisible);
}
}

View File

@@ -134,5 +134,40 @@ public sealed class DesktopPlacementMathTests
Assert.False(DesktopPlacementMath.CanCommitPlacement(placementRect, occludingLibraryBounds));
Assert.True(DesktopPlacementMath.CanCommitPlacement(placementRect, distantLibraryBounds));
Assert.True(DesktopPlacementMath.CanCommitPlacement(placementRect, componentLibraryBounds: null));
}
[Fact]
public void Session_AllowsCommitWhenComponentLibraryBoundsAreCleared()
{
var pendingSession = DesktopEditSession.CreatePendingNew(
componentId: "demo",
pageIndex: 0,
widthCells: 2,
heightCells: 2,
startPointerInViewport: new Point(80, 80),
pointerOffsetInViewport: new Point(60, 60),
componentLibraryBounds: null)
.WithCurrentPointer(new Point(200, 180));
Assert.True(pendingSession.HasExceededThreshold(DesktopPlacementMath.ComputeDragStartThreshold(80)));
Assert.False(pendingSession.IsPointerInsideComponentLibrary());
Assert.False(pendingSession.IsPreviewOccludedByComponentLibrary(new Rect(100, 100, 40, 40)));
Assert.False(pendingSession.CanCommit);
var resizeSession = DesktopEditSession.CreateResizingExisting(
componentId: "demo",
placementId: "placement-1",
pageIndex: 0,
widthCells: 2,
heightCells: 2,
startPointerInViewport: new Point(80, 80),
componentLibraryBounds: null)
.WithCurrentPointer(new Point(200, 180))
.WithTargetCell(row: 2, column: 3);
Assert.False(resizeSession.IsPointerInsideComponentLibrary());
Assert.False(resizeSession.IsPreviewOccludedByComponentLibrary(new Rect(100, 100, 40, 40)));
Assert.True(resizeSession.CanCommit);
}
}

View File

@@ -0,0 +1,280 @@
using System;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
namespace LanMountainDesktop.DesktopEditing;
internal sealed class ComponentLibraryCollapsePresenter
{
private static readonly TimeSpan TransitionDuration = TimeSpan.FromMilliseconds(150);
private static readonly Easing TransitionEasing = new CubicEaseOut();
private const double StableOpacityThreshold = 0.01;
private readonly Border _componentLibraryWindow;
private readonly Border _collapsedChipHost;
private readonly TextBlock _collapsedChipTextBlock;
private readonly Control? _collapsedChipIcon;
private readonly TranslateTransform _windowTranslate = new();
private readonly TranslateTransform _chipTranslate = new();
private readonly ScaleTransform _chipScale = new(1, 1);
private ComponentLibraryCollapseState _state;
private int _transitionVersion;
public ComponentLibraryCollapsePresenter(
Border componentLibraryWindow,
Border collapsedChipHost,
TextBlock collapsedChipTextBlock,
Control? collapsedChipIcon = null)
{
_componentLibraryWindow = componentLibraryWindow ?? throw new ArgumentNullException(nameof(componentLibraryWindow));
_collapsedChipHost = collapsedChipHost ?? throw new ArgumentNullException(nameof(collapsedChipHost));
_collapsedChipTextBlock = collapsedChipTextBlock ?? throw new ArgumentNullException(nameof(collapsedChipTextBlock));
_collapsedChipIcon = collapsedChipIcon;
EnsureTransforms();
_state = ComponentLibraryCollapseState.CreateExpanded(
_componentLibraryWindow.Margin,
_componentLibraryWindow.Opacity <= 0 ? 1 : _componentLibraryWindow.Opacity);
ApplyExpandedSnapshot();
_collapsedChipHost.IsVisible = false;
_collapsedChipHost.IsHitTestVisible = false;
_collapsedChipHost.Opacity = 0;
}
public bool IsCollapsed => _state.VisualState is ComponentLibraryCollapseVisualState.Collapsing or ComponentLibraryCollapseVisualState.Collapsed;
public ComponentLibraryCollapseVisualState VisualState => _state.VisualState;
public void SyncExpandedState(Thickness margin, double opacity)
{
var hasStableOpacity = IsStableExpandedOpacity(opacity);
var nextExpandedOpacity = hasStableOpacity ? Math.Clamp(opacity, 0, 1) : _state.ExpandedOpacity;
_state = _state with
{
ExpandedMargin = margin,
ExpandedOpacity = nextExpandedOpacity
};
if (_state.VisualState is ComponentLibraryCollapseVisualState.Expanded or ComponentLibraryCollapseVisualState.Restoring)
{
ApplyExpandedSnapshot(applyOpacity: hasStableOpacity);
}
}
public void Collapse(string title)
{
_collapsedChipTextBlock.Text = string.IsNullOrWhiteSpace(title) ? "Widgets" : title;
if (_state.VisualState is ComponentLibraryCollapseVisualState.Collapsing or ComponentLibraryCollapseVisualState.Collapsed)
{
ShowCollapsedChip(_transitionVersion);
return;
}
var version = ++_transitionVersion;
_state = _state.WithVisualState(ComponentLibraryCollapseVisualState.Collapsing, isChipVisible: true);
ApplyExpandedSnapshot();
ShowCollapsedChip(version);
SetCollapsedWindowTargets();
DispatcherTimer.RunOnce(
() =>
{
if (version != _transitionVersion)
{
return;
}
_state = _state.WithVisualState(ComponentLibraryCollapseVisualState.Collapsed, isChipVisible: true);
_componentLibraryWindow.IsVisible = false;
_componentLibraryWindow.IsHitTestVisible = false;
},
TransitionDuration);
}
public void Restore()
{
if (_state.VisualState is ComponentLibraryCollapseVisualState.Expanded)
{
ApplyExpandedSnapshot();
_collapsedChipHost.IsVisible = false;
_collapsedChipHost.IsHitTestVisible = false;
_collapsedChipHost.Opacity = 0;
return;
}
var version = ++_transitionVersion;
_state = _state.WithVisualState(ComponentLibraryCollapseVisualState.Restoring, isChipVisible: false);
PrepareRestoringWindow();
HideCollapsedChip(version);
Dispatcher.UIThread.Post(
() =>
{
if (version != _transitionVersion)
{
return;
}
_componentLibraryWindow.Opacity = _state.ExpandedOpacity;
_windowTranslate.Y = 0;
},
DispatcherPriority.Background);
DispatcherTimer.RunOnce(
() =>
{
if (version != _transitionVersion)
{
return;
}
_state = _state.WithVisualState(ComponentLibraryCollapseVisualState.Expanded, isChipVisible: false);
_componentLibraryWindow.IsVisible = true;
_componentLibraryWindow.IsHitTestVisible = true;
},
TransitionDuration);
}
private void EnsureTransforms()
{
_componentLibraryWindow.RenderTransform = _windowTranslate;
_windowTranslate.Transitions = new Transitions
{
new DoubleTransition
{
Property = TranslateTransform.YProperty,
Duration = TransitionDuration,
Easing = TransitionEasing
}
};
_collapsedChipHost.RenderTransformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
_collapsedChipHost.RenderTransform = new TransformGroup
{
Children =
{
_chipTranslate,
_chipScale
}
};
_chipTranslate.Transitions = new Transitions
{
new DoubleTransition
{
Property = TranslateTransform.YProperty,
Duration = TransitionDuration,
Easing = TransitionEasing
}
};
_chipScale.Transitions = new Transitions
{
new DoubleTransition
{
Property = ScaleTransform.ScaleXProperty,
Duration = TransitionDuration,
Easing = TransitionEasing
},
new DoubleTransition
{
Property = ScaleTransform.ScaleYProperty,
Duration = TransitionDuration,
Easing = TransitionEasing
}
};
}
private void ApplyExpandedSnapshot(bool applyOpacity = true)
{
_componentLibraryWindow.Margin = _state.ExpandedMargin;
if (applyOpacity)
{
_componentLibraryWindow.Opacity = _state.ExpandedOpacity;
}
_componentLibraryWindow.IsVisible = true;
_componentLibraryWindow.IsHitTestVisible = true;
_windowTranslate.Y = 0;
}
private void SetCollapsedWindowTargets()
{
_componentLibraryWindow.Opacity = 0;
_windowTranslate.Y = 28;
}
private void ShowCollapsedChip(int version)
{
_collapsedChipHost.IsVisible = true;
_collapsedChipHost.IsHitTestVisible = false;
_collapsedChipTextBlock.IsVisible = true;
if (_collapsedChipIcon is not null)
{
_collapsedChipIcon.IsVisible = true;
}
_collapsedChipHost.Opacity = 0;
_chipTranslate.Y = 8;
_chipScale.ScaleX = 0.96;
_chipScale.ScaleY = 0.96;
Dispatcher.UIThread.Post(
() =>
{
if (version != _transitionVersion)
{
return;
}
_collapsedChipHost.Opacity = 1;
_chipTranslate.Y = 0;
_chipScale.ScaleX = 1;
_chipScale.ScaleY = 1;
},
DispatcherPriority.Background);
}
private void HideCollapsedChip(int version)
{
_collapsedChipHost.IsVisible = true;
_collapsedChipHost.IsHitTestVisible = false;
_collapsedChipHost.Opacity = 0;
_chipTranslate.Y = 8;
_chipScale.ScaleX = 0.96;
_chipScale.ScaleY = 0.96;
DispatcherTimer.RunOnce(
() =>
{
if (version != _transitionVersion)
{
return;
}
_collapsedChipHost.IsVisible = false;
},
TransitionDuration);
}
private void PrepareRestoringWindow()
{
_componentLibraryWindow.IsVisible = true;
_componentLibraryWindow.IsHitTestVisible = true;
_componentLibraryWindow.Margin = _state.ExpandedMargin;
_componentLibraryWindow.Opacity = 0;
_windowTranslate.Y = 28;
}
private static bool IsStableExpandedOpacity(double opacity)
{
return !double.IsNaN(opacity) &&
!double.IsInfinity(opacity) &&
opacity > StableOpacityThreshold;
}
}

View File

@@ -0,0 +1,36 @@
using Avalonia;
namespace LanMountainDesktop.DesktopEditing;
internal enum ComponentLibraryCollapseVisualState
{
Expanded,
Collapsing,
Collapsed,
Restoring
}
internal readonly record struct ComponentLibraryCollapseState(
ComponentLibraryCollapseVisualState VisualState,
Thickness ExpandedMargin,
double ExpandedOpacity,
bool IsChipVisible)
{
public static ComponentLibraryCollapseState CreateExpanded(Thickness expandedMargin, double expandedOpacity)
{
return new(
ComponentLibraryCollapseVisualState.Expanded,
expandedMargin,
expandedOpacity,
IsChipVisible: false);
}
public ComponentLibraryCollapseState WithVisualState(ComponentLibraryCollapseVisualState visualState, bool isChipVisible)
{
return this with
{
VisualState = visualState,
IsChipVisible = isChipVisible
};
}
}

View File

@@ -9,6 +9,12 @@ using LanMountainDesktop.Theme;
namespace LanMountainDesktop.DesktopEditing;
internal enum DesktopEditGhostVisualStyle
{
StandardLift = 0,
ElevatedFromLibrary
}
internal sealed class DesktopEditOverlayPresenter
{
private static readonly TimeSpan FastDuration = FluttermotionToken.Fast;
@@ -133,14 +139,16 @@ internal sealed class DesktopEditOverlayPresenter
UpdateCandidateAppearance();
}
public void Show()
public void Show(DesktopEditGhostVisualStyle visualStyle = DesktopEditGhostVisualStyle.StandardLift)
{
_dismissVersion++;
_isVisible = true;
_root.IsVisible = true;
_root.Opacity = 0;
_ghostView.Opacity = 0;
_ghostView.SetRestingScale(0.96);
var initialGhostScale = visualStyle == DesktopEditGhostVisualStyle.ElevatedFromLibrary ? 1.02 : 0.985;
var targetGhostScale = visualStyle == DesktopEditGhostVisualStyle.ElevatedFromLibrary ? 1.06 : 1;
_ghostView.SetRestingScale(initialGhostScale);
_candidateOutline.Opacity = 0;
_candidateScale.ScaleX = 0.96;
_candidateScale.ScaleY = 0.96;
@@ -154,7 +162,7 @@ internal sealed class DesktopEditOverlayPresenter
_root.Opacity = 1;
_ghostView.Opacity = 1;
_ghostView.SetRestingScale(1);
_ghostView.SetRestingScale(targetGhostScale);
if (_candidateRect.HasValue)
{
_candidateOutline.Opacity = 1;

View File

@@ -453,6 +453,7 @@ public partial class MainWindow
_isComponentLibraryOpen = true;
UpdateDesktopComponentHostEditState();
ShowComponentLibraryCategoryView();
RestoreComponentLibraryAfterDesktopEdit();
ComponentLibraryWindow.IsVisible = true;
ComponentLibraryWindow.Opacity = 0;
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
@@ -467,6 +468,7 @@ public partial class MainWindow
BuildComponentLibraryCategoryPages();
ComponentLibraryWindow.Opacity = 1;
SyncComponentLibraryCollapseExpandedState();
}, DispatcherPriority.Background);
}
@@ -477,6 +479,7 @@ public partial class MainWindow
return;
}
RestoreComponentLibraryAfterDesktopEdit();
_isComponentLibraryOpen = false;
CancelDesktopComponentDrag();
CancelDesktopComponentResize(restoreOriginalSpan: true);
@@ -2056,13 +2059,14 @@ public partial class MainWindow
pointerOffset,
GetComponentLibraryBoundsInViewport());
CollapseComponentLibraryForDesktopEdit(ResolveDesktopEditTitle(placement.ComponentId));
SetDesktopEditSourceHost(sourceHost, 0.22);
EnsureDesktopEditOverlayPresenter();
UpdateDesktopEditOverlayMetadata(placement.ComponentId, widthCells, heightCells, L("component.move", "Move"));
_desktopEditOverlayPresenter?.SetPreviewRect(_desktopEditOriginalRect);
_desktopEditOverlayPresenter?.SetCandidateRect(_desktopEditOriginalRect);
_desktopEditOverlayPresenter?.SetInvalid(false);
_desktopEditOverlayPresenter?.Show();
_desktopEditOverlayPresenter?.Show(DesktopEditGhostVisualStyle.StandardLift);
UpdateDesktopEditSession(pointerInViewport);
e.Pointer.Capture(this);
@@ -2208,13 +2212,14 @@ public partial class MainWindow
TargetColumn = placement.Column
};
CollapseComponentLibraryForDesktopEdit(ResolveDesktopEditTitle(placement.ComponentId));
SetDesktopEditSourceHost(sourceHost, 0.22);
EnsureDesktopEditOverlayPresenter();
UpdateDesktopEditOverlayMetadata(placement.ComponentId, startSpan.WidthCells, startSpan.HeightCells, L("component.resize", "Resize"));
_desktopEditOverlayPresenter?.SetPreviewRect(_desktopEditOriginalRect);
_desktopEditOverlayPresenter?.SetCandidateRect(_desktopEditOriginalRect);
_desktopEditOverlayPresenter?.SetInvalid(false);
_desktopEditOverlayPresenter?.Show();
_desktopEditOverlayPresenter?.Show(DesktopEditGhostVisualStyle.StandardLift);
UpdateDesktopEditSession(pointerInViewport);
e.Pointer.Capture(this);
}
@@ -2883,7 +2888,7 @@ public partial class MainWindow
private void OnComponentLibraryWindowPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (ComponentLibraryWindow is null || !_isComponentLibraryOpen)
if (ComponentLibraryWindow is null || !_isComponentLibraryOpen || IsComponentLibraryTemporarilyCollapsedForDesktopEdit())
{
return;
}
@@ -2904,6 +2909,17 @@ public partial class MainWindow
private void OnComponentLibraryWindowPointerMoved(object? sender, PointerEventArgs e)
{
if (IsComponentLibraryTemporarilyCollapsedForDesktopEdit())
{
if (_isComponentLibraryWindowDragging)
{
_isComponentLibraryWindowDragging = false;
e.Pointer.Capture(null);
}
return;
}
if (!_isComponentLibraryWindowDragging || ComponentLibraryWindow is null)
{
return;
@@ -2920,11 +2936,23 @@ public partial class MainWindow
);
ComponentLibraryWindow.Margin = newMargin;
SyncComponentLibraryCollapseExpandedState();
e.Handled = true;
}
private void OnComponentLibraryWindowPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (IsComponentLibraryTemporarilyCollapsedForDesktopEdit())
{
if (_isComponentLibraryWindowDragging)
{
_isComponentLibraryWindowDragging = false;
e.Pointer.Capture(null);
}
return;
}
if (!_isComponentLibraryWindowDragging)
{
return;
@@ -3111,6 +3139,7 @@ public partial class MainWindow
var margin = ComponentLibraryWindow.Margin;
_savedComponentLibraryMargin = margin;
_isComponentLibraryWindowPositionCustomized = true;
SyncComponentLibraryCollapseExpandedState();
}
private void RestoreComponentLibraryWindowPosition()
@@ -3121,6 +3150,7 @@ public partial class MainWindow
}
ComponentLibraryWindow.Margin = _savedComponentLibraryMargin;
SyncComponentLibraryCollapseExpandedState();
}
private Thickness _savedComponentLibraryMargin = new Thickness(24, 24, 24, 100);

View File

@@ -32,6 +32,7 @@ public partial class MainWindow
private int _desktopEditOverlayVersion;
private int _desktopEditCommitVersion;
private bool _isDesktopEditCommitPending;
private ComponentLibraryCollapsePresenter? _componentLibraryCollapsePresenter;
private bool HasActiveDesktopEditSession => _desktopEditSession.IsActive || _isDesktopEditCommitPending;
@@ -79,6 +80,89 @@ public partial class MainWindow
_desktopEditOverlayPresenter.SetViewportSize(new Size(width, height));
}
private void EnsureComponentLibraryCollapsePresenter()
{
if (_componentLibraryCollapsePresenter is not null || ComponentLibraryWindow is null)
{
return;
}
var collapsedChipHost = this.FindControl<Border>("ComponentLibraryCollapsedChipHost");
var collapsedChipTextBlock = this.FindControl<TextBlock>("ComponentLibraryCollapsedChipTextBlock");
var collapsedChipIcon = this.FindControl<Control>("ComponentLibraryCollapsedChipIcon");
if (collapsedChipHost is null || collapsedChipTextBlock is null)
{
return;
}
_componentLibraryCollapsePresenter = new ComponentLibraryCollapsePresenter(
ComponentLibraryWindow,
collapsedChipHost,
collapsedChipTextBlock,
collapsedChipIcon);
}
private bool IsComponentLibraryTemporarilyCollapsedForDesktopEdit()
{
EnsureComponentLibraryCollapsePresenter();
return _componentLibraryCollapsePresenter is not null &&
_componentLibraryCollapsePresenter.VisualState != ComponentLibraryCollapseVisualState.Expanded;
}
private void SyncComponentLibraryCollapseExpandedState()
{
if (!_isComponentLibraryOpen || ComponentLibraryWindow is null)
{
return;
}
EnsureComponentLibraryCollapsePresenter();
if (_componentLibraryCollapsePresenter is null)
{
return;
}
_componentLibraryCollapsePresenter.SyncExpandedState(ComponentLibraryWindow.Margin, ComponentLibraryWindow.Opacity);
}
private void CollapseComponentLibraryForDesktopEdit(string? title)
{
if (!_isComponentLibraryOpen)
{
return;
}
EnsureComponentLibraryCollapsePresenter();
if (_componentLibraryCollapsePresenter is null)
{
return;
}
SyncComponentLibraryCollapseExpandedState();
_componentLibraryCollapsePresenter.Collapse(ResolveComponentLibraryCollapsedChipTitle());
}
private string ResolveComponentLibraryCollapsedChipTitle()
{
if (!string.IsNullOrWhiteSpace(ComponentLibraryTitleTextBlock?.Text))
{
return ComponentLibraryTitleTextBlock.Text;
}
return L("button.component_library", "Widgets");
}
private void RestoreComponentLibraryAfterDesktopEdit()
{
EnsureComponentLibraryCollapsePresenter();
if (_componentLibraryCollapsePresenter is null)
{
return;
}
_componentLibraryCollapsePresenter.Restore();
}
private bool TryGetCurrentDesktopGridGeometry(out DesktopGridGeometry geometry)
{
geometry = default;
@@ -109,6 +193,7 @@ public partial class MainWindow
private Rect? GetComponentLibraryBoundsInViewport()
{
if (!_isComponentLibraryOpen ||
IsComponentLibraryTemporarilyCollapsedForDesktopEdit() ||
ComponentLibraryWindow is null ||
DesktopPagesViewport is null ||
!ComponentLibraryWindow.IsVisible ||
@@ -214,6 +299,8 @@ public partial class MainWindow
private void CancelDesktopEditSession(bool animate)
{
RestoreComponentLibraryAfterDesktopEdit();
if (_isDesktopEditCommitPending)
{
_desktopEditCommitVersion++;
@@ -273,11 +360,13 @@ public partial class MainWindow
if (!CanCommitDesktopEditAtRect(finalRect))
{
RestoreComponentLibraryAfterDesktopEdit();
ResetDesktopEditState();
return;
}
commitAction();
RestoreComponentLibraryAfterDesktopEdit();
ResetDesktopEditState();
},
DesktopEditOverlayAnimationDuration);
@@ -319,8 +408,10 @@ public partial class MainWindow
}
_desktopEditSession = _desktopEditSession.PromoteToDraggingNew();
CollapseComponentLibraryForDesktopEdit(ResolveDesktopEditTitle(_desktopEditSession.ComponentId ?? string.Empty));
_desktopEditSession = _desktopEditSession.WithComponentLibraryBounds(GetComponentLibraryBoundsInViewport());
EnsureDesktopEditOverlayPresenter();
_desktopEditOverlayPresenter?.Show();
_desktopEditOverlayPresenter?.Show(DesktopEditGhostVisualStyle.ElevatedFromLibrary);
UpdateActiveDesktopDragPreview();
}

View File

@@ -588,6 +588,44 @@
</Border>
</Grid>
</Border>
<Border x:Name="ComponentLibraryCollapsedChipHost"
IsVisible="False"
IsHitTestVisible="False"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Margin="0,0,0,88"
Padding="14,10"
CornerRadius="999"
Classes="surface-translucent-strong"
BorderBrush="{DynamicResource AdaptiveDockGlassBorderBrush}"
BorderThickness="1"
Opacity="0">
<Border.Transitions>
<Transitions>
<DoubleTransition Property="Opacity" Duration="{StaticResource FluttermotionToken.Duration.Fast}" />
</Transitions>
</Border.Transitions>
<Grid ColumnDefinitions="Auto,*"
ColumnSpacing="10">
<fi:FluentIcon x:Name="ComponentLibraryCollapsedChipIcon"
Grid.Column="0"
Icon="Apps"
IconVariant="Regular"
Width="18"
Height="18"
VerticalAlignment="Center"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
<TextBlock x:Name="ComponentLibraryCollapsedChipTextBlock"
Grid.Column="1"
VerticalAlignment="Center"
FontSize="14"
FontWeight="SemiBold"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Text="Widgets" />
</Grid>
</Border>
</Grid>
</Window>