PlatformChrome.FullBleed 추가 - 화면을 꽉 쓰는 모듈이 셸 여백을 끌 수 있게

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
최민영 2026-09-21 22:44:28 +09:00
parent 00b3754063
commit 6991dfdfcf
12 changed files with 105 additions and 4 deletions

2
dist/README.txt vendored
View File

@ -1,5 +1,5 @@
UbisamBase 플랫폼 배포 폴더 (빌드할 때 자동 생성 - 직접 고치지 마세요) UbisamBase 플랫폼 배포 폴더 (빌드할 때 자동 생성 - 직접 고치지 마세요)
갱신 시각 : 2026-09-21 22:42:14 갱신 시각 : 2026-09-21 22:44:28
빌드 구성 : Release 빌드 구성 : Release
원본 소스 : D:\업무\03_개발\Source\용접기\08_UbisamPlatform\ 원본 소스 : D:\업무\03_개발\Source\용접기\08_UbisamPlatform\
---- ----

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
{ {
"version": "2026-09-21 22:42:14", "version": "2026-09-21 22:44:28",
"configuration": "Release" "configuration": "Release"
} }

View File

@ -19,3 +19,4 @@
| 2026-09-21 | 업데이트 진행 창 추가 — 런처를 누르자마자 확인/받기 상태와 진행률을 보여주고, 다 받은 뒤 설치 여부를 묻는다 | | 2026-09-21 | 업데이트 진행 창 추가 — 런처를 누르자마자 확인/받기 상태와 진행률을 보여주고, 다 받은 뒤 설치 여부를 묻는다 |
| 2026-09-21 | Shell.exe를 직접 실행하면 런처로 넘기도록 — 진입점을 런처 하나로 통일(업데이트 확인 누락 방지) | | 2026-09-21 | Shell.exe를 직접 실행하면 런처로 넘기도록 — 진입점을 런처 하나로 통일(업데이트 확인 누락 방지) |
| 2026-09-21 | 런처 창(진행·업데이트 안내)이 앱의 테마 설정(다크/라이트)을 따르도록 — 윈도우 설정만 보던 것을 수정 | | 2026-09-21 | 런처 창(진행·업데이트 안내)이 앱의 테마 설정(다크/라이트)을 따르도록 — 윈도우 설정만 보던 것을 수정 |
| 2026-09-21 | `PlatformChrome.FullBleed` 첨부 속성 추가 — 화면을 꽉 쓰는 모듈이 셸 여백을 끌 수 있게 (건의 6) |

View File

@ -485,6 +485,26 @@ public partial class DeckViewModel : ObservableObject, ICloseGuard
이 확인을 건너뛴다 — 모듈에서는 쓰지 말 것. 이 확인을 건너뛴다 — 모듈에서는 쓰지 말 것.
- UI 설정의 "상단 바 접기"는 사용자가 저장해 두는 별개 취향이다. 둘 중 하나라도 숨기라고 하면 상단 바는 숨는다. - UI 설정의 "상단 바 접기"는 사용자가 저장해 두는 별개 취향이다. 둘 중 하나라도 숨기라고 하면 상단 바는 숨는다.
## 8-4. 화면을 꽉 쓰는 모듈 — `PlatformChrome.FullBleed`
셸은 모듈 화면 둘레에 여백(26,22)을 준다. 설정 화면이나 폼처럼 글을 읽는 화면에는 그 여백이
있어야 보기 좋지만, 편집기·지도·카메라처럼 **자기 안에서 이미 칸을 나누는 모듈**은 여백이 한 겹 더
붙으면 화면만 좁아지고 테두리처럼 보인다. 그런 모듈은 View에 첨부 속성 하나만 붙이면 된다.
```xml
<UserControl x:Class="MyModule.Views.EditorView"
xmlns:shell="clr-namespace:UbisamBase.Core.Shell;assembly=UbisamBase.Core"
shell:PlatformChrome.FullBleed="True">
```
- 기본은 여백 있음(False). 이 화면이 떠 있는 동안만 여백이 사라지고, 다른 탭으로 옮기면 그 탭의
설정을 따른다.
- 발표자 모드(`PlatformChrome.IsVisible = false`)에서는 이 값과 무관하게 여백이 0이다.
- 코드에서 바꿔도 된다 — `PlatformChrome.SetFullBleed(view, true)`. 떠 있는 동안 바꿔도 셸이 다시
계산한다(`FullBleedChanged`).
- **비주얼 트리를 거슬러 올라가 Border의 Padding을 직접 0으로 만들지 말 것.** 플랫폼 레이아웃이
바뀌면 그대로 깨진다 — 이 속성이 그 자리를 대신한다.
## 9. 테마 · UI 컨벤션 ## 9. 테마 · UI 컨벤션
- `Ubisam.Brush.*`, `Ubisam.Style.*`로 시작하는 리소스가 다크/라이트 테마 전체를 정의 - `Ubisam.Brush.*`, `Ubisam.Style.*`로 시작하는 리소스가 다크/라이트 테마 전체를 정의

