|
56 | 56 | Foreground="#333"/> |
57 | 57 | </Border> |
58 | 58 |
|
59 | | - <!-- Image canvas (checkerboard background). Three mutually |
60 | | - exclusive layout overlays, switched by Mode. --> |
| 59 | + <!-- Image canvas (checkerboard background). Either three |
| 60 | + mutually exclusive layout overlays (HasBothImages = true, |
| 61 | + switched by Mode) or a single full-canvas image for |
| 62 | + add-only / delete-only changes. --> |
61 | 63 | <Border Grid.Row="1" Background="{StaticResource CheckerboardBrush}"> |
62 | 64 | <Grid> |
63 | | - <!-- SideBySide: two columns with a thin gap. --> |
64 | | - <Grid Visibility="{Binding Mode, Converter={x:Static vm:EnumToVisibilityConverter.Instance}, ConverterParameter={x:Static models:ImageDiffMode.SideBySide}}"> |
65 | | - <Grid.ColumnDefinitions> |
66 | | - <ColumnDefinition Width="*"/> |
67 | | - <ColumnDefinition Width="2"/> |
68 | | - <ColumnDefinition Width="*"/> |
69 | | - </Grid.ColumnDefinitions> |
70 | | - <Image Grid.Column="0" |
71 | | - Source="{Binding LeftImage}" |
72 | | - Stretch="Uniform" |
73 | | - Margin="4"/> |
74 | | - <Border Grid.Column="1" Background="#DDD"/> |
75 | | - <Image Grid.Column="2" |
76 | | - Source="{Binding RightImage}" |
77 | | - Stretch="Uniform" |
78 | | - Margin="4"/> |
79 | | - </Grid> |
| 65 | + <!-- Single-image fallback for add-only / delete-only |
| 66 | + changes. Renders whichever side has a bitmap full- |
| 67 | + canvas with Stretch=Uniform; the three modes have |
| 68 | + no meaningful semantics when one side is absent. --> |
| 69 | + <Image Source="{Binding SingleImage}" |
| 70 | + Stretch="Uniform" |
| 71 | + Margin="4" |
| 72 | + Visibility="{Binding HasBothImages, Converter={x:Static vm:FalseToVisibleConverter.Instance}}"/> |
80 | 73 |
|
81 | | - <!-- Swipe: both images stacked, left image clipped to the |
82 | | - area left of the draggable divider. Clip + divider |
83 | | - position are updated from code-behind in response to |
84 | | - SizeChanged + SwipePosition / Mode property changes. --> |
85 | | - <Grid x:Name="SwipeCanvas" |
86 | | - Background="Transparent" |
87 | | - Visibility="{Binding Mode, Converter={x:Static vm:EnumToVisibilityConverter.Instance}, ConverterParameter={x:Static models:ImageDiffMode.Swipe}}"> |
88 | | - <Image Source="{Binding RightImage}" Stretch="Uniform"/> |
89 | | - <Image x:Name="SwipeLeftImage" |
90 | | - Source="{Binding LeftImage}" |
91 | | - Stretch="Uniform"> |
92 | | - <Image.Clip> |
93 | | - <RectangleGeometry x:Name="SwipeLeftClip" Rect="0,0,0,0"/> |
94 | | - </Image.Clip> |
95 | | - </Image> |
96 | | - <Line x:Name="SwipeDividerLine" |
97 | | - Stroke="White" |
98 | | - StrokeThickness="2" |
99 | | - Y1="0" Y2="0" |
100 | | - X1="0" X2="0" |
101 | | - IsHitTestVisible="False"/> |
102 | | - </Grid> |
| 74 | + <!-- Three-mode compositing container, gated on |
| 75 | + HasBothImages so it stays Collapsed when single- |
| 76 | + sided (the Swipe code-behind also early-outs when |
| 77 | + SwipeCanvas has zero size). --> |
| 78 | + <Grid Visibility="{Binding HasBothImages, Converter={x:Static vm:BoolToVisibilityConverter.Instance}}"> |
| 79 | + <!-- SideBySide: two columns with a thin gap. --> |
| 80 | + <Grid Visibility="{Binding Mode, Converter={x:Static vm:EnumToVisibilityConverter.Instance}, ConverterParameter={x:Static models:ImageDiffMode.SideBySide}}"> |
| 81 | + <Grid.ColumnDefinitions> |
| 82 | + <ColumnDefinition Width="*"/> |
| 83 | + <ColumnDefinition Width="2"/> |
| 84 | + <ColumnDefinition Width="*"/> |
| 85 | + </Grid.ColumnDefinitions> |
| 86 | + <Image Grid.Column="0" |
| 87 | + Source="{Binding LeftImage}" |
| 88 | + Stretch="Uniform" |
| 89 | + Margin="4"/> |
| 90 | + <Border Grid.Column="1" Background="#DDD"/> |
| 91 | + <Image Grid.Column="2" |
| 92 | + Source="{Binding RightImage}" |
| 93 | + Stretch="Uniform" |
| 94 | + Margin="4"/> |
| 95 | + </Grid> |
103 | 96 |
|
104 | | - <!-- OnionSkin: left image full opacity, right image overlay |
105 | | - with user-controllable opacity. --> |
106 | | - <Grid Visibility="{Binding Mode, Converter={x:Static vm:EnumToVisibilityConverter.Instance}, ConverterParameter={x:Static models:ImageDiffMode.OnionSkin}}"> |
107 | | - <Image Source="{Binding LeftImage}" Stretch="Uniform"/> |
108 | | - <Image Source="{Binding RightImage}" |
109 | | - Stretch="Uniform" |
110 | | - Opacity="{Binding OnionOpacity}"/> |
| 97 | + <!-- Swipe: both images stacked, left image clipped to the |
| 98 | + area left of the draggable divider. Clip + divider |
| 99 | + position are updated from code-behind in response to |
| 100 | + SizeChanged + SwipePosition / Mode property changes. --> |
| 101 | + <Grid x:Name="SwipeCanvas" |
| 102 | + Background="Transparent" |
| 103 | + Visibility="{Binding Mode, Converter={x:Static vm:EnumToVisibilityConverter.Instance}, ConverterParameter={x:Static models:ImageDiffMode.Swipe}}"> |
| 104 | + <Image Source="{Binding RightImage}" Stretch="Uniform"/> |
| 105 | + <Image x:Name="SwipeLeftImage" |
| 106 | + Source="{Binding LeftImage}" |
| 107 | + Stretch="Uniform"> |
| 108 | + <Image.Clip> |
| 109 | + <RectangleGeometry x:Name="SwipeLeftClip" Rect="0,0,0,0"/> |
| 110 | + </Image.Clip> |
| 111 | + </Image> |
| 112 | + <Line x:Name="SwipeDividerLine" |
| 113 | + Stroke="White" |
| 114 | + StrokeThickness="2" |
| 115 | + Y1="0" Y2="0" |
| 116 | + X1="0" X2="0" |
| 117 | + IsHitTestVisible="False"/> |
| 118 | + </Grid> |
| 119 | + |
| 120 | + <!-- OnionSkin: left image full opacity, right image overlay |
| 121 | + with user-controllable opacity. --> |
| 122 | + <Grid Visibility="{Binding Mode, Converter={x:Static vm:EnumToVisibilityConverter.Instance}, ConverterParameter={x:Static models:ImageDiffMode.OnionSkin}}"> |
| 123 | + <Image Source="{Binding LeftImage}" Stretch="Uniform"/> |
| 124 | + <Image Source="{Binding RightImage}" |
| 125 | + Stretch="Uniform" |
| 126 | + Opacity="{Binding OnionOpacity}"/> |
| 127 | + </Grid> |
111 | 128 | </Grid> |
112 | 129 | </Grid> |
113 | 130 | </Border> |
114 | 131 |
|
115 | 132 | <!-- Bottom controls row. Slider is mode-specific: Swipe uses the |
116 | 133 | whole-canvas divider position; OnionSkin uses the right-side |
117 | | - opacity. SideBySide gets nothing. --> |
| 134 | + opacity. SideBySide gets nothing. Hidden entirely for |
| 135 | + single-sided changes since neither slider does anything. --> |
118 | 136 | <Border Grid.Row="2" |
119 | 137 | Background="#F8F8F8" |
120 | 138 | BorderBrush="#DDD" |
121 | 139 | BorderThickness="0,1,0,0" |
122 | | - Padding="12,6"> |
| 140 | + Padding="12,6" |
| 141 | + Visibility="{Binding HasBothImages, Converter={x:Static vm:BoolToVisibilityConverter.Instance}}"> |
123 | 142 | <Grid> |
124 | 143 | <StackPanel Orientation="Horizontal" |
125 | 144 | Visibility="{Binding Mode, Converter={x:Static vm:EnumToVisibilityConverter.Instance}, ConverterParameter={x:Static models:ImageDiffMode.Swipe}}"> |
|
0 commit comments