업데이트 안내창을 플랫폼 디자인으로 교체

Launcher는 Core를 참조할 수 없어(갱신 대상 Core.dll을 물게 된다) 같은 생김새의 창을
자체 XAML로 만들고, 색은 윈도우 밝게/어둡게 설정을 따라 코드에서 넣는다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
최민영 2026-09-21 22:04:49 +09:00
parent 5e5ef8c36f
commit d11f428045
8 changed files with 239 additions and 15 deletions

2
dist/README.txt vendored
View File

@ -1,5 +1,5 @@
UbisamBase 플랫폼 배포 폴더 (빌드할 때 자동 생성 - 직접 고치지 마세요)
갱신 시각 : 2026-09-21 21:55:55
갱신 시각 : 2026-09-21 22:04:42
빌드 구성 : Release
원본 소스 : 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 21:55:55",
"version": "2026-09-21 22:04:42",
"configuration": "Release"
}

View File

@ -117,20 +117,10 @@ internal static class PlatformUpdater
}
// 3) 자동 업데이트가 아니면 물어본다.
if (!settings.AutoUpdate)
if (!settings.AutoUpdate && !UpdateDialog.Ask(localVersion, remoteVersion))
{
var answer = MessageBox.Show(
"새 플랫폼 버전이 있습니다.\n\n" +
"지금 버전 : " + (string.IsNullOrEmpty(localVersion) ? "(알 수 없음)" : localVersion) + "\n" +
"새 버전 : " + remoteVersion + "\n\n" +
"지금 업데이트하고 실행할까요?",
"UbisamBase 업데이트", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (answer != MessageBoxResult.Yes)
{
// 기록을 남기지 않는다 — 다음에 켤 때 다시 물어본다.
return;
}
// 기록을 남기지 않는다 — 다음에 켤 때 다시 물어본다.
return;
}
var failed = CopyAll(remoteDist, platformDir);

View File

@ -0,0 +1,131 @@
<Window x:Class="UbisamBase.Launcher.UpdateDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="440"
SizeToContent="Height"
WindowStyle="None"
ResizeMode="NoResize"
ShowInTaskbar="False"
Topmost="True"
WindowStartupLocation="CenterScreen"
FontFamily="Barlow, Segoe UI"
FontSize="14"
PreviewKeyDown="Window_PreviewKeyDown">
<!-- Core의 MessageDialog와 같은 생김새지만 리소스를 공유하지 않는다 — Launcher가 Core를 참조하면
갱신 대상인 Core.dll을 먼저 물고 있게 되어 덮어쓸 수 없기 때문이다(PlatformUpdater 주석 참고).
그래서 색은 코드비하인드가 시스템 테마(밝게/어둡게)에 맞춰 직접 넣는다. -->
<Window.Resources>
<!-- 버튼: 채움색·글자색은 코드에서 넣고, 여기서는 모양과 hover 반응만 정의한다. -->
<Style x:Key="DialogButton" TargetType="Button">
<Setter Property="MinWidth" Value="84"/>
<Setter Property="Padding" Value="14,7"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="6"
SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Opacity" Value="0.85"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bd" Property="Opacity" Value="0.7"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Border x:Name="RootBorder" BorderThickness="1">
<DockPanel>
<!-- 제목줄: 끌어서 창 이동 -->
<Border x:Name="TitleBar"
DockPanel.Dock="Top"
BorderThickness="0,0,0,1"
Padding="16,10"
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
<TextBlock x:Name="TitleText" Text="UbisamBase 업데이트" FontWeight="SemiBold"/>
</Border>
<!-- 버튼줄: 예가 주 동작(accent 채움), 아니오가 보조(외곽선) -->
<Border x:Name="ButtonBar"
DockPanel.Dock="Bottom"
BorderThickness="0,1,0,0"
Padding="16,12">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="NoButton"
Content="나중에"
Style="{StaticResource DialogButton}"
Margin="0,0,8,0"
Click="No_Click"/>
<Button x:Name="YesButton"
Content="업데이트"
Style="{StaticResource DialogButton}"
IsDefault="True"
Click="Yes_Click"/>
</StackPanel>
</Border>
<!-- 본문: 아이콘 + 안내문 + 버전 비교 -->
<Grid Margin="20,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Path x:Name="IconPath"
Grid.Column="0"
Data="M3,12 A9,9 0 0 1 21,12 A9,9 0 0 1 3,12 Z M9.3,9.3 A2.7,2.7 0 1 1 13.3,12.6 C12.5,13.1 12,13.6 12,14.8 M12,17.4 L12,17.5"
Width="24" Height="24"
StrokeThickness="1.5"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
StrokeLineJoin="Round"
VerticalAlignment="Top"
Margin="0,0,14,0"/>
<StackPanel Grid.Column="1">
<TextBlock x:Name="HeadlineText"
Text="새 플랫폼 버전이 있습니다."
TextWrapping="Wrap"
Margin="0,0,0,14"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="CurrentLabel" Grid.Row="0" Grid.Column="0" Text="지금 버전" Margin="0,0,14,4"/>
<TextBlock x:Name="CurrentValue" Grid.Row="0" Grid.Column="1" Margin="0,0,0,4"/>
<TextBlock x:Name="NewLabel" Grid.Row="1" Grid.Column="0" Text="새 버전" Margin="0,0,14,0"/>
<TextBlock x:Name="NewValue" Grid.Row="1" Grid.Column="1" FontWeight="SemiBold"/>
</Grid>
<TextBlock x:Name="FootnoteText"
Text="지금 받으면 새 버전으로 실행합니다. 나중에를 누르면 이번에는 그대로 실행하고, 다음에 다시 물어봅니다."
TextWrapping="Wrap"
Margin="0,14,0,0"/>
</StackPanel>
</Grid>
</DockPanel>
</Border>
</Window>

View File

@ -0,0 +1,103 @@
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Win32;
namespace UbisamBase.Launcher;
/// <summary>
/// "새 버전이 있습니다 — 지금 받을까요?" 대화상자. 플랫폼(Core)의 MessageDialog와 같은 생김새지만,
/// Launcher는 Core를 참조하지 않으므로(참조하면 갱신 대상인 Core.dll을 물고 있게 된다) 색을 직접 넣는다.
///
/// 테마는 윈도우 설정(밝게/어둡게)을 따라간다 — 이 시점에는 아직 앱의 UI 설정을 읽을 수 없기 때문이다.
/// 색 값은 Core의 Colors.Dark.xaml / Colors.Light.xaml에서 같은 이름으로 가져온 것이다.
/// </summary>
public partial class UpdateDialog : Window
{
private UpdateDialog(string currentVersion, string newVersion)
{
InitializeComponent();
CurrentValue.Text = string.IsNullOrEmpty(currentVersion) ? "(알 수 없음)" : currentVersion;
NewValue.Text = newVersion;
ApplyTheme(IsSystemLightTheme());
}
/// <summary>대화상자를 띄우고 "업데이트"를 눌렀는지 돌려준다.</summary>
public static bool Ask(string currentVersion, string newVersion)
=> new UpdateDialog(currentVersion, newVersion).ShowDialog() == true;
private void ApplyTheme(bool light)
{
// Colors.Light.xaml / Colors.Dark.xaml 과 같은 값.
var background = Color(light ? "#FFF2F2F3" : "#FF23262B");
var foreground = Color(light ? "#FF1D1F20" : "#FFE8EAED");
var panel = Color(light ? "#FFFFFFFF" : "#FF2A2E34");
var border = Color(light ? "#FFD6D6D9" : "#FF3A3F47");
var muted = Color(light ? "#FF98989B" : "#FF8A9099");
var accentFill = Color(light ? "#FF1D82E8" : "#FF1E7FD4");
var onAccent = Color(light ? "#FFFFFFFF" : "#FFF2F7FC");
Background = background;
Foreground = foreground;
RootBorder.BorderBrush = border;
TitleBar.Background = panel;
TitleBar.BorderBrush = border;
TitleText.Foreground = foreground;
ButtonBar.Background = panel;
ButtonBar.BorderBrush = border;
IconPath.Stroke = foreground;
HeadlineText.Foreground = foreground;
CurrentLabel.Foreground = muted;
NewLabel.Foreground = muted;
CurrentValue.Foreground = muted;
NewValue.Foreground = foreground;
FootnoteText.Foreground = muted;
YesButton.Background = accentFill;
YesButton.BorderBrush = accentFill;
YesButton.Foreground = onAccent;
NoButton.Background = Brushes.Transparent;
NoButton.BorderBrush = border;
NoButton.Foreground = foreground;
}
private static SolidColorBrush Color(string hex)
=> new SolidColorBrush((Color)ColorConverter.ConvertFromString(hex));
/// <summary>윈도우가 밝은 테마인지. Core의 UiSettingsService.IsSystemLightTheme과 같은 판정이다.</summary>
private static bool IsSystemLightTheme()
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(
@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
return key?.GetValue("AppsUseLightTheme") is not int value || value != 0;
}
catch
{
return true;
}
}
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) => DragMove();
private void Yes_Click(object sender, RoutedEventArgs e) => DialogResult = true;
private void No_Click(object sender, RoutedEventArgs e) => DialogResult = false;
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
DialogResult = false;
}
}
}