View File

@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using UbisamBase.Core.Shell;
namespace UbisamBase.Core.Converters;
/// <summary>
/// 셸 컨텐츠 영역의 여백을 정한다. 값 두 개를 받는다 — (1) 플랫폼 껍데기가 보이는지,
/// (2) 지금 올라와 있는 모듈 화면.
///
/// 발표자 모드(껍데기 숨김)이거나 그 화면이 <see cref="PlatformChrome.FullBleedProperty"/>를 켜 뒀으면
/// 여백 0, 그 외에는 <see cref="Default"/>.
/// </summary>
public class ContentPaddingConverter : IMultiValueConverter
{
/// <summary>평소 여백. ShellStyles.xaml에서 지정한다.</summary>
public Thickness Default { get; set; }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var chromeVisible = values.Length > 0 && values[0] is bool visible && visible;
if (!chromeVisible)
{
return new Thickness(0);
}
var content = values.Length > 1 ? values[1] as DependencyObject : null;
return content != null && PlatformChrome.GetFullBleed(content) ? new Thickness(0) : Default;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}

View File

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Windows;
namespace UbisamBase.Core.Shell; namespace UbisamBase.Core.Shell;
@ -38,6 +39,35 @@ public static class PlatformChrome
/// <summary>보임/숨김을 뒤집는다 — 발표 중 단축키나 버튼에 걸어 쓰기 좋다.</summary> /// <summary>보임/숨김을 뒤집는다 — 발표 중 단축키나 버튼에 걸어 쓰기 좋다.</summary>
public static void Toggle() => IsVisible = !IsVisible; public static void Toggle() => IsVisible = !IsVisible;
/// <summary>
/// 모듈 화면에 붙이는 첨부 속성. True면 셸이 그 화면 둘레의 여백(26,22)을 주지 않는다.
///
/// 편집기·지도·카메라처럼 화면을 꽉 쓰는 모듈은 자기 안에서 이미 칸을 나누고 각 칸이 자기 여백을
/// 갖는다. 거기에 바깥 여백이 한 겹 더 붙으면 화면만 좁아지고 테두리처럼 보인다. 반대로 설정
/// 화면이나 폼은 여백이 있어야 읽기 좋으므로, 기본은 여백 있음이고 필요한 모듈만 끈다.
///
/// <code>&lt;UserControl ... shell:PlatformChrome.FullBleed="True"&gt;</code>
/// </summary>
public static readonly DependencyProperty FullBleedProperty =
DependencyProperty.RegisterAttached(
"FullBleed",
typeof(bool),
typeof(PlatformChrome),
new PropertyMetadata(false, OnFullBleedChanged));
public static void SetFullBleed(DependencyObject element, bool value)
=> element.SetValue(FullBleedProperty, value);
public static bool GetFullBleed(DependencyObject element)
=> element != null && (bool)element.GetValue(FullBleedProperty);
/// <summary>화면이 떠 있는 동안 값을 바꿔도 셸이 여백을 다시 계산하도록 알린다
/// (첨부 속성은 셸의 바인딩에서 직접 감시할 수 없다).</summary>
public static event EventHandler? FullBleedChanged;
private static void OnFullBleedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> FullBleedChanged?.Invoke(d, EventArgs.Empty);
/// <summary>보임/숨김이 바뀔 때마다 새 값(true=보임)과 함께 알린다. 모듈이 자기가 바꾸지 않은 /// <summary>보임/숨김이 바뀔 때마다 새 값(true=보임)과 함께 알린다. 모듈이 자기가 바꾸지 않은
/// 변화(설정의 "상단 바 접기", 다른 모듈, 단축키)에도 반응할 수 있게 하는 통로다 — /// 변화(설정의 "상단 바 접기", 다른 모듈, 단축키)에도 반응할 수 있게 하는 통로다 —
/// 발표 모드를 나갈 때 타이머를 멈추거나 띄워둔 창을 닫는 정리에 쓴다. /// 발표 모드를 나갈 때 타이머를 멈추거나 띄워둔 창을 닫는 정리에 쓴다.

View File

@ -161,8 +161,14 @@
<!-- 모듈이 마운트되기 전에는 사선 해치가 빈 슬롯을 표시, 마운트되면 단색 배경. <!-- 모듈이 마운트되기 전에는 사선 해치가 빈 슬롯을 표시, 마운트되면 단색 배경.
여백은 발표자 모드에서 0이 된다 — 껍데기를 걷었는데 액자처럼 테두리가 남으면 안 된다. --> 여백은 발표자 모드에서 0이 된다 — 껍데기를 걷었는데 액자처럼 테두리가 남으면 안 된다. -->
<Border Grid.Row="0" <Border x:Name="ContentBorder" Grid.Row="0">
Padding="{Binding Path=(shell:PlatformChrome.IsVisible), Converter={StaticResource BoolToContentPadding}}"> <Border.Padding>
<!-- 발표자 모드이거나, 올라온 모듈 화면이 FullBleed면 여백 0. -->
<MultiBinding Converter="{StaticResource ContentPadding}">
<Binding Path="(shell:PlatformChrome.IsVisible)"/>
<Binding Path="SelectedMainTab.CurrentContent"/>
</MultiBinding>
</Border.Padding>
<Border.Style> <Border.Style>
<Style TargetType="Border"> <Style TargetType="Border">
<Setter Property="Background" Value="{DynamicResource Ubisam.Brush.ContentBackground}"/> <Setter Property="Background" Value="{DynamicResource Ubisam.Brush.ContentBackground}"/>

View File

@ -38,6 +38,10 @@ public partial class ShellWindow : Window
DataContext = viewModel; DataContext = viewModel;
viewModel.UiSettings.PropertyChanged += OnUiSettingsChanged; viewModel.UiSettings.PropertyChanged += OnUiSettingsChanged;
// FullBleed는 첨부 속성이라 바인딩이 스스로 변화를 못 본다 — 바뀌면 여백을 다시 계산한다.
PlatformChrome.FullBleedChanged += OnFullBleedChanged;
Closed += (_, _) => PlatformChrome.FullBleedChanged -= OnFullBleedChanged;
ApplyNavPosition(viewModel.UiSettings.NavPosition); ApplyNavPosition(viewModel.UiSettings.NavPosition);
ApplyToastPosition(viewModel.UiSettings.ToastPosition); ApplyToastPosition(viewModel.UiSettings.ToastPosition);
@ -313,6 +317,9 @@ public partial class ShellWindow : Window
} }
} }
private void OnFullBleedChanged(object? sender, EventArgs e)
=> BindingOperations.GetMultiBindingExpression(ContentBorder, Border.PaddingProperty)?.UpdateTarget();
private void OnUiSettingsChanged(object? sender, PropertyChangedEventArgs e) private void OnUiSettingsChanged(object? sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(UiSettingsService.NavPosition)) if (e.PropertyName == nameof(UiSettingsService.NavPosition))

View File

@ -13,6 +13,8 @@
<conv:AllBoolToVisibilityConverter x:Key="AllBoolToVis"/> <conv:AllBoolToVisibilityConverter x:Key="AllBoolToVis"/>
<!-- 발표자 모드(PlatformChrome.IsVisible=false)에서는 컨텐츠 여백을 없애 화면을 꽉 채운다. --> <!-- 발표자 모드(PlatformChrome.IsVisible=false)에서는 컨텐츠 여백을 없애 화면을 꽉 채운다. -->
<conv:BoolToThicknessConverter x:Key="BoolToContentPadding" True="26,22" False="0"/> <conv:BoolToThicknessConverter x:Key="BoolToContentPadding" True="26,22" False="0"/>
<!-- 컨텐츠 여백 — 발표자 모드이거나 그 화면이 FullBleed면 0. -->
<conv:ContentPaddingConverter x:Key="ContentPadding" Default="26,22"/>
<conv:InverseBooleanConverter x:Key="InverseBool"/> <conv:InverseBooleanConverter x:Key="InverseBool"/>
<conv:FirstCharacterConverter x:Key="FirstCharConverter"/> <conv:FirstCharacterConverter x:Key="FirstCharConverter"/>
<conv:ToastTypeToBrushConverter x:Key="ToastTypeToBrush"/> <conv:ToastTypeToBrushConverter x:Key="ToastTypeToBrush"/>