From a0cb9929b6eaf55778e94ad2589f76200b3e8080 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Fri, 14 Aug 2026 21:52:04 +0900 Subject: [PATCH 01/24] ui: support native dark theme control --- utable/Forms/EveryTimeBrowserNew.Designer.vb | 44 +- utable/Forms/EveryTimeBrowserNew.vb | 7 +- utable/Forms/EverytimeSemesterSelector.vb | 24 + utable/Forms/OptionForm.Designer.vb | 1310 +++++++++--------- utable/Forms/OptionForm.vb | 23 + utable/Modules/GUIModule.vb | 25 + utable/UserControls/CellControl.Designer.vb | 10 +- 7 files changed, 761 insertions(+), 682 deletions(-) diff --git a/utable/Forms/EveryTimeBrowserNew.Designer.vb b/utable/Forms/EveryTimeBrowserNew.Designer.vb index 965cf9a..fe232d1 100644 --- a/utable/Forms/EveryTimeBrowserNew.Designer.vb +++ b/utable/Forms/EveryTimeBrowserNew.Designer.vb @@ -24,13 +24,13 @@ Partial Class EveryTimeBrowserNew Private Sub InitializeComponent() Me.components = New System.ComponentModel.Container() Me.TableChecker = New System.Windows.Forms.Timer(Me.components) - Me.Panel1 = New System.Windows.Forms.Panel() + Me.TopPanel = New System.Windows.Forms.Panel() Me.WebView21 = New Microsoft.Web.WebView2.WinForms.WebView2() Me.Panel2 = New System.Windows.Forms.Panel() Me.TSLinkLabel1 = New System.Windows.Forms.LinkLabel() Me.CloseBT = New System.Windows.Forms.PictureBox() Me.Label1 = New System.Windows.Forms.Label() - Me.Panel1.SuspendLayout() + Me.TopPanel.SuspendLayout() CType(Me.WebView21, System.ComponentModel.ISupportInitialize).BeginInit() Me.Panel2.SuspendLayout() CType(Me.CloseBT, System.ComponentModel.ISupportInitialize).BeginInit() @@ -40,17 +40,17 @@ Partial Class EveryTimeBrowserNew ' Me.TableChecker.Interval = 500 ' - 'Panel1 + 'TopPanel ' - Me.Panel1.BackColor = System.Drawing.Color.White - Me.Panel1.Controls.Add(Me.WebView21) - Me.Panel1.Controls.Add(Me.Panel2) - Me.Panel1.Dock = System.Windows.Forms.DockStyle.Fill - Me.Panel1.Location = New System.Drawing.Point(1, 1) - Me.Panel1.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) - Me.Panel1.Name = "Panel1" - Me.Panel1.Size = New System.Drawing.Size(502, 684) - Me.Panel1.TabIndex = 1 + Me.TopPanel.BackColor = System.Drawing.Color.White + Me.TopPanel.Controls.Add(Me.WebView21) + Me.TopPanel.Controls.Add(Me.Panel2) + Me.TopPanel.Dock = System.Windows.Forms.DockStyle.Fill + Me.TopPanel.Location = New System.Drawing.Point(1, 1) + Me.TopPanel.Margin = New System.Windows.Forms.Padding(4) + Me.TopPanel.Name = "TopPanel" + Me.TopPanel.Size = New System.Drawing.Size(502, 684) + Me.TopPanel.TabIndex = 1 ' 'WebView21 ' @@ -59,7 +59,7 @@ Partial Class EveryTimeBrowserNew Me.WebView21.DefaultBackgroundColor = System.Drawing.Color.White Me.WebView21.Dock = System.Windows.Forms.DockStyle.Fill Me.WebView21.Location = New System.Drawing.Point(0, 80) - Me.WebView21.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.WebView21.Margin = New System.Windows.Forms.Padding(4) Me.WebView21.Name = "WebView21" Me.WebView21.Size = New System.Drawing.Size(502, 604) Me.WebView21.TabIndex = 30 @@ -67,18 +67,20 @@ Partial Class EveryTimeBrowserNew ' 'Panel2 ' + Me.Panel2.BackColor = System.Drawing.Color.Transparent Me.Panel2.Controls.Add(Me.TSLinkLabel1) Me.Panel2.Controls.Add(Me.CloseBT) Me.Panel2.Controls.Add(Me.Label1) Me.Panel2.Dock = System.Windows.Forms.DockStyle.Top Me.Panel2.Location = New System.Drawing.Point(0, 0) - Me.Panel2.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.Panel2.Margin = New System.Windows.Forms.Padding(4) Me.Panel2.Name = "Panel2" Me.Panel2.Size = New System.Drawing.Size(502, 80) Me.Panel2.TabIndex = 33 ' 'TSLinkLabel1 ' + Me.TSLinkLabel1.BackColor = System.Drawing.Color.Transparent Me.TSLinkLabel1.Dock = System.Windows.Forms.DockStyle.Top Me.TSLinkLabel1.LinkColor = System.Drawing.Color.Maroon Me.TSLinkLabel1.Location = New System.Drawing.Point(0, 41) @@ -93,10 +95,10 @@ Partial Class EveryTimeBrowserNew 'CloseBT ' Me.CloseBT.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) - Me.CloseBT.BackColor = System.Drawing.Color.White + Me.CloseBT.BackColor = System.Drawing.Color.Transparent Me.CloseBT.Image = Global.uTable.My.Resources.Resources.closeicon_b Me.CloseBT.Location = New System.Drawing.Point(461, 4) - Me.CloseBT.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.CloseBT.Margin = New System.Windows.Forms.Padding(4) Me.CloseBT.Name = "CloseBT" Me.CloseBT.Size = New System.Drawing.Size(38, 38) Me.CloseBT.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom @@ -105,7 +107,7 @@ Partial Class EveryTimeBrowserNew ' 'Label1 ' - Me.Label1.BackColor = System.Drawing.Color.White + Me.Label1.BackColor = System.Drawing.Color.Transparent Me.Label1.Dock = System.Windows.Forms.DockStyle.Top Me.Label1.Font = New System.Drawing.Font("Noto Sans KR", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.Label1.ForeColor = System.Drawing.Color.Gray @@ -123,18 +125,18 @@ Partial Class EveryTimeBrowserNew Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi Me.BackColor = System.Drawing.Color.Gray Me.ClientSize = New System.Drawing.Size(504, 686) - Me.Controls.Add(Me.Panel1) + Me.Controls.Add(Me.TopPanel) Me.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None Me.Icon = Global.uTable.My.Resources.Resources.ptable_icon - Me.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.Margin = New System.Windows.Forms.Padding(4) Me.Name = "EveryTimeBrowserNew" Me.Padding = New System.Windows.Forms.Padding(1) Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent Me.Text = "에브리타임 시간표 불러오기" Me.TopMost = True Me.TransparencyKey = System.Drawing.Color.Fuchsia - Me.Panel1.ResumeLayout(False) + Me.TopPanel.ResumeLayout(False) CType(Me.WebView21, System.ComponentModel.ISupportInitialize).EndInit() Me.Panel2.ResumeLayout(False) CType(Me.CloseBT, System.ComponentModel.ISupportInitialize).EndInit() @@ -142,7 +144,7 @@ Partial Class EveryTimeBrowserNew End Sub Friend WithEvents TableChecker As Timer - Friend WithEvents Panel1 As Panel + Friend WithEvents TopPanel As Panel Friend WithEvents Label1 As Label Friend WithEvents CloseBT As PictureBox Friend WithEvents WebView21 As Microsoft.Web.WebView2.WinForms.WebView2 diff --git a/utable/Forms/EveryTimeBrowserNew.vb b/utable/Forms/EveryTimeBrowserNew.vb index b179520..aa31d31 100644 --- a/utable/Forms/EveryTimeBrowserNew.vb +++ b/utable/Forms/EveryTimeBrowserNew.vb @@ -85,7 +85,12 @@ Public Class EveryTimeBrowserNew WebView21.Source = New Uri(targetUrl) trialCount = 0 - Refresh() + ' 호스트 OS의 컬러 테마가 다크 -> 브라우저 내부도 다크이므로 검은 TopPanel 배경 설정 + If IsWindowsDarkMode() Then + TopPanel.BackColor = ColorTranslator.FromHtml("#111111") + CloseBT.Image = My.Resources.closeicon_w + Label1.ForeColor = ThemeColors.Dark.Text + End If End Sub Private Sub WebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs) Handles WebView21.NavigationCompleted diff --git a/utable/Forms/EverytimeSemesterSelector.vb b/utable/Forms/EverytimeSemesterSelector.vb index 557b50c..3f0b712 100644 --- a/utable/Forms/EverytimeSemesterSelector.vb +++ b/utable/Forms/EverytimeSemesterSelector.vb @@ -96,8 +96,32 @@ Public Class EverytimeSemesterSelector Select Case colorMode Case "Dark" CloseBT.Image = My.Resources.closeicon_w + + ' 옵션 컨트롤 내에 있는 모든 체크박스, 라디오버튼에 다크 모드 테마 적용 + For Each chk As CheckBox In GetAll(Me, GetType(CheckBox)) + SetWindowTheme(chk.Handle, "DarkMode_Explorer", Nothing) + Next + + For Each rdo As RadioButton In GetAll(Me, GetType(RadioButton)) + SetWindowTheme(rdo.Handle, "DarkMode_Explorer", Nothing) + Next + + SetWindowTheme(AgreementRTB.Handle, "DarkMode_Explorer", Nothing) + AgreementRTB.BackColor = Color.FromArgb(30, 30, 30) + Case Else CloseBT.Image = My.Resources.closeicon_b + + ' 옵션 컨트롤 내에 있는 모든 체크박스, 라디오버튼에 다크 모드 테마 적용 + For Each chk As CheckBox In GetAll(Me, GetType(CheckBox)) + SetWindowTheme(chk.Handle, "Explorer", Nothing) + Next + + For Each rdo As RadioButton In GetAll(Me, GetType(RadioButton)) + SetWindowTheme(rdo.Handle, "Explorer", Nothing) + Next + + SetWindowTheme(AgreementRTB.Handle, "Explorer", Nothing) End Select End Sub diff --git a/utable/Forms/OptionForm.Designer.vb b/utable/Forms/OptionForm.Designer.vb index c2fc5d4..9ccd709 100644 --- a/utable/Forms/OptionForm.Designer.vb +++ b/utable/Forms/OptionForm.Designer.vb @@ -27,40 +27,21 @@ Partial Class OptionForm Me.TitleLabel = New System.Windows.Forms.Label() Me.CloseBT = New System.Windows.Forms.PictureBox() Me.MainPanel = New System.Windows.Forms.Panel() - Me.TapPage_Notification = New System.Windows.Forms.Panel() - Me.TodaysCourseNotifyChk = New System.Windows.Forms.CheckBox() - Me.CourseNotifyPanel = New System.Windows.Forms.Panel() - Me.NotificationSoundPlayBT = New System.Windows.Forms.Button() - Me.NotificationSoundFileOpenBT = New System.Windows.Forms.Button() - Me.NotificationSoundLocationTB = New System.Windows.Forms.TextBox() - Me.NotifySoundChk = New System.Windows.Forms.CheckBox() - Me.Label22 = New System.Windows.Forms.Label() - Me.Label19 = New System.Windows.Forms.Label() - Me.Label18 = New System.Windows.Forms.Label() - Me.Label20 = New System.Windows.Forms.Label() - Me.Notify_5min_Chk = New System.Windows.Forms.CheckBox() - Me.Notify_15min_Chk = New System.Windows.Forms.CheckBox() - Me.Notify_30min_Chk = New System.Windows.Forms.CheckBox() - Me.Label21 = New System.Windows.Forms.Label() - Me.CourseNotifyChk = New System.Windows.Forms.CheckBox() - Me.TabPage_Data = New System.Windows.Forms.Panel() - Me.Panel6 = New System.Windows.Forms.Panel() - Me.SettingSaveRbt = New System.Windows.Forms.RadioButton() - Me.TableSaveRbt = New System.Windows.Forms.RadioButton() - Me.ImportDataBT = New System.Windows.Forms.Button() - Me.SaveToFileBT = New System.Windows.Forms.Button() - Me.CopyToClipboardBT = New System.Windows.Forms.Button() - Me.Label17 = New System.Windows.Forms.Label() - Me.Label16 = New System.Windows.Forms.Label() - Me.CustomDirPanel = New System.Windows.Forms.Panel() - Me.CheckAndApplyDirSettingBT = New System.Windows.Forms.Button() - Me.Label15 = New System.Windows.Forms.Label() - Me.Label14 = New System.Windows.Forms.Label() - Me.SaveNameTB = New System.Windows.Forms.TextBox() - Me.FolderBrowBT = New System.Windows.Forms.Button() - Me.SaveDirectoryTB = New System.Windows.Forms.TextBox() - Me.CustomSaveDirChk = New System.Windows.Forms.CheckBox() - Me.Label13 = New System.Windows.Forms.Label() + Me.TabPage_Basic = New System.Windows.Forms.Panel() + Me.AlwaysHideToTrayChk = New System.Windows.Forms.CheckBox() + Me.TopMostChk = New System.Windows.Forms.CheckBox() + Me.HideToTrayChk = New System.Windows.Forms.CheckBox() + Me.Panel1 = New System.Windows.Forms.Panel() + Me.D_ThemeRbt = New System.Windows.Forms.RadioButton() + Me.W_ThemeRbt = New System.Windows.Forms.RadioButton() + Me.Label3 = New System.Windows.Forms.Label() + Me.FadeEffectChk = New System.Windows.Forms.CheckBox() + Me.MinStartChk = New System.Windows.Forms.CheckBox() + Me.Label7 = New System.Windows.Forms.Label() + Me.SnapToEdgeChk = New System.Windows.Forms.CheckBox() + Me.Label2 = New System.Windows.Forms.Label() + Me.Label1 = New System.Windows.Forms.Label() + Me.StartupChk = New System.Windows.Forms.CheckBox() Me.TabPage_Table = New System.Windows.Forms.Panel() Me.FixStartTimePicker = New System.Windows.Forms.DateTimePicker() Me.FixStartTimeChk = New System.Windows.Forms.CheckBox() @@ -84,26 +65,40 @@ Partial Class OptionForm Me.AlwaysExpandChk = New System.Windows.Forms.CheckBox() Me.Label4 = New System.Windows.Forms.Label() Me.ExpandCellChk = New System.Windows.Forms.CheckBox() - Me.TabPage_Basic = New System.Windows.Forms.Panel() - Me.AlwaysHideToTrayChk = New System.Windows.Forms.CheckBox() - Me.TopMostChk = New System.Windows.Forms.CheckBox() - Me.HideToTrayChk = New System.Windows.Forms.CheckBox() - Me.Panel1 = New System.Windows.Forms.Panel() - Me.D_ThemeRbt = New System.Windows.Forms.RadioButton() - Me.W_ThemeRbt = New System.Windows.Forms.RadioButton() - Me.Label3 = New System.Windows.Forms.Label() - Me.FadeEffectChk = New System.Windows.Forms.CheckBox() - Me.MinStartChk = New System.Windows.Forms.CheckBox() - Me.Label7 = New System.Windows.Forms.Label() - Me.SnapToEdgeChk = New System.Windows.Forms.CheckBox() - Me.Label2 = New System.Windows.Forms.Label() - Me.Label1 = New System.Windows.Forms.Label() - Me.StartupChk = New System.Windows.Forms.CheckBox() - Me.TabPage_Info = New System.Windows.Forms.Panel() - Me.RichTextBox1 = New System.Windows.Forms.RichTextBox() - Me.VersionLabel = New System.Windows.Forms.Label() - Me.FeedbackLabel = New System.Windows.Forms.LinkLabel() - Me.BannerPictureBox = New System.Windows.Forms.PictureBox() + Me.TabPage_Data = New System.Windows.Forms.Panel() + Me.Panel6 = New System.Windows.Forms.Panel() + Me.SettingSaveRbt = New System.Windows.Forms.RadioButton() + Me.TableSaveRbt = New System.Windows.Forms.RadioButton() + Me.ImportDataBT = New System.Windows.Forms.Button() + Me.SaveToFileBT = New System.Windows.Forms.Button() + Me.CopyToClipboardBT = New System.Windows.Forms.Button() + Me.Label17 = New System.Windows.Forms.Label() + Me.Label16 = New System.Windows.Forms.Label() + Me.CustomDirPanel = New System.Windows.Forms.Panel() + Me.CheckAndApplyDirSettingBT = New System.Windows.Forms.Button() + Me.Label15 = New System.Windows.Forms.Label() + Me.Label14 = New System.Windows.Forms.Label() + Me.SaveNameTB = New System.Windows.Forms.TextBox() + Me.FolderBrowBT = New System.Windows.Forms.Button() + Me.SaveDirectoryTB = New System.Windows.Forms.TextBox() + Me.CustomSaveDirChk = New System.Windows.Forms.CheckBox() + Me.Label13 = New System.Windows.Forms.Label() + Me.TapPage_Notification = New System.Windows.Forms.Panel() + Me.TodaysCourseNotifyChk = New System.Windows.Forms.CheckBox() + Me.CourseNotifyPanel = New System.Windows.Forms.Panel() + Me.NotificationSoundPlayBT = New System.Windows.Forms.Button() + Me.NotificationSoundFileOpenBT = New System.Windows.Forms.Button() + Me.NotificationSoundLocationTB = New System.Windows.Forms.TextBox() + Me.NotifySoundChk = New System.Windows.Forms.CheckBox() + Me.Label22 = New System.Windows.Forms.Label() + Me.Label19 = New System.Windows.Forms.Label() + Me.Label18 = New System.Windows.Forms.Label() + Me.Label20 = New System.Windows.Forms.Label() + Me.Notify_5min_Chk = New System.Windows.Forms.CheckBox() + Me.Notify_15min_Chk = New System.Windows.Forms.CheckBox() + Me.Notify_30min_Chk = New System.Windows.Forms.CheckBox() + Me.Label21 = New System.Windows.Forms.Label() + Me.CourseNotifyChk = New System.Windows.Forms.CheckBox() Me.TabPage_Update = New System.Windows.Forms.Panel() Me.Panel4 = New System.Windows.Forms.Panel() Me.WebBrowser1 = New System.Windows.Forms.WebBrowser() @@ -124,6 +119,11 @@ Partial Class OptionForm Me.Label23 = New System.Windows.Forms.Label() Me.PictureBox1 = New System.Windows.Forms.PictureBox() Me.MSStoreLinkLabel = New System.Windows.Forms.LinkLabel() + Me.TabPage_Info = New System.Windows.Forms.Panel() + Me.RichTextBox1 = New System.Windows.Forms.RichTextBox() + Me.VersionLabel = New System.Windows.Forms.Label() + Me.FeedbackLabel = New System.Windows.Forms.LinkLabel() + Me.BannerPictureBox = New System.Windows.Forms.PictureBox() Me.ShadowPanel = New System.Windows.Forms.Panel() Me.SidePanel = New System.Windows.Forms.Panel() Me.SettingMenu_Info = New uTable.SettingMenu() @@ -141,23 +141,23 @@ Partial Class OptionForm Me.TitlePanel.SuspendLayout() CType(Me.CloseBT, System.ComponentModel.ISupportInitialize).BeginInit() Me.MainPanel.SuspendLayout() - Me.TapPage_Notification.SuspendLayout() - Me.CourseNotifyPanel.SuspendLayout() + Me.TabPage_Basic.SuspendLayout() + Me.Panel1.SuspendLayout() + Me.TabPage_Table.SuspendLayout() + Me.PrevTablePanel.SuspendLayout() Me.TabPage_Data.SuspendLayout() Me.Panel6.SuspendLayout() Me.CustomDirPanel.SuspendLayout() - Me.TabPage_Table.SuspendLayout() - Me.PrevTablePanel.SuspendLayout() - Me.TabPage_Basic.SuspendLayout() - Me.Panel1.SuspendLayout() - Me.TabPage_Info.SuspendLayout() - CType(Me.BannerPictureBox, System.ComponentModel.ISupportInitialize).BeginInit() + Me.TapPage_Notification.SuspendLayout() + Me.CourseNotifyPanel.SuspendLayout() Me.TabPage_Update.SuspendLayout() Me.Panel4.SuspendLayout() Me.Panel2.SuspendLayout() Me.UpdateCtrlPanel.SuspendLayout() Me.UpdateCtrlMSStorePanel.SuspendLayout() CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).BeginInit() + Me.TabPage_Info.SuspendLayout() + CType(Me.BannerPictureBox, System.ComponentModel.ISupportInitialize).BeginInit() Me.SidePanel.SuspendLayout() Me.SuspendLayout() ' @@ -216,399 +216,183 @@ Partial Class OptionForm Me.MainPanel.Size = New System.Drawing.Size(737, 359) Me.MainPanel.TabIndex = 1 ' - 'TapPage_Notification + 'TabPage_Basic ' - Me.TapPage_Notification.Controls.Add(Me.TodaysCourseNotifyChk) - Me.TapPage_Notification.Controls.Add(Me.CourseNotifyPanel) - Me.TapPage_Notification.Controls.Add(Me.Label21) - Me.TapPage_Notification.Controls.Add(Me.CourseNotifyChk) - Me.TapPage_Notification.Dock = System.Windows.Forms.DockStyle.Fill - Me.TapPage_Notification.Location = New System.Drawing.Point(185, 12) - Me.TapPage_Notification.Margin = New System.Windows.Forms.Padding(4) - Me.TapPage_Notification.Name = "TapPage_Notification" - Me.TapPage_Notification.Size = New System.Drawing.Size(552, 347) - Me.TapPage_Notification.TabIndex = 12 + Me.TabPage_Basic.Controls.Add(Me.AlwaysHideToTrayChk) + Me.TabPage_Basic.Controls.Add(Me.TopMostChk) + Me.TabPage_Basic.Controls.Add(Me.HideToTrayChk) + Me.TabPage_Basic.Controls.Add(Me.Panel1) + Me.TabPage_Basic.Controls.Add(Me.Label3) + Me.TabPage_Basic.Controls.Add(Me.FadeEffectChk) + Me.TabPage_Basic.Controls.Add(Me.MinStartChk) + Me.TabPage_Basic.Controls.Add(Me.Label7) + Me.TabPage_Basic.Controls.Add(Me.SnapToEdgeChk) + Me.TabPage_Basic.Controls.Add(Me.Label2) + Me.TabPage_Basic.Controls.Add(Me.Label1) + Me.TabPage_Basic.Controls.Add(Me.StartupChk) + Me.TabPage_Basic.Dock = System.Windows.Forms.DockStyle.Fill + Me.TabPage_Basic.Location = New System.Drawing.Point(185, 12) + Me.TabPage_Basic.Margin = New System.Windows.Forms.Padding(4) + Me.TabPage_Basic.Name = "TabPage_Basic" + Me.TabPage_Basic.Size = New System.Drawing.Size(552, 347) + Me.TabPage_Basic.TabIndex = 3 ' - 'TodaysCourseNotifyChk + 'AlwaysHideToTrayChk ' - Me.TodaysCourseNotifyChk.AutoSize = True - Me.TodaysCourseNotifyChk.Location = New System.Drawing.Point(26, 257) - Me.TodaysCourseNotifyChk.Margin = New System.Windows.Forms.Padding(4) - Me.TodaysCourseNotifyChk.Name = "TodaysCourseNotifyChk" - Me.TodaysCourseNotifyChk.Size = New System.Drawing.Size(290, 25) - Me.TodaysCourseNotifyChk.TabIndex = 7 - Me.TodaysCourseNotifyChk.Text = "프로그램 시작시 오늘의 수업 알림 활성화" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) - Me.TodaysCourseNotifyChk.UseVisualStyleBackColor = True + Me.AlwaysHideToTrayChk.AutoCheck = False + Me.AlwaysHideToTrayChk.AutoSize = True + Me.AlwaysHideToTrayChk.Location = New System.Drawing.Point(29, 288) + Me.AlwaysHideToTrayChk.Margin = New System.Windows.Forms.Padding(4) + Me.AlwaysHideToTrayChk.Name = "AlwaysHideToTrayChk" + Me.AlwaysHideToTrayChk.Size = New System.Drawing.Size(212, 25) + Me.AlwaysHideToTrayChk.TabIndex = 14 + Me.AlwaysHideToTrayChk.Text = "항상 작업 표시줄에서 숨기기" + Me.AlwaysHideToTrayChk.UseVisualStyleBackColor = True ' - 'CourseNotifyPanel + 'TopMostChk ' - Me.CourseNotifyPanel.Controls.Add(Me.NotificationSoundPlayBT) - Me.CourseNotifyPanel.Controls.Add(Me.NotificationSoundFileOpenBT) - Me.CourseNotifyPanel.Controls.Add(Me.NotificationSoundLocationTB) - Me.CourseNotifyPanel.Controls.Add(Me.NotifySoundChk) - Me.CourseNotifyPanel.Controls.Add(Me.Label22) - Me.CourseNotifyPanel.Controls.Add(Me.Label19) - Me.CourseNotifyPanel.Controls.Add(Me.Label18) - Me.CourseNotifyPanel.Controls.Add(Me.Label20) - Me.CourseNotifyPanel.Controls.Add(Me.Notify_5min_Chk) - Me.CourseNotifyPanel.Controls.Add(Me.Notify_15min_Chk) - Me.CourseNotifyPanel.Controls.Add(Me.Notify_30min_Chk) - Me.CourseNotifyPanel.Location = New System.Drawing.Point(16, 82) - Me.CourseNotifyPanel.Margin = New System.Windows.Forms.Padding(4) - Me.CourseNotifyPanel.Name = "CourseNotifyPanel" - Me.CourseNotifyPanel.Size = New System.Drawing.Size(519, 165) - Me.CourseNotifyPanel.TabIndex = 6 + Me.TopMostChk.AutoSize = True + Me.TopMostChk.Location = New System.Drawing.Point(324, 204) + Me.TopMostChk.Margin = New System.Windows.Forms.Padding(4) + Me.TopMostChk.Name = "TopMostChk" + Me.TopMostChk.Size = New System.Drawing.Size(124, 25) + Me.TopMostChk.TabIndex = 13 + Me.TopMostChk.Text = "항상 위에 표시" + Me.TopMostChk.UseVisualStyleBackColor = True ' - 'NotificationSoundPlayBT + 'HideToTrayChk ' - Me.NotificationSoundPlayBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat - Me.NotificationSoundPlayBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.NotificationSoundPlayBT.Location = New System.Drawing.Point(470, 86) - Me.NotificationSoundPlayBT.Margin = New System.Windows.Forms.Padding(4) - Me.NotificationSoundPlayBT.Name = "NotificationSoundPlayBT" - Me.NotificationSoundPlayBT.Size = New System.Drawing.Size(36, 29) - Me.NotificationSoundPlayBT.TabIndex = 20 - Me.NotificationSoundPlayBT.Text = "▶" - Me.NotificationSoundPlayBT.UseVisualStyleBackColor = True + Me.HideToTrayChk.AutoSize = True + Me.HideToTrayChk.Location = New System.Drawing.Point(29, 260) + Me.HideToTrayChk.Margin = New System.Windows.Forms.Padding(4) + Me.HideToTrayChk.Name = "HideToTrayChk" + Me.HideToTrayChk.Size = New System.Drawing.Size(244, 25) + Me.HideToTrayChk.TabIndex = 12 + Me.HideToTrayChk.Text = "최소화 시 작업 표시줄에서 숨기기" + Me.HideToTrayChk.UseVisualStyleBackColor = True ' - 'NotificationSoundFileOpenBT + 'Panel1 ' - Me.NotificationSoundFileOpenBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat - Me.NotificationSoundFileOpenBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.NotificationSoundFileOpenBT.Location = New System.Drawing.Point(429, 86) - Me.NotificationSoundFileOpenBT.Margin = New System.Windows.Forms.Padding(4) - Me.NotificationSoundFileOpenBT.Name = "NotificationSoundFileOpenBT" - Me.NotificationSoundFileOpenBT.Size = New System.Drawing.Size(36, 29) - Me.NotificationSoundFileOpenBT.TabIndex = 19 - Me.NotificationSoundFileOpenBT.Text = "..." - Me.NotificationSoundFileOpenBT.UseVisualStyleBackColor = True + Me.Panel1.Controls.Add(Me.D_ThemeRbt) + Me.Panel1.Controls.Add(Me.W_ThemeRbt) + Me.Panel1.Location = New System.Drawing.Point(351, 40) + Me.Panel1.Margin = New System.Windows.Forms.Padding(4) + Me.Panel1.Name = "Panel1" + Me.Panel1.Size = New System.Drawing.Size(159, 66) + Me.Panel1.TabIndex = 11 ' - 'NotificationSoundLocationTB + 'D_ThemeRbt ' - Me.NotificationSoundLocationTB.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.NotificationSoundLocationTB.Location = New System.Drawing.Point(171, 86) - Me.NotificationSoundLocationTB.Margin = New System.Windows.Forms.Padding(4) - Me.NotificationSoundLocationTB.Name = "NotificationSoundLocationTB" - Me.NotificationSoundLocationTB.Size = New System.Drawing.Size(250, 27) - Me.NotificationSoundLocationTB.TabIndex = 9 + Me.D_ThemeRbt.AutoSize = True + Me.D_ThemeRbt.Location = New System.Drawing.Point(4, 35) + Me.D_ThemeRbt.Margin = New System.Windows.Forms.Padding(4) + Me.D_ThemeRbt.Name = "D_ThemeRbt" + Me.D_ThemeRbt.Size = New System.Drawing.Size(59, 25) + Me.D_ThemeRbt.TabIndex = 1 + Me.D_ThemeRbt.TabStop = True + Me.D_ThemeRbt.Text = "다크" + Me.D_ThemeRbt.UseVisualStyleBackColor = True ' - 'NotifySoundChk + 'W_ThemeRbt ' - Me.NotifySoundChk.AutoSize = True - Me.NotifySoundChk.Location = New System.Drawing.Point(101, 89) - Me.NotifySoundChk.Margin = New System.Windows.Forms.Padding(4) - Me.NotifySoundChk.Name = "NotifySoundChk" - Me.NotifySoundChk.Size = New System.Drawing.Size(60, 25) - Me.NotifySoundChk.TabIndex = 8 - Me.NotifySoundChk.Text = "사용" - Me.NotifySoundChk.UseVisualStyleBackColor = True - ' - 'Label22 - ' - Me.Label22.AutoSize = True - Me.Label22.Location = New System.Drawing.Point(12, 90) - Me.Label22.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label22.Name = "Label22" - Me.Label22.Size = New System.Drawing.Size(70, 21) - Me.Label22.TabIndex = 7 - Me.Label22.Text = "알림 소리" - ' - 'Label19 - ' - Me.Label19.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label19.ForeColor = System.Drawing.Color.Gray - Me.Label19.Location = New System.Drawing.Point(10, 122) - Me.Label19.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label19.Name = "Label19" - Me.Label19.Size = New System.Drawing.Size(486, 39) - Me.Label19.TabIndex = 6 - Me.Label19.Text = "Tip: 강의 메모에 ""(알림 무시)"" 또는 ""(알림X)"" 라고 입력하면" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "해당 과목의 알림이 무시됩니다." - ' - 'Label18 - ' - Me.Label18.AutoSize = True - Me.Label18.Location = New System.Drawing.Point(12, 14) - Me.Label18.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label18.Name = "Label18" - Me.Label18.Size = New System.Drawing.Size(70, 21) - Me.Label18.TabIndex = 3 - Me.Label18.Text = "알림 시간" - ' - 'Label20 - ' - Me.Label20.Font = New System.Drawing.Font("Noto Sans KR", 8.0!) - Me.Label20.ForeColor = System.Drawing.Color.Gray - Me.Label20.Location = New System.Drawing.Point(12, 40) - Me.Label20.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label20.Name = "Label20" - Me.Label20.Size = New System.Drawing.Size(485, 38) - Me.Label20.TabIndex = 5 - Me.Label20.Text = "아무것도 체크하지 않을 시 수업 시작 시간에만," & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "중복 체크시 해당 시간 모두 알림이 옵니다." - ' - 'Notify_5min_Chk - ' - Me.Notify_5min_Chk.AutoSize = True - Me.Notify_5min_Chk.Location = New System.Drawing.Point(279, 12) - Me.Notify_5min_Chk.Margin = New System.Windows.Forms.Padding(4) - Me.Notify_5min_Chk.Name = "Notify_5min_Chk" - Me.Notify_5min_Chk.Size = New System.Drawing.Size(72, 25) - Me.Notify_5min_Chk.TabIndex = 2 - Me.Notify_5min_Chk.Text = "5분 전" - Me.Notify_5min_Chk.UseVisualStyleBackColor = True - ' - 'Notify_15min_Chk - ' - Me.Notify_15min_Chk.AutoSize = True - Me.Notify_15min_Chk.Location = New System.Drawing.Point(190, 12) - Me.Notify_15min_Chk.Margin = New System.Windows.Forms.Padding(4) - Me.Notify_15min_Chk.Name = "Notify_15min_Chk" - Me.Notify_15min_Chk.Size = New System.Drawing.Size(80, 25) - Me.Notify_15min_Chk.TabIndex = 1 - Me.Notify_15min_Chk.Text = "15분 전" - Me.Notify_15min_Chk.UseVisualStyleBackColor = True - ' - 'Notify_30min_Chk - ' - Me.Notify_30min_Chk.AutoSize = True - Me.Notify_30min_Chk.Location = New System.Drawing.Point(101, 12) - Me.Notify_30min_Chk.Margin = New System.Windows.Forms.Padding(4) - Me.Notify_30min_Chk.Name = "Notify_30min_Chk" - Me.Notify_30min_Chk.Size = New System.Drawing.Size(80, 25) - Me.Notify_30min_Chk.TabIndex = 0 - Me.Notify_30min_Chk.Text = "30분 전" - Me.Notify_30min_Chk.UseVisualStyleBackColor = True - ' - 'Label21 - ' - Me.Label21.AutoSize = True - Me.Label21.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label21.Location = New System.Drawing.Point(21, 15) - Me.Label21.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label21.Name = "Label21" - Me.Label21.Size = New System.Drawing.Size(85, 27) - Me.Label21.TabIndex = 4 - Me.Label21.Text = "알림 설정" - ' - 'CourseNotifyChk - ' - Me.CourseNotifyChk.AutoSize = True - Me.CourseNotifyChk.Location = New System.Drawing.Point(28, 58) - Me.CourseNotifyChk.Margin = New System.Windows.Forms.Padding(4) - Me.CourseNotifyChk.Name = "CourseNotifyChk" - Me.CourseNotifyChk.Size = New System.Drawing.Size(138, 25) - Me.CourseNotifyChk.TabIndex = 3 - Me.CourseNotifyChk.Text = "강의 알림 활성화" - Me.CourseNotifyChk.TextAlign = System.Drawing.ContentAlignment.MiddleCenter - Me.CourseNotifyChk.UseVisualStyleBackColor = True - ' - 'TabPage_Data - ' - Me.TabPage_Data.Controls.Add(Me.Panel6) - Me.TabPage_Data.Controls.Add(Me.Label17) - Me.TabPage_Data.Controls.Add(Me.Label16) - Me.TabPage_Data.Controls.Add(Me.CustomDirPanel) - Me.TabPage_Data.Controls.Add(Me.CustomSaveDirChk) - Me.TabPage_Data.Controls.Add(Me.Label13) - Me.TabPage_Data.Dock = System.Windows.Forms.DockStyle.Fill - Me.TabPage_Data.Location = New System.Drawing.Point(185, 12) - Me.TabPage_Data.Margin = New System.Windows.Forms.Padding(4) - Me.TabPage_Data.Name = "TabPage_Data" - Me.TabPage_Data.Size = New System.Drawing.Size(552, 347) - Me.TabPage_Data.TabIndex = 15 - ' - 'Panel6 - ' - Me.Panel6.Controls.Add(Me.SettingSaveRbt) - Me.Panel6.Controls.Add(Me.TableSaveRbt) - Me.Panel6.Controls.Add(Me.ImportDataBT) - Me.Panel6.Controls.Add(Me.SaveToFileBT) - Me.Panel6.Controls.Add(Me.CopyToClipboardBT) - Me.Panel6.Location = New System.Drawing.Point(16, 239) - Me.Panel6.Margin = New System.Windows.Forms.Padding(4) - Me.Panel6.Name = "Panel6" - Me.Panel6.Size = New System.Drawing.Size(506, 81) - Me.Panel6.TabIndex = 26 - ' - 'SettingSaveRbt - ' - Me.SettingSaveRbt.AutoSize = True - Me.SettingSaveRbt.Location = New System.Drawing.Point(101, 4) - Me.SettingSaveRbt.Margin = New System.Windows.Forms.Padding(4) - Me.SettingSaveRbt.Name = "SettingSaveRbt" - Me.SettingSaveRbt.Size = New System.Drawing.Size(119, 25) - Me.SettingSaveRbt.TabIndex = 27 - Me.SettingSaveRbt.Text = "프로그램 설정" - Me.SettingSaveRbt.UseVisualStyleBackColor = True - ' - 'TableSaveRbt - ' - Me.TableSaveRbt.AutoSize = True - Me.TableSaveRbt.Checked = True - Me.TableSaveRbt.Location = New System.Drawing.Point(12, 4) - Me.TableSaveRbt.Margin = New System.Windows.Forms.Padding(4) - Me.TableSaveRbt.Name = "TableSaveRbt" - Me.TableSaveRbt.Size = New System.Drawing.Size(73, 25) - Me.TableSaveRbt.TabIndex = 26 - Me.TableSaveRbt.TabStop = True - Me.TableSaveRbt.Text = "시간표" - Me.TableSaveRbt.UseVisualStyleBackColor = True - ' - 'ImportDataBT - ' - Me.ImportDataBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat - Me.ImportDataBT.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.ImportDataBT.Location = New System.Drawing.Point(279, 32) - Me.ImportDataBT.Margin = New System.Windows.Forms.Padding(4) - Me.ImportDataBT.Name = "ImportDataBT" - Me.ImportDataBT.Size = New System.Drawing.Size(205, 31) - Me.ImportDataBT.TabIndex = 25 - Me.ImportDataBT.Text = "클립보드/파일 불러오기, 적용" - Me.ImportDataBT.UseVisualStyleBackColor = True - ' - 'SaveToFileBT - ' - Me.SaveToFileBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat - Me.SaveToFileBT.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.SaveToFileBT.Location = New System.Drawing.Point(12, 32) - Me.SaveToFileBT.Margin = New System.Windows.Forms.Padding(4) - Me.SaveToFileBT.Name = "SaveToFileBT" - Me.SaveToFileBT.Size = New System.Drawing.Size(128, 31) - Me.SaveToFileBT.TabIndex = 23 - Me.SaveToFileBT.Text = "파일로 저장" - Me.SaveToFileBT.UseVisualStyleBackColor = True - ' - 'CopyToClipboardBT - ' - Me.CopyToClipboardBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat - Me.CopyToClipboardBT.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.CopyToClipboardBT.Location = New System.Drawing.Point(146, 32) - Me.CopyToClipboardBT.Margin = New System.Windows.Forms.Padding(4) - Me.CopyToClipboardBT.Name = "CopyToClipboardBT" - Me.CopyToClipboardBT.Size = New System.Drawing.Size(122, 31) - Me.CopyToClipboardBT.TabIndex = 24 - Me.CopyToClipboardBT.Text = "클립보드에 복사" - Me.CopyToClipboardBT.UseVisualStyleBackColor = True - ' - 'Label17 - ' - Me.Label17.AutoSize = True - Me.Label17.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label17.Location = New System.Drawing.Point(21, 210) - Me.Label17.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label17.Name = "Label17" - Me.Label17.Size = New System.Drawing.Size(157, 27) - Me.Label17.TabIndex = 22 - Me.Label17.Text = "내보내기, 불러오기" - ' - 'Label16 - ' - Me.Label16.Font = New System.Drawing.Font("Noto Sans KR", 8.0!) - Me.Label16.ForeColor = System.Drawing.Color.Gray - Me.Label16.Location = New System.Drawing.Point(22, 145) - Me.Label16.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label16.Name = "Label16" - Me.Label16.Size = New System.Drawing.Size(504, 58) - Me.Label16.TabIndex = 21 - Me.Label16.Text = "경고: uTable 실행 경로에 설정 파일(settings.ini)이 없는 경우 설정값이 초기화되어 시간표를 불러올 수 없으며, 다른 컴퓨터 환경" & - "에서 불러오는 데 실패할 수 있습니다." - ' - 'CustomDirPanel - ' - Me.CustomDirPanel.Controls.Add(Me.CheckAndApplyDirSettingBT) - Me.CustomDirPanel.Controls.Add(Me.Label15) - Me.CustomDirPanel.Controls.Add(Me.Label14) - Me.CustomDirPanel.Controls.Add(Me.SaveNameTB) - Me.CustomDirPanel.Controls.Add(Me.FolderBrowBT) - Me.CustomDirPanel.Controls.Add(Me.SaveDirectoryTB) - Me.CustomDirPanel.Location = New System.Drawing.Point(16, 79) - Me.CustomDirPanel.Margin = New System.Windows.Forms.Padding(4) - Me.CustomDirPanel.Name = "CustomDirPanel" - Me.CustomDirPanel.Size = New System.Drawing.Size(506, 62) - Me.CustomDirPanel.TabIndex = 20 + Me.W_ThemeRbt.AutoSize = True + Me.W_ThemeRbt.Location = New System.Drawing.Point(4, 9) + Me.W_ThemeRbt.Margin = New System.Windows.Forms.Padding(4) + Me.W_ThemeRbt.Name = "W_ThemeRbt" + Me.W_ThemeRbt.Size = New System.Drawing.Size(115, 25) + Me.W_ThemeRbt.TabIndex = 0 + Me.W_ThemeRbt.TabStop = True + Me.W_ThemeRbt.Text = "화이트 (기본)" + Me.W_ThemeRbt.UseVisualStyleBackColor = True ' - 'CheckAndApplyDirSettingBT + 'Label3 ' - Me.CheckAndApplyDirSettingBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat - Me.CheckAndApplyDirSettingBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.CheckAndApplyDirSettingBT.Location = New System.Drawing.Point(385, 4) - Me.CheckAndApplyDirSettingBT.Margin = New System.Windows.Forms.Padding(4) - Me.CheckAndApplyDirSettingBT.Name = "CheckAndApplyDirSettingBT" - Me.CheckAndApplyDirSettingBT.Size = New System.Drawing.Size(115, 51) - Me.CheckAndApplyDirSettingBT.TabIndex = 22 - Me.CheckAndApplyDirSettingBT.Text = "설정 적용" - Me.CheckAndApplyDirSettingBT.UseVisualStyleBackColor = True + Me.Label3.AutoSize = True + Me.Label3.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label3.Location = New System.Drawing.Point(346, 15) + Me.Label3.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label3.Name = "Label3" + Me.Label3.Size = New System.Drawing.Size(85, 27) + Me.Label3.TabIndex = 10 + Me.Label3.Text = "테마 선택" ' - 'Label15 + 'FadeEffectChk ' - Me.Label15.AutoSize = True - Me.Label15.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label15.Location = New System.Drawing.Point(8, 6) - Me.Label15.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label15.Name = "Label15" - Me.Label15.Size = New System.Drawing.Size(155, 20) - Me.Label15.TabIndex = 21 - Me.Label15.Text = "저장 위치 (빈 칸: 기본값)" + Me.FadeEffectChk.AutoSize = True + Me.FadeEffectChk.Location = New System.Drawing.Point(29, 232) + Me.FadeEffectChk.Margin = New System.Windows.Forms.Padding(4) + Me.FadeEffectChk.Name = "FadeEffectChk" + Me.FadeEffectChk.Size = New System.Drawing.Size(232, 25) + Me.FadeEffectChk.TabIndex = 9 + Me.FadeEffectChk.Text = "페이드 인/아웃 애니메이션 적용" + Me.FadeEffectChk.UseVisualStyleBackColor = True ' - 'Label14 + 'MinStartChk ' - Me.Label14.AutoSize = True - Me.Label14.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label14.Location = New System.Drawing.Point(234, 6) - Me.Label14.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label14.Name = "Label14" - Me.Label14.Size = New System.Drawing.Size(48, 20) - Me.Label14.TabIndex = 20 - Me.Label14.Text = "파일명" + Me.MinStartChk.AutoSize = True + Me.MinStartChk.Location = New System.Drawing.Point(29, 78) + Me.MinStartChk.Margin = New System.Windows.Forms.Padding(4) + Me.MinStartChk.Name = "MinStartChk" + Me.MinStartChk.Size = New System.Drawing.Size(170, 25) + Me.MinStartChk.TabIndex = 8 + Me.MinStartChk.Text = "최소화 된 상태로 시작" + Me.MinStartChk.UseVisualStyleBackColor = True ' - 'SaveNameTB + 'Label7 ' - Me.SaveNameTB.Location = New System.Drawing.Point(238, 26) - Me.SaveNameTB.Margin = New System.Windows.Forms.Padding(4) - Me.SaveNameTB.Name = "SaveNameTB" - Me.SaveNameTB.Size = New System.Drawing.Size(139, 29) - Me.SaveNameTB.TabIndex = 19 + Me.Label7.AutoSize = True + Me.Label7.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label7.Location = New System.Drawing.Point(21, 170) + Me.Label7.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label7.Name = "Label7" + Me.Label7.Size = New System.Drawing.Size(85, 27) + Me.Label7.TabIndex = 7 + Me.Label7.Text = "모양 설정" ' - 'FolderBrowBT + 'SnapToEdgeChk ' - Me.FolderBrowBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat - Me.FolderBrowBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.FolderBrowBT.Location = New System.Drawing.Point(184, 26) - Me.FolderBrowBT.Margin = New System.Windows.Forms.Padding(4) - Me.FolderBrowBT.Name = "FolderBrowBT" - Me.FolderBrowBT.Size = New System.Drawing.Size(41, 29) - Me.FolderBrowBT.TabIndex = 18 - Me.FolderBrowBT.Text = "..." - Me.FolderBrowBT.UseVisualStyleBackColor = True + Me.SnapToEdgeChk.AutoSize = True + Me.SnapToEdgeChk.Location = New System.Drawing.Point(29, 205) + Me.SnapToEdgeChk.Margin = New System.Windows.Forms.Padding(4) + Me.SnapToEdgeChk.Name = "SnapToEdgeChk" + Me.SnapToEdgeChk.Size = New System.Drawing.Size(244, 25) + Me.SnapToEdgeChk.TabIndex = 6 + Me.SnapToEdgeChk.Text = "화면 모서리에 자동으로 붙게 하기" + Me.SnapToEdgeChk.UseVisualStyleBackColor = True ' - 'SaveDirectoryTB + 'Label2 ' - Me.SaveDirectoryTB.Location = New System.Drawing.Point(11, 26) - Me.SaveDirectoryTB.Margin = New System.Windows.Forms.Padding(4) - Me.SaveDirectoryTB.Name = "SaveDirectoryTB" - Me.SaveDirectoryTB.Size = New System.Drawing.Size(164, 29) - Me.SaveDirectoryTB.TabIndex = 14 + Me.Label2.AutoSize = True + Me.Label2.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label2.ForeColor = System.Drawing.Color.Gray + Me.Label2.Location = New System.Drawing.Point(25, 111) + Me.Label2.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label2.Name = "Label2" + Me.Label2.Size = New System.Drawing.Size(277, 42) + Me.Label2.TabIndex = 5 + Me.Label2.Text = "※ 실행 파일(.exe)의 위치가 변경되는 경우" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & " 다시 설정하셔야 합니다." ' - 'CustomSaveDirChk + 'Label1 ' - Me.CustomSaveDirChk.AutoSize = True - Me.CustomSaveDirChk.Location = New System.Drawing.Point(29, 50) - Me.CustomSaveDirChk.Margin = New System.Windows.Forms.Padding(4) - Me.CustomSaveDirChk.Name = "CustomSaveDirChk" - Me.CustomSaveDirChk.Size = New System.Drawing.Size(251, 25) - Me.CustomSaveDirChk.TabIndex = 13 - Me.CustomSaveDirChk.Text = "사용자 지정 저장 위치, 파일명 사용" - Me.CustomSaveDirChk.UseVisualStyleBackColor = True + Me.Label1.AutoSize = True + Me.Label1.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label1.Location = New System.Drawing.Point(21, 15) + Me.Label1.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label1.Name = "Label1" + Me.Label1.Size = New System.Drawing.Size(85, 27) + Me.Label1.TabIndex = 4 + Me.Label1.Text = "시작 설정" ' - 'Label13 + 'StartupChk ' - Me.Label13.AutoSize = True - Me.Label13.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label13.Location = New System.Drawing.Point(21, 15) - Me.Label13.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label13.Name = "Label13" - Me.Label13.Size = New System.Drawing.Size(180, 27) - Me.Label13.TabIndex = 12 - Me.Label13.Text = "시간표 저장 위치 설정" + Me.StartupChk.AutoSize = True + Me.StartupChk.Location = New System.Drawing.Point(29, 50) + Me.StartupChk.Margin = New System.Windows.Forms.Padding(4) + Me.StartupChk.Name = "StartupChk" + Me.StartupChk.Size = New System.Drawing.Size(268, 25) + Me.StartupChk.TabIndex = 3 + Me.StartupChk.Text = "Windows 시작 시 같이 프로그램 실행" + Me.StartupChk.UseVisualStyleBackColor = True ' 'TabPage_Table ' @@ -874,262 +658,410 @@ Partial Class OptionForm Me.Label4.TabIndex = 10 Me.Label4.Text = "표시 설정" ' - 'ExpandCellChk + 'ExpandCellChk + ' + Me.ExpandCellChk.AutoSize = True + Me.ExpandCellChk.Location = New System.Drawing.Point(29, 50) + Me.ExpandCellChk.Margin = New System.Windows.Forms.Padding(4) + Me.ExpandCellChk.Name = "ExpandCellChk" + Me.ExpandCellChk.Size = New System.Drawing.Size(170, 25) + Me.ExpandCellChk.TabIndex = 9 + Me.ExpandCellChk.Text = "마우스 올리면 표 확장" + Me.ExpandCellChk.UseVisualStyleBackColor = True + ' + 'TabPage_Data + ' + Me.TabPage_Data.Controls.Add(Me.Panel6) + Me.TabPage_Data.Controls.Add(Me.Label17) + Me.TabPage_Data.Controls.Add(Me.Label16) + Me.TabPage_Data.Controls.Add(Me.CustomDirPanel) + Me.TabPage_Data.Controls.Add(Me.CustomSaveDirChk) + Me.TabPage_Data.Controls.Add(Me.Label13) + Me.TabPage_Data.Dock = System.Windows.Forms.DockStyle.Fill + Me.TabPage_Data.Location = New System.Drawing.Point(185, 12) + Me.TabPage_Data.Margin = New System.Windows.Forms.Padding(4) + Me.TabPage_Data.Name = "TabPage_Data" + Me.TabPage_Data.Size = New System.Drawing.Size(552, 347) + Me.TabPage_Data.TabIndex = 15 + ' + 'Panel6 + ' + Me.Panel6.Controls.Add(Me.SettingSaveRbt) + Me.Panel6.Controls.Add(Me.TableSaveRbt) + Me.Panel6.Controls.Add(Me.ImportDataBT) + Me.Panel6.Controls.Add(Me.SaveToFileBT) + Me.Panel6.Controls.Add(Me.CopyToClipboardBT) + Me.Panel6.Location = New System.Drawing.Point(16, 239) + Me.Panel6.Margin = New System.Windows.Forms.Padding(4) + Me.Panel6.Name = "Panel6" + Me.Panel6.Size = New System.Drawing.Size(506, 81) + Me.Panel6.TabIndex = 26 + ' + 'SettingSaveRbt + ' + Me.SettingSaveRbt.AutoSize = True + Me.SettingSaveRbt.Location = New System.Drawing.Point(101, 4) + Me.SettingSaveRbt.Margin = New System.Windows.Forms.Padding(4) + Me.SettingSaveRbt.Name = "SettingSaveRbt" + Me.SettingSaveRbt.Size = New System.Drawing.Size(119, 25) + Me.SettingSaveRbt.TabIndex = 27 + Me.SettingSaveRbt.Text = "프로그램 설정" + Me.SettingSaveRbt.UseVisualStyleBackColor = True + ' + 'TableSaveRbt + ' + Me.TableSaveRbt.AutoSize = True + Me.TableSaveRbt.Checked = True + Me.TableSaveRbt.Location = New System.Drawing.Point(12, 4) + Me.TableSaveRbt.Margin = New System.Windows.Forms.Padding(4) + Me.TableSaveRbt.Name = "TableSaveRbt" + Me.TableSaveRbt.Size = New System.Drawing.Size(73, 25) + Me.TableSaveRbt.TabIndex = 26 + Me.TableSaveRbt.TabStop = True + Me.TableSaveRbt.Text = "시간표" + Me.TableSaveRbt.UseVisualStyleBackColor = True + ' + 'ImportDataBT + ' + Me.ImportDataBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat + Me.ImportDataBT.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.ImportDataBT.Location = New System.Drawing.Point(279, 32) + Me.ImportDataBT.Margin = New System.Windows.Forms.Padding(4) + Me.ImportDataBT.Name = "ImportDataBT" + Me.ImportDataBT.Size = New System.Drawing.Size(205, 31) + Me.ImportDataBT.TabIndex = 25 + Me.ImportDataBT.Text = "클립보드/파일 불러오기, 적용" + Me.ImportDataBT.UseVisualStyleBackColor = True + ' + 'SaveToFileBT + ' + Me.SaveToFileBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat + Me.SaveToFileBT.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.SaveToFileBT.Location = New System.Drawing.Point(12, 32) + Me.SaveToFileBT.Margin = New System.Windows.Forms.Padding(4) + Me.SaveToFileBT.Name = "SaveToFileBT" + Me.SaveToFileBT.Size = New System.Drawing.Size(128, 31) + Me.SaveToFileBT.TabIndex = 23 + Me.SaveToFileBT.Text = "파일로 저장" + Me.SaveToFileBT.UseVisualStyleBackColor = True + ' + 'CopyToClipboardBT + ' + Me.CopyToClipboardBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat + Me.CopyToClipboardBT.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.CopyToClipboardBT.Location = New System.Drawing.Point(146, 32) + Me.CopyToClipboardBT.Margin = New System.Windows.Forms.Padding(4) + Me.CopyToClipboardBT.Name = "CopyToClipboardBT" + Me.CopyToClipboardBT.Size = New System.Drawing.Size(122, 31) + Me.CopyToClipboardBT.TabIndex = 24 + Me.CopyToClipboardBT.Text = "클립보드에 복사" + Me.CopyToClipboardBT.UseVisualStyleBackColor = True + ' + 'Label17 + ' + Me.Label17.AutoSize = True + Me.Label17.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label17.Location = New System.Drawing.Point(21, 210) + Me.Label17.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label17.Name = "Label17" + Me.Label17.Size = New System.Drawing.Size(157, 27) + Me.Label17.TabIndex = 22 + Me.Label17.Text = "내보내기, 불러오기" + ' + 'Label16 + ' + Me.Label16.Font = New System.Drawing.Font("Noto Sans KR", 8.0!) + Me.Label16.ForeColor = System.Drawing.Color.Gray + Me.Label16.Location = New System.Drawing.Point(22, 145) + Me.Label16.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label16.Name = "Label16" + Me.Label16.Size = New System.Drawing.Size(504, 58) + Me.Label16.TabIndex = 21 + Me.Label16.Text = "경고: uTable 실행 경로에 설정 파일(settings.ini)이 없는 경우 설정값이 초기화되어 시간표를 불러올 수 없으며, 다른 컴퓨터 환경" & + "에서 불러오는 데 실패할 수 있습니다." + ' + 'CustomDirPanel + ' + Me.CustomDirPanel.Controls.Add(Me.CheckAndApplyDirSettingBT) + Me.CustomDirPanel.Controls.Add(Me.Label15) + Me.CustomDirPanel.Controls.Add(Me.Label14) + Me.CustomDirPanel.Controls.Add(Me.SaveNameTB) + Me.CustomDirPanel.Controls.Add(Me.FolderBrowBT) + Me.CustomDirPanel.Controls.Add(Me.SaveDirectoryTB) + Me.CustomDirPanel.Location = New System.Drawing.Point(16, 79) + Me.CustomDirPanel.Margin = New System.Windows.Forms.Padding(4) + Me.CustomDirPanel.Name = "CustomDirPanel" + Me.CustomDirPanel.Size = New System.Drawing.Size(506, 62) + Me.CustomDirPanel.TabIndex = 20 + ' + 'CheckAndApplyDirSettingBT + ' + Me.CheckAndApplyDirSettingBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat + Me.CheckAndApplyDirSettingBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.CheckAndApplyDirSettingBT.Location = New System.Drawing.Point(385, 4) + Me.CheckAndApplyDirSettingBT.Margin = New System.Windows.Forms.Padding(4) + Me.CheckAndApplyDirSettingBT.Name = "CheckAndApplyDirSettingBT" + Me.CheckAndApplyDirSettingBT.Size = New System.Drawing.Size(115, 51) + Me.CheckAndApplyDirSettingBT.TabIndex = 22 + Me.CheckAndApplyDirSettingBT.Text = "설정 적용" + Me.CheckAndApplyDirSettingBT.UseVisualStyleBackColor = True + ' + 'Label15 + ' + Me.Label15.AutoSize = True + Me.Label15.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label15.Location = New System.Drawing.Point(8, 6) + Me.Label15.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label15.Name = "Label15" + Me.Label15.Size = New System.Drawing.Size(155, 20) + Me.Label15.TabIndex = 21 + Me.Label15.Text = "저장 위치 (빈 칸: 기본값)" + ' + 'Label14 + ' + Me.Label14.AutoSize = True + Me.Label14.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label14.Location = New System.Drawing.Point(234, 6) + Me.Label14.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label14.Name = "Label14" + Me.Label14.Size = New System.Drawing.Size(48, 20) + Me.Label14.TabIndex = 20 + Me.Label14.Text = "파일명" + ' + 'SaveNameTB ' - Me.ExpandCellChk.AutoSize = True - Me.ExpandCellChk.Location = New System.Drawing.Point(29, 50) - Me.ExpandCellChk.Margin = New System.Windows.Forms.Padding(4) - Me.ExpandCellChk.Name = "ExpandCellChk" - Me.ExpandCellChk.Size = New System.Drawing.Size(170, 25) - Me.ExpandCellChk.TabIndex = 9 - Me.ExpandCellChk.Text = "마우스 올리면 표 확장" - Me.ExpandCellChk.UseVisualStyleBackColor = True + Me.SaveNameTB.Location = New System.Drawing.Point(238, 26) + Me.SaveNameTB.Margin = New System.Windows.Forms.Padding(4) + Me.SaveNameTB.Name = "SaveNameTB" + Me.SaveNameTB.Size = New System.Drawing.Size(139, 29) + Me.SaveNameTB.TabIndex = 19 ' - 'TabPage_Basic + 'FolderBrowBT ' - Me.TabPage_Basic.Controls.Add(Me.AlwaysHideToTrayChk) - Me.TabPage_Basic.Controls.Add(Me.TopMostChk) - Me.TabPage_Basic.Controls.Add(Me.HideToTrayChk) - Me.TabPage_Basic.Controls.Add(Me.Panel1) - Me.TabPage_Basic.Controls.Add(Me.Label3) - Me.TabPage_Basic.Controls.Add(Me.FadeEffectChk) - Me.TabPage_Basic.Controls.Add(Me.MinStartChk) - Me.TabPage_Basic.Controls.Add(Me.Label7) - Me.TabPage_Basic.Controls.Add(Me.SnapToEdgeChk) - Me.TabPage_Basic.Controls.Add(Me.Label2) - Me.TabPage_Basic.Controls.Add(Me.Label1) - Me.TabPage_Basic.Controls.Add(Me.StartupChk) - Me.TabPage_Basic.Dock = System.Windows.Forms.DockStyle.Fill - Me.TabPage_Basic.Location = New System.Drawing.Point(185, 12) - Me.TabPage_Basic.Margin = New System.Windows.Forms.Padding(4) - Me.TabPage_Basic.Name = "TabPage_Basic" - Me.TabPage_Basic.Size = New System.Drawing.Size(552, 347) - Me.TabPage_Basic.TabIndex = 3 + Me.FolderBrowBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat + Me.FolderBrowBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.FolderBrowBT.Location = New System.Drawing.Point(184, 26) + Me.FolderBrowBT.Margin = New System.Windows.Forms.Padding(4) + Me.FolderBrowBT.Name = "FolderBrowBT" + Me.FolderBrowBT.Size = New System.Drawing.Size(41, 29) + Me.FolderBrowBT.TabIndex = 18 + Me.FolderBrowBT.Text = "..." + Me.FolderBrowBT.UseVisualStyleBackColor = True ' - 'AlwaysHideToTrayChk + 'SaveDirectoryTB ' - Me.AlwaysHideToTrayChk.AutoCheck = False - Me.AlwaysHideToTrayChk.AutoSize = True - Me.AlwaysHideToTrayChk.Location = New System.Drawing.Point(29, 288) - Me.AlwaysHideToTrayChk.Margin = New System.Windows.Forms.Padding(4) - Me.AlwaysHideToTrayChk.Name = "AlwaysHideToTrayChk" - Me.AlwaysHideToTrayChk.Size = New System.Drawing.Size(212, 25) - Me.AlwaysHideToTrayChk.TabIndex = 14 - Me.AlwaysHideToTrayChk.Text = "항상 작업 표시줄에서 숨기기" - Me.AlwaysHideToTrayChk.UseVisualStyleBackColor = True + Me.SaveDirectoryTB.Location = New System.Drawing.Point(11, 26) + Me.SaveDirectoryTB.Margin = New System.Windows.Forms.Padding(4) + Me.SaveDirectoryTB.Name = "SaveDirectoryTB" + Me.SaveDirectoryTB.Size = New System.Drawing.Size(164, 29) + Me.SaveDirectoryTB.TabIndex = 14 ' - 'TopMostChk + 'CustomSaveDirChk ' - Me.TopMostChk.AutoSize = True - Me.TopMostChk.Location = New System.Drawing.Point(324, 204) - Me.TopMostChk.Margin = New System.Windows.Forms.Padding(4) - Me.TopMostChk.Name = "TopMostChk" - Me.TopMostChk.Size = New System.Drawing.Size(124, 25) - Me.TopMostChk.TabIndex = 13 - Me.TopMostChk.Text = "항상 위에 표시" - Me.TopMostChk.UseVisualStyleBackColor = True + Me.CustomSaveDirChk.AutoSize = True + Me.CustomSaveDirChk.Location = New System.Drawing.Point(29, 50) + Me.CustomSaveDirChk.Margin = New System.Windows.Forms.Padding(4) + Me.CustomSaveDirChk.Name = "CustomSaveDirChk" + Me.CustomSaveDirChk.Size = New System.Drawing.Size(251, 25) + Me.CustomSaveDirChk.TabIndex = 13 + Me.CustomSaveDirChk.Text = "사용자 지정 저장 위치, 파일명 사용" + Me.CustomSaveDirChk.UseVisualStyleBackColor = True ' - 'HideToTrayChk + 'Label13 ' - Me.HideToTrayChk.AutoSize = True - Me.HideToTrayChk.Location = New System.Drawing.Point(29, 260) - Me.HideToTrayChk.Margin = New System.Windows.Forms.Padding(4) - Me.HideToTrayChk.Name = "HideToTrayChk" - Me.HideToTrayChk.Size = New System.Drawing.Size(244, 25) - Me.HideToTrayChk.TabIndex = 12 - Me.HideToTrayChk.Text = "최소화 시 작업 표시줄에서 숨기기" - Me.HideToTrayChk.UseVisualStyleBackColor = True + Me.Label13.AutoSize = True + Me.Label13.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label13.Location = New System.Drawing.Point(21, 15) + Me.Label13.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label13.Name = "Label13" + Me.Label13.Size = New System.Drawing.Size(180, 27) + Me.Label13.TabIndex = 12 + Me.Label13.Text = "시간표 저장 위치 설정" ' - 'Panel1 + 'TapPage_Notification ' - Me.Panel1.Controls.Add(Me.D_ThemeRbt) - Me.Panel1.Controls.Add(Me.W_ThemeRbt) - Me.Panel1.Location = New System.Drawing.Point(351, 40) - Me.Panel1.Margin = New System.Windows.Forms.Padding(4) - Me.Panel1.Name = "Panel1" - Me.Panel1.Size = New System.Drawing.Size(159, 66) - Me.Panel1.TabIndex = 11 + Me.TapPage_Notification.Controls.Add(Me.TodaysCourseNotifyChk) + Me.TapPage_Notification.Controls.Add(Me.CourseNotifyPanel) + Me.TapPage_Notification.Controls.Add(Me.Label21) + Me.TapPage_Notification.Controls.Add(Me.CourseNotifyChk) + Me.TapPage_Notification.Dock = System.Windows.Forms.DockStyle.Fill + Me.TapPage_Notification.Location = New System.Drawing.Point(185, 12) + Me.TapPage_Notification.Margin = New System.Windows.Forms.Padding(4) + Me.TapPage_Notification.Name = "TapPage_Notification" + Me.TapPage_Notification.Size = New System.Drawing.Size(552, 347) + Me.TapPage_Notification.TabIndex = 12 ' - 'D_ThemeRbt + 'TodaysCourseNotifyChk ' - Me.D_ThemeRbt.AutoSize = True - Me.D_ThemeRbt.Location = New System.Drawing.Point(4, 35) - Me.D_ThemeRbt.Margin = New System.Windows.Forms.Padding(4) - Me.D_ThemeRbt.Name = "D_ThemeRbt" - Me.D_ThemeRbt.Size = New System.Drawing.Size(59, 25) - Me.D_ThemeRbt.TabIndex = 1 - Me.D_ThemeRbt.TabStop = True - Me.D_ThemeRbt.Text = "다크" - Me.D_ThemeRbt.UseVisualStyleBackColor = True + Me.TodaysCourseNotifyChk.AutoSize = True + Me.TodaysCourseNotifyChk.Location = New System.Drawing.Point(26, 257) + Me.TodaysCourseNotifyChk.Margin = New System.Windows.Forms.Padding(4) + Me.TodaysCourseNotifyChk.Name = "TodaysCourseNotifyChk" + Me.TodaysCourseNotifyChk.Size = New System.Drawing.Size(290, 25) + Me.TodaysCourseNotifyChk.TabIndex = 7 + Me.TodaysCourseNotifyChk.Text = "프로그램 시작시 오늘의 수업 알림 활성화" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) + Me.TodaysCourseNotifyChk.UseVisualStyleBackColor = True ' - 'W_ThemeRbt + 'CourseNotifyPanel ' - Me.W_ThemeRbt.AutoSize = True - Me.W_ThemeRbt.Location = New System.Drawing.Point(4, 9) - Me.W_ThemeRbt.Margin = New System.Windows.Forms.Padding(4) - Me.W_ThemeRbt.Name = "W_ThemeRbt" - Me.W_ThemeRbt.Size = New System.Drawing.Size(115, 25) - Me.W_ThemeRbt.TabIndex = 0 - Me.W_ThemeRbt.TabStop = True - Me.W_ThemeRbt.Text = "화이트 (기본)" - Me.W_ThemeRbt.UseVisualStyleBackColor = True + Me.CourseNotifyPanel.Controls.Add(Me.NotificationSoundPlayBT) + Me.CourseNotifyPanel.Controls.Add(Me.NotificationSoundFileOpenBT) + Me.CourseNotifyPanel.Controls.Add(Me.NotificationSoundLocationTB) + Me.CourseNotifyPanel.Controls.Add(Me.NotifySoundChk) + Me.CourseNotifyPanel.Controls.Add(Me.Label22) + Me.CourseNotifyPanel.Controls.Add(Me.Label19) + Me.CourseNotifyPanel.Controls.Add(Me.Label18) + Me.CourseNotifyPanel.Controls.Add(Me.Label20) + Me.CourseNotifyPanel.Controls.Add(Me.Notify_5min_Chk) + Me.CourseNotifyPanel.Controls.Add(Me.Notify_15min_Chk) + Me.CourseNotifyPanel.Controls.Add(Me.Notify_30min_Chk) + Me.CourseNotifyPanel.Location = New System.Drawing.Point(16, 82) + Me.CourseNotifyPanel.Margin = New System.Windows.Forms.Padding(4) + Me.CourseNotifyPanel.Name = "CourseNotifyPanel" + Me.CourseNotifyPanel.Size = New System.Drawing.Size(519, 165) + Me.CourseNotifyPanel.TabIndex = 6 ' - 'Label3 + 'NotificationSoundPlayBT ' - Me.Label3.AutoSize = True - Me.Label3.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label3.Location = New System.Drawing.Point(346, 15) - Me.Label3.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label3.Name = "Label3" - Me.Label3.Size = New System.Drawing.Size(85, 27) - Me.Label3.TabIndex = 10 - Me.Label3.Text = "테마 선택" + Me.NotificationSoundPlayBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat + Me.NotificationSoundPlayBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.NotificationSoundPlayBT.Location = New System.Drawing.Point(470, 86) + Me.NotificationSoundPlayBT.Margin = New System.Windows.Forms.Padding(4) + Me.NotificationSoundPlayBT.Name = "NotificationSoundPlayBT" + Me.NotificationSoundPlayBT.Size = New System.Drawing.Size(36, 29) + Me.NotificationSoundPlayBT.TabIndex = 20 + Me.NotificationSoundPlayBT.Text = "▶" + Me.NotificationSoundPlayBT.UseVisualStyleBackColor = True ' - 'FadeEffectChk + 'NotificationSoundFileOpenBT ' - Me.FadeEffectChk.AutoSize = True - Me.FadeEffectChk.Location = New System.Drawing.Point(29, 232) - Me.FadeEffectChk.Margin = New System.Windows.Forms.Padding(4) - Me.FadeEffectChk.Name = "FadeEffectChk" - Me.FadeEffectChk.Size = New System.Drawing.Size(232, 25) - Me.FadeEffectChk.TabIndex = 9 - Me.FadeEffectChk.Text = "페이드 인/아웃 애니메이션 적용" - Me.FadeEffectChk.UseVisualStyleBackColor = True + Me.NotificationSoundFileOpenBT.FlatStyle = System.Windows.Forms.FlatStyle.Flat + Me.NotificationSoundFileOpenBT.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.NotificationSoundFileOpenBT.Location = New System.Drawing.Point(429, 86) + Me.NotificationSoundFileOpenBT.Margin = New System.Windows.Forms.Padding(4) + Me.NotificationSoundFileOpenBT.Name = "NotificationSoundFileOpenBT" + Me.NotificationSoundFileOpenBT.Size = New System.Drawing.Size(36, 29) + Me.NotificationSoundFileOpenBT.TabIndex = 19 + Me.NotificationSoundFileOpenBT.Text = "..." + Me.NotificationSoundFileOpenBT.UseVisualStyleBackColor = True ' - 'MinStartChk + 'NotificationSoundLocationTB ' - Me.MinStartChk.AutoSize = True - Me.MinStartChk.Location = New System.Drawing.Point(29, 78) - Me.MinStartChk.Margin = New System.Windows.Forms.Padding(4) - Me.MinStartChk.Name = "MinStartChk" - Me.MinStartChk.Size = New System.Drawing.Size(170, 25) - Me.MinStartChk.TabIndex = 8 - Me.MinStartChk.Text = "최소화 된 상태로 시작" - Me.MinStartChk.UseVisualStyleBackColor = True + Me.NotificationSoundLocationTB.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.NotificationSoundLocationTB.Location = New System.Drawing.Point(171, 86) + Me.NotificationSoundLocationTB.Margin = New System.Windows.Forms.Padding(4) + Me.NotificationSoundLocationTB.Name = "NotificationSoundLocationTB" + Me.NotificationSoundLocationTB.Size = New System.Drawing.Size(250, 27) + Me.NotificationSoundLocationTB.TabIndex = 9 ' - 'Label7 + 'NotifySoundChk ' - Me.Label7.AutoSize = True - Me.Label7.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label7.Location = New System.Drawing.Point(21, 170) - Me.Label7.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label7.Name = "Label7" - Me.Label7.Size = New System.Drawing.Size(85, 27) - Me.Label7.TabIndex = 7 - Me.Label7.Text = "모양 설정" + Me.NotifySoundChk.AutoSize = True + Me.NotifySoundChk.Location = New System.Drawing.Point(101, 89) + Me.NotifySoundChk.Margin = New System.Windows.Forms.Padding(4) + Me.NotifySoundChk.Name = "NotifySoundChk" + Me.NotifySoundChk.Size = New System.Drawing.Size(60, 25) + Me.NotifySoundChk.TabIndex = 8 + Me.NotifySoundChk.Text = "사용" + Me.NotifySoundChk.UseVisualStyleBackColor = True ' - 'SnapToEdgeChk + 'Label22 ' - Me.SnapToEdgeChk.AutoSize = True - Me.SnapToEdgeChk.Location = New System.Drawing.Point(29, 205) - Me.SnapToEdgeChk.Margin = New System.Windows.Forms.Padding(4) - Me.SnapToEdgeChk.Name = "SnapToEdgeChk" - Me.SnapToEdgeChk.Size = New System.Drawing.Size(244, 25) - Me.SnapToEdgeChk.TabIndex = 6 - Me.SnapToEdgeChk.Text = "화면 모서리에 자동으로 붙게 하기" - Me.SnapToEdgeChk.UseVisualStyleBackColor = True + Me.Label22.AutoSize = True + Me.Label22.Location = New System.Drawing.Point(12, 90) + Me.Label22.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label22.Name = "Label22" + Me.Label22.Size = New System.Drawing.Size(70, 21) + Me.Label22.TabIndex = 7 + Me.Label22.Text = "알림 소리" ' - 'Label2 + 'Label19 ' - Me.Label2.AutoSize = True - Me.Label2.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label2.ForeColor = System.Drawing.Color.Gray - Me.Label2.Location = New System.Drawing.Point(25, 111) - Me.Label2.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label2.Name = "Label2" - Me.Label2.Size = New System.Drawing.Size(277, 42) - Me.Label2.TabIndex = 5 - Me.Label2.Text = "※ 실행 파일(.exe)의 위치가 변경되는 경우" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & " 다시 설정하셔야 합니다." + Me.Label19.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label19.ForeColor = System.Drawing.Color.Gray + Me.Label19.Location = New System.Drawing.Point(10, 122) + Me.Label19.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label19.Name = "Label19" + Me.Label19.Size = New System.Drawing.Size(486, 39) + Me.Label19.TabIndex = 6 + Me.Label19.Text = "Tip: 강의 메모에 ""(알림 무시)"" 또는 ""(알림X)"" 라고 입력하면" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "해당 과목의 알림이 무시됩니다." ' - 'Label1 + 'Label18 ' - Me.Label1.AutoSize = True - Me.Label1.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Label1.Location = New System.Drawing.Point(21, 15) - Me.Label1.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.Label1.Name = "Label1" - Me.Label1.Size = New System.Drawing.Size(85, 27) - Me.Label1.TabIndex = 4 - Me.Label1.Text = "시작 설정" + Me.Label18.AutoSize = True + Me.Label18.Location = New System.Drawing.Point(12, 14) + Me.Label18.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label18.Name = "Label18" + Me.Label18.Size = New System.Drawing.Size(70, 21) + Me.Label18.TabIndex = 3 + Me.Label18.Text = "알림 시간" ' - 'StartupChk + 'Label20 ' - Me.StartupChk.AutoSize = True - Me.StartupChk.Location = New System.Drawing.Point(29, 50) - Me.StartupChk.Margin = New System.Windows.Forms.Padding(4) - Me.StartupChk.Name = "StartupChk" - Me.StartupChk.Size = New System.Drawing.Size(268, 25) - Me.StartupChk.TabIndex = 3 - Me.StartupChk.Text = "Windows 시작 시 같이 프로그램 실행" - Me.StartupChk.UseVisualStyleBackColor = True + Me.Label20.Font = New System.Drawing.Font("Noto Sans KR", 8.0!) + Me.Label20.ForeColor = System.Drawing.Color.Gray + Me.Label20.Location = New System.Drawing.Point(12, 40) + Me.Label20.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label20.Name = "Label20" + Me.Label20.Size = New System.Drawing.Size(485, 38) + Me.Label20.TabIndex = 5 + Me.Label20.Text = "아무것도 체크하지 않을 시 수업 시작 시간에만," & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "중복 체크시 해당 시간 모두 알림이 옵니다." ' - 'TabPage_Info + 'Notify_5min_Chk ' - Me.TabPage_Info.Controls.Add(Me.RichTextBox1) - Me.TabPage_Info.Controls.Add(Me.VersionLabel) - Me.TabPage_Info.Controls.Add(Me.FeedbackLabel) - Me.TabPage_Info.Controls.Add(Me.BannerPictureBox) - Me.TabPage_Info.Dock = System.Windows.Forms.DockStyle.Fill - Me.TabPage_Info.Location = New System.Drawing.Point(185, 12) - Me.TabPage_Info.Margin = New System.Windows.Forms.Padding(4) - Me.TabPage_Info.Name = "TabPage_Info" - Me.TabPage_Info.Padding = New System.Windows.Forms.Padding(12) - Me.TabPage_Info.Size = New System.Drawing.Size(552, 347) - Me.TabPage_Info.TabIndex = 14 + Me.Notify_5min_Chk.AutoSize = True + Me.Notify_5min_Chk.Location = New System.Drawing.Point(279, 12) + Me.Notify_5min_Chk.Margin = New System.Windows.Forms.Padding(4) + Me.Notify_5min_Chk.Name = "Notify_5min_Chk" + Me.Notify_5min_Chk.Size = New System.Drawing.Size(72, 25) + Me.Notify_5min_Chk.TabIndex = 2 + Me.Notify_5min_Chk.Text = "5분 전" + Me.Notify_5min_Chk.UseVisualStyleBackColor = True ' - 'RichTextBox1 + 'Notify_15min_Chk ' - Me.RichTextBox1.BackColor = System.Drawing.Color.White - Me.RichTextBox1.BorderStyle = System.Windows.Forms.BorderStyle.None - Me.RichTextBox1.Dock = System.Windows.Forms.DockStyle.Fill - Me.RichTextBox1.ForeColor = System.Drawing.Color.Black - Me.RichTextBox1.Location = New System.Drawing.Point(12, 115) - Me.RichTextBox1.Margin = New System.Windows.Forms.Padding(4) - Me.RichTextBox1.Name = "RichTextBox1" - Me.RichTextBox1.ReadOnly = True - Me.RichTextBox1.Size = New System.Drawing.Size(528, 200) - Me.RichTextBox1.TabIndex = 3 - Me.RichTextBox1.Text = resources.GetString("RichTextBox1.Text") + Me.Notify_15min_Chk.AutoSize = True + Me.Notify_15min_Chk.Location = New System.Drawing.Point(190, 12) + Me.Notify_15min_Chk.Margin = New System.Windows.Forms.Padding(4) + Me.Notify_15min_Chk.Name = "Notify_15min_Chk" + Me.Notify_15min_Chk.Size = New System.Drawing.Size(80, 25) + Me.Notify_15min_Chk.TabIndex = 1 + Me.Notify_15min_Chk.Text = "15분 전" + Me.Notify_15min_Chk.UseVisualStyleBackColor = True ' - 'VersionLabel + 'Notify_30min_Chk ' - Me.VersionLabel.Dock = System.Windows.Forms.DockStyle.Top - Me.VersionLabel.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.VersionLabel.Location = New System.Drawing.Point(12, 84) - Me.VersionLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.VersionLabel.Name = "VersionLabel" - Me.VersionLabel.Size = New System.Drawing.Size(528, 31) - Me.VersionLabel.TabIndex = 7 - Me.VersionLabel.Text = "VersionLabel" - Me.VersionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter + Me.Notify_30min_Chk.AutoSize = True + Me.Notify_30min_Chk.Location = New System.Drawing.Point(101, 12) + Me.Notify_30min_Chk.Margin = New System.Windows.Forms.Padding(4) + Me.Notify_30min_Chk.Name = "Notify_30min_Chk" + Me.Notify_30min_Chk.Size = New System.Drawing.Size(80, 25) + Me.Notify_30min_Chk.TabIndex = 0 + Me.Notify_30min_Chk.Text = "30분 전" + Me.Notify_30min_Chk.UseVisualStyleBackColor = True ' - 'FeedbackLabel + 'Label21 ' - Me.FeedbackLabel.AutoSize = True - Me.FeedbackLabel.Dock = System.Windows.Forms.DockStyle.Bottom - Me.FeedbackLabel.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.FeedbackLabel.LinkColor = System.Drawing.Color.FromArgb(CType(CType(64, Byte), Integer), CType(CType(64, Byte), Integer), CType(CType(64, Byte), Integer)) - Me.FeedbackLabel.Location = New System.Drawing.Point(12, 315) - Me.FeedbackLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.FeedbackLabel.MinimumSize = New System.Drawing.Size(525, 0) - Me.FeedbackLabel.Name = "FeedbackLabel" - Me.FeedbackLabel.Size = New System.Drawing.Size(525, 20) - Me.FeedbackLabel.TabIndex = 6 - Me.FeedbackLabel.TabStop = True - Me.FeedbackLabel.Text = "오류 보고/피드백 남기기" - Me.FeedbackLabel.TextAlign = System.Drawing.ContentAlignment.TopRight + Me.Label21.AutoSize = True + Me.Label21.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.Label21.Location = New System.Drawing.Point(21, 15) + Me.Label21.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.Label21.Name = "Label21" + Me.Label21.Size = New System.Drawing.Size(85, 27) + Me.Label21.TabIndex = 4 + Me.Label21.Text = "알림 설정" ' - 'BannerPictureBox + 'CourseNotifyChk ' - Me.BannerPictureBox.Dock = System.Windows.Forms.DockStyle.Top - Me.BannerPictureBox.Image = Global.uTable.My.Resources.Resources.uTable_banner - Me.BannerPictureBox.Location = New System.Drawing.Point(12, 12) - Me.BannerPictureBox.Margin = New System.Windows.Forms.Padding(4) - Me.BannerPictureBox.Name = "BannerPictureBox" - Me.BannerPictureBox.Size = New System.Drawing.Size(528, 72) - Me.BannerPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom - Me.BannerPictureBox.TabIndex = 4 - Me.BannerPictureBox.TabStop = False + Me.CourseNotifyChk.AutoSize = True + Me.CourseNotifyChk.Location = New System.Drawing.Point(28, 58) + Me.CourseNotifyChk.Margin = New System.Windows.Forms.Padding(4) + Me.CourseNotifyChk.Name = "CourseNotifyChk" + Me.CourseNotifyChk.Size = New System.Drawing.Size(138, 25) + Me.CourseNotifyChk.TabIndex = 3 + Me.CourseNotifyChk.Text = "강의 알림 활성화" + Me.CourseNotifyChk.TextAlign = System.Drawing.ContentAlignment.MiddleCenter + Me.CourseNotifyChk.UseVisualStyleBackColor = True ' 'TabPage_Update ' @@ -1372,6 +1304,74 @@ Partial Class OptionForm Me.MSStoreLinkLabel.Text = "스토어 페이지로 가기" Me.MSStoreLinkLabel.TextAlign = System.Drawing.ContentAlignment.TopRight ' + 'TabPage_Info + ' + Me.TabPage_Info.Controls.Add(Me.RichTextBox1) + Me.TabPage_Info.Controls.Add(Me.VersionLabel) + Me.TabPage_Info.Controls.Add(Me.FeedbackLabel) + Me.TabPage_Info.Controls.Add(Me.BannerPictureBox) + Me.TabPage_Info.Dock = System.Windows.Forms.DockStyle.Fill + Me.TabPage_Info.Location = New System.Drawing.Point(185, 12) + Me.TabPage_Info.Margin = New System.Windows.Forms.Padding(4) + Me.TabPage_Info.Name = "TabPage_Info" + Me.TabPage_Info.Padding = New System.Windows.Forms.Padding(12) + Me.TabPage_Info.Size = New System.Drawing.Size(552, 347) + Me.TabPage_Info.TabIndex = 14 + ' + 'RichTextBox1 + ' + Me.RichTextBox1.BackColor = System.Drawing.Color.White + Me.RichTextBox1.BorderStyle = System.Windows.Forms.BorderStyle.None + Me.RichTextBox1.Dock = System.Windows.Forms.DockStyle.Fill + Me.RichTextBox1.ForeColor = System.Drawing.Color.Black + Me.RichTextBox1.Location = New System.Drawing.Point(12, 115) + Me.RichTextBox1.Margin = New System.Windows.Forms.Padding(4) + Me.RichTextBox1.Name = "RichTextBox1" + Me.RichTextBox1.ReadOnly = True + Me.RichTextBox1.Size = New System.Drawing.Size(528, 200) + Me.RichTextBox1.TabIndex = 3 + Me.RichTextBox1.Text = resources.GetString("RichTextBox1.Text") + ' + 'VersionLabel + ' + Me.VersionLabel.Dock = System.Windows.Forms.DockStyle.Top + Me.VersionLabel.Font = New System.Drawing.Font("Noto Sans KR", 8.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.VersionLabel.Location = New System.Drawing.Point(12, 84) + Me.VersionLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.VersionLabel.Name = "VersionLabel" + Me.VersionLabel.Size = New System.Drawing.Size(528, 31) + Me.VersionLabel.TabIndex = 7 + Me.VersionLabel.Text = "VersionLabel" + Me.VersionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter + ' + 'FeedbackLabel + ' + Me.FeedbackLabel.AutoSize = True + Me.FeedbackLabel.Dock = System.Windows.Forms.DockStyle.Bottom + Me.FeedbackLabel.Font = New System.Drawing.Font("Noto Sans KR", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) + Me.FeedbackLabel.LinkColor = System.Drawing.Color.FromArgb(CType(CType(64, Byte), Integer), CType(CType(64, Byte), Integer), CType(CType(64, Byte), Integer)) + Me.FeedbackLabel.Location = New System.Drawing.Point(12, 315) + Me.FeedbackLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.FeedbackLabel.MinimumSize = New System.Drawing.Size(525, 0) + Me.FeedbackLabel.Name = "FeedbackLabel" + Me.FeedbackLabel.Size = New System.Drawing.Size(525, 20) + Me.FeedbackLabel.TabIndex = 6 + Me.FeedbackLabel.TabStop = True + Me.FeedbackLabel.Text = "오류 보고/피드백 남기기" + Me.FeedbackLabel.TextAlign = System.Drawing.ContentAlignment.TopRight + ' + 'BannerPictureBox + ' + Me.BannerPictureBox.Dock = System.Windows.Forms.DockStyle.Top + Me.BannerPictureBox.Image = Global.uTable.My.Resources.Resources.uTable_banner + Me.BannerPictureBox.Location = New System.Drawing.Point(12, 12) + Me.BannerPictureBox.Margin = New System.Windows.Forms.Padding(4) + Me.BannerPictureBox.Name = "BannerPictureBox" + Me.BannerPictureBox.Size = New System.Drawing.Size(528, 72) + Me.BannerPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom + Me.BannerPictureBox.TabIndex = 4 + Me.BannerPictureBox.TabStop = False + ' 'ShadowPanel ' Me.ShadowPanel.BackgroundImage = Global.uTable.My.Resources.Resources.shadow1 @@ -1506,26 +1506,23 @@ Partial Class OptionForm Me.TitlePanel.ResumeLayout(False) CType(Me.CloseBT, System.ComponentModel.ISupportInitialize).EndInit() Me.MainPanel.ResumeLayout(False) - Me.TapPage_Notification.ResumeLayout(False) - Me.TapPage_Notification.PerformLayout() - Me.CourseNotifyPanel.ResumeLayout(False) - Me.CourseNotifyPanel.PerformLayout() + Me.TabPage_Basic.ResumeLayout(False) + Me.TabPage_Basic.PerformLayout() + Me.Panel1.ResumeLayout(False) + Me.Panel1.PerformLayout() + Me.TabPage_Table.ResumeLayout(False) + Me.TabPage_Table.PerformLayout() + Me.PrevTablePanel.ResumeLayout(False) Me.TabPage_Data.ResumeLayout(False) Me.TabPage_Data.PerformLayout() Me.Panel6.ResumeLayout(False) Me.Panel6.PerformLayout() Me.CustomDirPanel.ResumeLayout(False) Me.CustomDirPanel.PerformLayout() - Me.TabPage_Table.ResumeLayout(False) - Me.TabPage_Table.PerformLayout() - Me.PrevTablePanel.ResumeLayout(False) - Me.TabPage_Basic.ResumeLayout(False) - Me.TabPage_Basic.PerformLayout() - Me.Panel1.ResumeLayout(False) - Me.Panel1.PerformLayout() - Me.TabPage_Info.ResumeLayout(False) - Me.TabPage_Info.PerformLayout() - CType(Me.BannerPictureBox, System.ComponentModel.ISupportInitialize).EndInit() + Me.TapPage_Notification.ResumeLayout(False) + Me.TapPage_Notification.PerformLayout() + Me.CourseNotifyPanel.ResumeLayout(False) + Me.CourseNotifyPanel.PerformLayout() Me.TabPage_Update.ResumeLayout(False) Me.Panel4.ResumeLayout(False) Me.Panel2.ResumeLayout(False) @@ -1535,6 +1532,9 @@ Partial Class OptionForm Me.UpdateCtrlMSStorePanel.ResumeLayout(False) Me.UpdateCtrlMSStorePanel.PerformLayout() CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).EndInit() + Me.TabPage_Info.ResumeLayout(False) + Me.TabPage_Info.PerformLayout() + CType(Me.BannerPictureBox, System.ComponentModel.ISupportInitialize).EndInit() Me.SidePanel.ResumeLayout(False) Me.ResumeLayout(False) diff --git a/utable/Forms/OptionForm.vb b/utable/Forms/OptionForm.vb index e40eff1..c553963 100644 --- a/utable/Forms/OptionForm.vb +++ b/utable/Forms/OptionForm.vb @@ -275,9 +275,32 @@ Public Class OptionForm Case "Dark" BannerPictureBox.Image = My.Resources.uTable_banner_dark CloseBT.Image = My.Resources.closeicon_w + + ' 옵션 컨트롤 내에 있는 모든 체크박스, 라디오버튼에 다크 모드 테마 적용 + For Each chk As CheckBox In GetAll(Me, GetType(CheckBox)) + SetWindowTheme(chk.Handle, "DarkMode_Explorer", Nothing) + Next + + For Each rdo As RadioButton In GetAll(Me, GetType(RadioButton)) + SetWindowTheme(rdo.Handle, "DarkMode_Explorer", Nothing) + Next + + SetWindowTheme(RichTextBox1.Handle, "DarkMode_Explorer", Nothing) + Case Else BannerPictureBox.Image = My.Resources.uTable_banner CloseBT.Image = My.Resources.closeicon_b + + For Each chk As CheckBox In GetAll(Me, GetType(CheckBox)) + SetWindowTheme(chk.Handle, "Explorer", Nothing) + Next + + For Each rdo As RadioButton In GetAll(Me, GetType(RadioButton)) + SetWindowTheme(rdo.Handle, "Explorer", Nothing) + Next + + SetWindowTheme(RichTextBox1.Handle, "Explorer", Nothing) + End Select End Sub diff --git a/utable/Modules/GUIModule.vb b/utable/Modules/GUIModule.vb index b11f92c..c58a48d 100644 --- a/utable/Modules/GUIModule.vb +++ b/utable/Modules/GUIModule.vb @@ -1,4 +1,5 @@ Imports System.Runtime.InteropServices +Imports Microsoft.Win32 Module GUIModule @@ -259,4 +260,28 @@ Module GUIModule #End Region +#Region "라이트/다크 Win32 호출모듈" + + Public Function SetWindowTheme( + hWnd As IntPtr, + pszSubAppName As String, + pszSubIdList As String + ) As Integer + End Function + Public Function IsWindowsDarkMode() As Boolean + Using key = Registry.CurrentUser.OpenSubKey( + "Software\Microsoft\Windows\CurrentVersion\Themes\Personalize") + + If key Is Nothing Then Return False + + Dim value = key.GetValue("AppsUseLightTheme") + + If value Is Nothing Then Return False + + Return Convert.ToInt32(value) = 0 + End Using + End Function +#End Region + + End Module diff --git a/utable/UserControls/CellControl.Designer.vb b/utable/UserControls/CellControl.Designer.vb index e1f60e6..c90b712 100644 --- a/utable/UserControls/CellControl.Designer.vb +++ b/utable/UserControls/CellControl.Designer.vb @@ -71,7 +71,7 @@ Partial Class CellControl Me.Panel1.Controls.Add(Me.TitleLabel) Me.Panel1.Dock = System.Windows.Forms.DockStyle.Fill Me.Panel1.Location = New System.Drawing.Point(0, 26) - Me.Panel1.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.Panel1.Margin = New System.Windows.Forms.Padding(4) Me.Panel1.Name = "Panel1" Me.Panel1.Size = New System.Drawing.Size(180, 274) Me.Panel1.TabIndex = 2 @@ -122,7 +122,7 @@ Partial Class CellControl ' Me.TopNotchPanel.Dock = System.Windows.Forms.DockStyle.Top Me.TopNotchPanel.Location = New System.Drawing.Point(0, 0) - Me.TopNotchPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.TopNotchPanel.Margin = New System.Windows.Forms.Padding(4) Me.TopNotchPanel.Name = "TopNotchPanel" Me.TopNotchPanel.Size = New System.Drawing.Size(180, 2) Me.TopNotchPanel.TabIndex = 3 @@ -138,7 +138,7 @@ Partial Class CellControl Me.TopPanel.Controls.Add(Me.ChkBox1) Me.TopPanel.Dock = System.Windows.Forms.DockStyle.Top Me.TopPanel.Location = New System.Drawing.Point(0, 2) - Me.TopPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.TopPanel.Margin = New System.Windows.Forms.Padding(4) Me.TopPanel.Name = "TopPanel" Me.TopPanel.Size = New System.Drawing.Size(180, 24) Me.TopPanel.TabIndex = 3 @@ -148,7 +148,7 @@ Partial Class CellControl Me.ChkBox1.Dock = System.Windows.Forms.DockStyle.Left Me.ChkBox1.Image = Global.uTable.My.Resources.Resources.check1_w_96 Me.ChkBox1.Location = New System.Drawing.Point(0, 0) - Me.ChkBox1.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.ChkBox1.Margin = New System.Windows.Forms.Padding(4) Me.ChkBox1.Name = "ChkBox1" Me.ChkBox1.Size = New System.Drawing.Size(24, 24) Me.ChkBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom @@ -171,7 +171,7 @@ Partial Class CellControl Me.DoubleBuffered = True Me.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.ForeColor = System.Drawing.Color.White - Me.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.Margin = New System.Windows.Forms.Padding(4) Me.Name = "CellControl" Me.Size = New System.Drawing.Size(180, 325) Me.Panel1.ResumeLayout(False) From 32a672337a380a219b9da9ca8d5287e70da21d90 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Wed, 19 Aug 2026 15:35:54 +0900 Subject: [PATCH 02/24] refactor: integrate table cellcontrol timer --- utable/Forms/TableForm.vb | 148 ++++++++++++-------- utable/UserControls/CellControl.Designer.vb | 13 -- utable/UserControls/CellControl.resx | 8 +- utable/UserControls/CellControl.vb | 50 ++++--- 4 files changed, 115 insertions(+), 104 deletions(-) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index f7f1c62..d057c32 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -16,6 +16,9 @@ Public Class TableForm Dim updated As Boolean = False Dim courseData As New List(Of String) Dim courseRecords As New List(Of TableCourse) + Private ReadOnly cellsByCourse As New Dictionary(Of TableCourse, CellControl) + Private courseLayoutQueued As Boolean = False + Private WithEvents courseFadeTimer As New Timer With {.Interval = 10} '슬라이딩 애니메이션용 변수 Dim poscount As Integer = 0 @@ -921,6 +924,7 @@ Public Class TableForm #Region "시간표 셀 관리" Public Sub updateCell() + courseFadeTimer.Stop() SuspendCourseLayouts() Try @@ -948,6 +952,7 @@ Public Class TableForm ClearCourseCells(FriPanel) ClearCourseCells(SatPanel) ClearCourseCells(SunPanel) + cellsByCourse.Clear() updated = False Dim data = readTable() @@ -985,9 +990,10 @@ Public Class TableForm '셀계산 '여기서 xmldecode 하니까 꼭 눈여겨두자 For Each course As TableCourse In courseRecords - addCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), course.Identity(), - xmlDecode(course.Name), xmlDecode(course.Professor), xmlDecode(course.Memo), - ColorTranslator.FromHtml(course.Color), Convert.ToInt16(course.Day), course.Checked, course.RawData) + Dim cell = addCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), course.Identity(), + xmlDecode(course.Name), xmlDecode(course.Professor), xmlDecode(course.Memo), + ColorTranslator.FromHtml(course.Color), Convert.ToInt16(course.Day), course.Checked, course.RawData) + cellsByCourse.Add(course, cell) If Convert.ToInt16(course.Day) = 5 Then '토요일 추가시 showSaturday = True @@ -1043,9 +1049,7 @@ Public Class TableForm TimeTable.ColumnStyles(6).Width = 0 End If - For Each course As TableCourse In courseRecords - resizeCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), course.Identity()) - Next + UpdateCourseBounds() Catch ex As Exception @@ -1082,7 +1086,7 @@ Public Class TableForm TimeTable.ResumeLayout(True) End Sub - Sub addCell(startt As Integer, endt As Integer, name As String, title As String, prof As String, memo As String, color As Color, day As Integer, checked As String, rawData As String) + Private Function addCell(startt As Integer, endt As Integer, name As String, title As String, prof As String, memo As String, color As Color, day As Integer, checked As String, rawData As String) As CellControl Dim timelength As Integer = endtime - starttime Dim part As Double = (endt - startt) / timelength @@ -1091,6 +1095,7 @@ Public Class TableForm cell.ForeColor = Color.White 'cell.BackColor = color cell.goalColor = color + AddHandler cell.FadeStarted, AddressOf CellFadeStarted With cell .FadeEffect = FadeEffect @@ -1157,23 +1162,61 @@ Public Class TableForm cell.Tag = rawData cell.checked = (checked = "True") - End Sub + Return cell + End Function - Sub resizeCell(startt As Integer, endt As Integer, name As String) + Private Sub resizeCell(startt As Integer, endt As Integer, cell As CellControl) Dim timelength As Integer = endtime - starttime + If timelength <= 0 OrElse cell Is Nothing OrElse cell.Parent Is Nothing Then Exit Sub + Dim part As Double = (endt - startt) / timelength - Dim cell As CellControl = TimeTable.Controls.Find(name, True).First + Dim parentPanel As Panel = DirectCast(cell.Parent, Panel) - cell.Location = New Point(0, ((startt - starttime) / timelength) * MonPanel.Height) - cell.defLoc = ((startt - starttime) / timelength) * MonPanel.Height - 'MsgBox(((startt - starttime) / timelength) * Panel1.Height) - cell.Width = DirectCast(cell.Parent, Panel).ClientSize.Width - cell.Height = part * MonPanel.Height - cell.defHeight = part * MonPanel.Height + cell.Location = New Point(0, CInt(((startt - starttime) / timelength) * parentPanel.ClientSize.Height)) + cell.defLoc = CInt(((startt - starttime) / timelength) * parentPanel.ClientSize.Height) + cell.Width = parentPanel.ClientSize.Width + cell.Height = CInt(part * parentPanel.ClientSize.Height) + cell.defHeight = CInt(part * parentPanel.ClientSize.Height) If cell.alwaysExpand Then cell.ForceExpand() End Sub + Private Sub UpdateCourseBounds() + If Not updated Then Exit Sub + + For Each course As TableCourse In courseRecords + Dim cell As CellControl = Nothing + If cellsByCourse.TryGetValue(course, cell) Then + resizeCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), cell) + End If + Next + End Sub + + Private Sub QueueCourseBoundsUpdate() + If Not updated OrElse courseLayoutQueued OrElse IsDisposed OrElse Disposing Then Exit Sub + + courseLayoutQueued = True + BeginInvoke(New MethodInvoker( + Sub() + courseLayoutQueued = False + UpdateCourseBounds() + End Sub)) + End Sub + + Private Sub CellFadeStarted(sender As Object, e As EventArgs) + If Not courseFadeTimer.Enabled Then courseFadeTimer.Start() + End Sub + + Private Sub courseFadeTimer_Tick(sender As Object, e As EventArgs) Handles courseFadeTimer.Tick + Dim hasActiveFade As Boolean = False + + For Each cell As CellControl In cellsByCourse.Values + If cell.AdvanceFade() Then hasActiveFade = True + Next + + If Not hasActiveFade Then courseFadeTimer.Stop() + End Sub + #End Region #Region "시간표 타이틀 관리" @@ -1206,7 +1249,8 @@ Public Class TableForm TableTitleLabel.Visible = True RenameTitleTextBox.Visible = False TitleEditBT.Image = My.Resources.bt_titleedit - updateCell() + TableTitleLabel.Text = newtitle + Text = newtitle End If End Sub @@ -1217,24 +1261,11 @@ Public Class TableForm Private Sub TimeTable_SizeChanged(sender As Object, e As EventArgs) Handles TimeTable.SizeChanged 'TimeTable.ResumeLayout() - If updated Then - - For Each course As TableCourse In courseRecords - resizeCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), course.Identity()) - Next - - TimeTable.Visible = True - - End If + QueueCourseBoundsUpdate() End Sub Private Sub TimeTable_Layout(sender As Object, e As System.Windows.Forms.LayoutEventArgs) Handles TimeTable.Layout - If Not updated Then Exit Sub - - For Each course As TableCourse In courseRecords - Dim cell As CellControl = TimeTable.Controls.Find(course.Identity(), True).First - cell.Width = DirectCast(cell.Parent, Panel).ClientSize.Width - Next + QueueCourseBoundsUpdate() End Sub Private Sub TablePanel_Paint(sender As Object, e As PaintEventArgs) Handles MonPanel.Paint, TuePanel.Paint, WedPanel.Paint, @@ -1252,25 +1283,24 @@ Public Class TableForm Dim timeLength As Integer = endtime - starttime If Not timeLength > 0 Then Exit Sub - Dim panelHeight As Integer = MonPanel.Height - Dim panelWidth As Integer = MonPanel.Width + Dim panelHeight As Integer = panel.ClientSize.Height + Dim panelWidth As Integer = panel.ClientSize.Width Dim minLengh As Double = 1 / timeLength * panelHeight 'Dim left As Integer = starttime Mod 60 - Dim thickness As Integer = 3 * (currentDPI / 96) + Dim thickness As Integer = Math.Max(1, CInt(Math.Round(3 * currentDPI / 96.0))) Dim colorMul As Single = 0.9 If colorMode = "Dark" Then colorMul = 1.35 End If - Dim c As Color = Color.FromArgb(panel.BackColor.R * colorMul, - panel.BackColor.G * colorMul, - panel.BackColor.B * colorMul) + Dim c As Color = Color.FromArgb(Math.Min(255, CInt(panel.BackColor.R * colorMul)), + Math.Min(255, CInt(panel.BackColor.G * colorMul)), + Math.Min(255, CInt(panel.BackColor.B * colorMul))) - Dim g As Graphics = panel.CreateGraphics + Dim g As Graphics = e.Graphics g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias - g.Clear(panel.BackColor) 'Win11용 코더라운딩 @@ -1288,39 +1318,33 @@ Public Class TableForm End If If showRnd Then - Dim edgeBrush = New SolidBrush(BackColor) - Dim backBrush = New SolidBrush(panel.BackColor) Dim rndSize = Convert.ToInt16(Me.RndSize * (currentDPI / 96)) - If isLeft Then - '좌측 - g.FillRectangle(edgeBrush, 0, panel.Height - rndSize, rndSize, rndSize) - g.FillEllipse(backBrush, 0, panel.Height - rndSize * 2, rndSize * 2, rndSize * 2) - Else - '우측 - g.FillRectangle(edgeBrush, panel.Width - rndSize, panel.Height - rndSize, rndSize, rndSize) - g.FillEllipse(backBrush, panel.Width - rndSize * 2, panel.Height - rndSize * 2, rndSize * 2, rndSize * 2) - End If + Using edgeBrush As New SolidBrush(BackColor), backBrush As New SolidBrush(panel.BackColor) + If isLeft Then + '좌측 + g.FillRectangle(edgeBrush, 0, panel.Height - rndSize, rndSize, rndSize) + g.FillEllipse(backBrush, 0, panel.Height - rndSize * 2, rndSize * 2, rndSize * 2) + Else + '우측 + g.FillRectangle(edgeBrush, panel.Width - rndSize, panel.Height - rndSize, rndSize, rndSize) + g.FillEllipse(backBrush, panel.Width - rndSize * 2, panel.Height - rndSize * 2, rndSize * 2, rndSize * 2) + End If + End Using End If ' 점선 표시 If Not tablePatternSetting = "None" Then - Dim p As New Pen(c, thickness) - 'Dim g As Graphics = panel.CreateGraphics + Using p As New Pen(c, thickness) + p.DashStyle = Drawing2D.DashStyle.Dot - p.DashStyle = Drawing2D.DashStyle.Dot - - For j As Integer = starttime To endtime - If j > 0 And j Mod 60 = 0 Then + For j As Integer = ((starttime + 59) \ 60) * 60 To endtime Step 60 Dim pos As Double = (j - starttime) * minLengh + thickness / 2 g.DrawLine(p, New Point(0, pos), New Point(panelWidth, pos)) - End If - Next - - g.Dispose() - p.Dispose() + Next + End Using End If End Sub diff --git a/utable/UserControls/CellControl.Designer.vb b/utable/UserControls/CellControl.Designer.vb index c90b712..0782619 100644 --- a/utable/UserControls/CellControl.Designer.vb +++ b/utable/UserControls/CellControl.Designer.vb @@ -30,10 +30,8 @@ Partial Class CellControl Me.ProfLabel = New System.Windows.Forms.Label() Me.TitleLabel = New System.Windows.Forms.Label() Me.TopNotchPanel = New System.Windows.Forms.Panel() - Me.Timer1 = New System.Windows.Forms.Timer(Me.components) Me.TopPanel = New System.Windows.Forms.Panel() Me.ChkBox1 = New System.Windows.Forms.PictureBox() - Me.AniTimer = New System.Windows.Forms.Timer(Me.components) Me.Panel1.SuspendLayout() Me.TopPanel.SuspendLayout() CType(Me.ChkBox1, System.ComponentModel.ISupportInitialize).BeginInit() @@ -127,11 +125,6 @@ Partial Class CellControl Me.TopNotchPanel.Size = New System.Drawing.Size(180, 2) Me.TopNotchPanel.TabIndex = 3 ' - 'Timer1 - ' - Me.Timer1.Enabled = True - Me.Timer1.Interval = 150 - ' 'TopPanel ' Me.TopPanel.Controls.Add(Me.TopTimeLabel) @@ -155,10 +148,6 @@ Partial Class CellControl Me.ChkBox1.TabIndex = 3 Me.ChkBox1.TabStop = False ' - 'AniTimer - ' - Me.AniTimer.Interval = 10 - ' 'CellControl ' Me.AutoScaleDimensions = New System.Drawing.SizeF(120.0!, 120.0!) @@ -190,8 +179,6 @@ Partial Class CellControl Friend WithEvents ProfLabel As Label Friend WithEvents TitleLabel As Label Friend WithEvents TopNotchPanel As Panel - Friend WithEvents Timer1 As Timer Friend WithEvents TopPanel As Panel Friend WithEvents ChkBox1 As PictureBox - Friend WithEvents AniTimer As Timer End Class diff --git a/utable/UserControls/CellControl.resx b/utable/UserControls/CellControl.resx index 5c926a1..4848987 100644 --- a/utable/UserControls/CellControl.resx +++ b/utable/UserControls/CellControl.resx @@ -117,10 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 17, 17 - - - 107, 17 - - \ No newline at end of file + diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index 4820ea6..c0bf8cd 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -9,7 +9,6 @@ Dim blackText As Boolean = False Dim hovered As Boolean = False - Dim prev_hove As Boolean = False '설정값 Public FadeEffect As String = "" @@ -27,7 +26,9 @@ Dim deltaColor_R As Integer = 1 Dim deltaColor_G As Integer = 1 Dim deltaColor_B As Integer = 1 + Dim fadeInProgress As Boolean = False Public goalColor As Color = Nothing + Public Event FadeStarted As EventHandler Private Sub UserControl1_SizeChanged(sender As Object, e As EventArgs) Handles MyBase.SizeChanged TitleLabel.MaximumSize() = New Size(Width, 0) @@ -63,14 +64,16 @@ ViewCourse.Show() End Sub - Private Sub TopTimeLabel_MouseEnter(sender As Object, e As EventArgs) Handles TopTimeLabel.MouseEnter, - TitleLabel.MouseEnter, ProfLabel.MouseEnter, Panel1.MouseEnter, MemoLabel.MouseEnter, BottomTimeLabel.MouseEnter - hovered = True + Private Sub TopTimeLabel_MouseEnter(sender As Object, e As EventArgs) Handles MyBase.MouseEnter, TopTimeLabel.MouseEnter, + TitleLabel.MouseEnter, ProfLabel.MouseEnter, Panel1.MouseEnter, MemoLabel.MouseEnter, BottomTimeLabel.MouseEnter, + ChkBox1.MouseEnter, TopNotchPanel.MouseEnter + SetHovered(True) End Sub - Private Sub CellControl_MouseLeave(sender As Object, e As EventArgs) Handles TopTimeLabel.MouseLeave, - TitleLabel.MouseLeave, ProfLabel.MouseLeave, Panel1.MouseLeave, MemoLabel.MouseLeave, BottomTimeLabel.MouseLeave - hovered = False + Private Sub CellControl_MouseLeave(sender As Object, e As EventArgs) Handles MyBase.MouseLeave, TopTimeLabel.MouseLeave, + TitleLabel.MouseLeave, ProfLabel.MouseLeave, Panel1.MouseLeave, MemoLabel.MouseLeave, BottomTimeLabel.MouseLeave, + ChkBox1.MouseLeave, TopNotchPanel.MouseLeave + If Not RectangleToScreen(ClientRectangle).Contains(Cursor.Position) Then SetHovered(False) End Sub Private Sub TitleLabel_MouseEnter(sender As Object, e As EventArgs) Handles TitleLabel.MouseEnter @@ -136,11 +139,7 @@ BackColor = TableForm.TuePanel.BackColor End If - deltaColor_R = Int(Math.Abs((Int(goalColor.R) - Int(BackColor.R)) / 10)) - deltaColor_G = Int(Math.Abs((Int(goalColor.G) - Int(BackColor.G)) / 10)) - deltaColor_B = Int(Math.Abs((Int(goalColor.B) - Int(BackColor.B)) / 10)) - - AniTimer.Start() + BeginFade() Else BackColor = goalColor @@ -167,13 +166,9 @@ ' TopNotchPanel.BackColor = ControlPaint.Light(BackColor, 0.3) 'End Sub - Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick - - If prev_hove = hovered Then - Exit Sub - Else - prev_hove = hovered - End If + Private Sub SetHovered(value As Boolean) + If hovered = value Then Exit Sub + hovered = value If hovered Then Dim fullheight As Integer = TopTimeLabel.Height + TitleLabel.Height + BottomTimeLabel.Height @@ -292,7 +287,16 @@ Tag = newdata End Sub - Private Sub AniTimer_Tick(sender As Object, e As EventArgs) Handles AniTimer.Tick + Private Sub BeginFade() + deltaColor_R = CInt(Math.Abs((CInt(goalColor.R) - CInt(BackColor.R)) / 10)) + deltaColor_G = CInt(Math.Abs((CInt(goalColor.G) - CInt(BackColor.G)) / 10)) + deltaColor_B = CInt(Math.Abs((CInt(goalColor.B) - CInt(BackColor.B)) / 10)) + fadeInProgress = True + RaiseEvent FadeStarted(Me, EventArgs.Empty) + End Sub + + Public Function AdvanceFade() As Boolean + If Not fadeInProgress Then Return False Dim R As Byte = BackColor.R Dim G As Byte = BackColor.G @@ -326,9 +330,11 @@ TopNotchPanel.BackColor = ControlPaint.Light(BackColor, 0.3) If goalColor = BackColor Then - AniTimer.Stop() + fadeInProgress = False End If - End Sub + + Return fadeInProgress + End Function Private Sub TopPanel_Paint(sender As Object, e As PaintEventArgs) Handles TopPanel.Paint From 1d40cbe1f2f621f52b18cc82f911c3edd2b8c05d Mon Sep 17 00:00:00 2001 From: pdjdev Date: Wed, 19 Aug 2026 16:07:20 +0900 Subject: [PATCH 03/24] refactor: update CellControl and rendering logic for improved clarity and functionality --- utable/Forms/OptionForm.vb | 10 +- utable/Forms/TableForm.vb | 43 +- utable/My Project/app.manifest | 2 +- utable/UserControls/CellControl.Designer.vb | 164 +----- utable/UserControls/CellControl.resx | 2 +- utable/UserControls/CellControl.vb | 526 +++++++++++--------- 6 files changed, 329 insertions(+), 418 deletions(-) diff --git a/utable/Forms/OptionForm.vb b/utable/Forms/OptionForm.vb index c553963..9ecb720 100644 --- a/utable/Forms/OptionForm.vb +++ b/utable/Forms/OptionForm.vb @@ -531,12 +531,12 @@ Public Class OptionForm .Height = PrevTableArea.Height * 0.6 .defHeight = PrevTableArea.Height * 0.6 - .TopTimeLabel.Text = "12:27" - .BottomTimeLabel.Text = "13:27" + .StartText = "12:27" + .EndText = "13:27" - .TitleLabel.Text = "수업명" - .ProfLabel.Text = names(rnd.Next(0, names.Count)) + " 교수님" - .MemoLabel.Text = "메모 내용" + .CourseTitle = "수업명" + .ProfessorText = names(rnd.Next(0, names.Count)) + " 교수님" + .MemoText = "메모 내용" .FadeEffect = GetINI("SETTING", "FadeEffect", "", ININamePath) .CustomFont = GetINI("SETTING", "CustomFont", "", ININamePath) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index d057c32..c691abd 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -1108,60 +1108,63 @@ Public Class TableForm .ShowMemo = ShowMemo .ShowProf = ShowProf ._ShowChkBox = _ShowChkBox + .UsesSharedFadeClock = True End With + Dim parentPanel As Panel = Nothing Select Case day Case 0 - MonPanel.Controls.Add(cell) + parentPanel = MonPanel Case 1 - TuePanel.Controls.Add(cell) + parentPanel = TuePanel Case 2 - WedPanel.Controls.Add(cell) + parentPanel = WedPanel Case 3 - ThuPanel.Controls.Add(cell) + parentPanel = ThuPanel Case 4 - FriPanel.Controls.Add(cell) + parentPanel = FriPanel Case 5 - SatPanel.Controls.Add(cell) + parentPanel = SatPanel Case 6 - SunPanel.Controls.Add(cell) + parentPanel = SunPanel End Select - cell.Location = New Point(0, ((startt - starttime) / timelength) * MonPanel.Height) + cell.Location = New Point(0, ((startt - starttime) / timelength) * parentPanel.Height) 'MsgBox(((startt - starttime) / timelength) * Panel1.Height) - cell.Width = DirectCast(cell.Parent, Panel).ClientSize.Width - cell.Height = part * MonPanel.Height - cell.defHeight = part * MonPanel.Height + cell.Width = parentPanel.ClientSize.Width + cell.Height = part * parentPanel.Height + cell.defHeight = part * parentPanel.Height cell.dayNum = day 'MsgBox(part * Panel1.Height) - cell.TopTimeLabel.Text = (startt \ 60).ToString + ":" + cell.StartText = (startt \ 60).ToString + ":" If startt Mod 60 = 0 Then - cell.TopTimeLabel.Text += "00" + cell.StartText += "00" Else - cell.TopTimeLabel.Text += (startt Mod 60).ToString("D2") + cell.StartText += (startt Mod 60).ToString("D2") End If - cell.BottomTimeLabel.Text = (endt \ 60).ToString + ":" + cell.EndText = (endt \ 60).ToString + ":" If endt Mod 60 = 0 Then - cell.BottomTimeLabel.Text += "00" + cell.EndText += "00" Else - cell.BottomTimeLabel.Text += (endt Mod 60).ToString("D2") + cell.EndText += (endt Mod 60).ToString("D2") End If If title.Length > 100 Then title = Mid(title, 1, 100) + "..." If prof.Length > 50 Then prof = Mid(prof, 1, 50) + "..." If memo.Length > 2000 Then memo = Mid(memo, 1, 2000) + "..." - cell.TitleLabel.Text = title - cell.ProfLabel.Text = prof - cell.MemoLabel.Text = memo + cell.CourseTitle = title + cell.ProfessorText = prof + cell.MemoText = memo cell.Name = name cell.Tag = rawData cell.checked = (checked = "True") + parentPanel.Controls.Add(cell) Return cell End Function diff --git a/utable/My Project/app.manifest b/utable/My Project/app.manifest index 7147613..f7f79d0 100644 --- a/utable/My Project/app.manifest +++ b/utable/My Project/app.manifest @@ -52,7 +52,7 @@ - true + false diff --git a/utable/UserControls/CellControl.Designer.vb b/utable/UserControls/CellControl.Designer.vb index 0782619..36c8677 100644 --- a/utable/UserControls/CellControl.Designer.vb +++ b/utable/UserControls/CellControl.Designer.vb @@ -1,184 +1,32 @@ - _ + Partial Class CellControl Inherits System.Windows.Forms.UserControl - 'UserControl은 Dispose를 재정의하여 구성 요소 목록을 정리합니다. - _ + Protected Overrides Sub Dispose(ByVal disposing As Boolean) Try - If disposing AndAlso components IsNot Nothing Then - components.Dispose() - End If + If disposing Then DisposeRenderFonts() Finally MyBase.Dispose(disposing) End Try End Sub - 'Windows Form 디자이너에 필요합니다. - Private components As System.ComponentModel.IContainer - - '참고: 다음 프로시저는 Windows Form 디자이너에 필요합니다. - '수정하려면 Windows Form 디자이너를 사용하십시오. - '코드 편집기에서는 수정하지 마세요. - _ + Private Sub InitializeComponent() - Me.components = New System.ComponentModel.Container() - Me.TopTimeLabel = New System.Windows.Forms.Label() - Me.BottomTimeLabel = New System.Windows.Forms.Label() - Me.Panel1 = New System.Windows.Forms.Panel() - Me.MemoLabel = New System.Windows.Forms.Label() - Me.ProfLabel = New System.Windows.Forms.Label() - Me.TitleLabel = New System.Windows.Forms.Label() - Me.TopNotchPanel = New System.Windows.Forms.Panel() - Me.TopPanel = New System.Windows.Forms.Panel() - Me.ChkBox1 = New System.Windows.Forms.PictureBox() - Me.Panel1.SuspendLayout() - Me.TopPanel.SuspendLayout() - CType(Me.ChkBox1, System.ComponentModel.ISupportInitialize).BeginInit() Me.SuspendLayout() ' - 'TopTimeLabel - ' - Me.TopTimeLabel.Dock = System.Windows.Forms.DockStyle.Fill - Me.TopTimeLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.75!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.TopTimeLabel.Location = New System.Drawing.Point(24, 0) - Me.TopTimeLabel.Margin = New System.Windows.Forms.Padding(0) - Me.TopTimeLabel.Name = "TopTimeLabel" - Me.TopTimeLabel.Size = New System.Drawing.Size(156, 24) - Me.TopTimeLabel.TabIndex = 0 - Me.TopTimeLabel.Text = "TopTimeLabel" - Me.TopTimeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft - ' - 'BottomTimeLabel - ' - Me.BottomTimeLabel.AutoSize = True - Me.BottomTimeLabel.Dock = System.Windows.Forms.DockStyle.Bottom - Me.BottomTimeLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.75!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.BottomTimeLabel.Location = New System.Drawing.Point(0, 300) - Me.BottomTimeLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.BottomTimeLabel.Name = "BottomTimeLabel" - Me.BottomTimeLabel.Size = New System.Drawing.Size(164, 25) - Me.BottomTimeLabel.TabIndex = 1 - Me.BottomTimeLabel.Text = "BottomTimeLabel" - Me.BottomTimeLabel.TextAlign = System.Drawing.ContentAlignment.BottomRight - ' - 'Panel1 - ' - Me.Panel1.Controls.Add(Me.MemoLabel) - Me.Panel1.Controls.Add(Me.ProfLabel) - Me.Panel1.Controls.Add(Me.TitleLabel) - Me.Panel1.Dock = System.Windows.Forms.DockStyle.Fill - Me.Panel1.Location = New System.Drawing.Point(0, 26) - Me.Panel1.Margin = New System.Windows.Forms.Padding(4) - Me.Panel1.Name = "Panel1" - Me.Panel1.Size = New System.Drawing.Size(180, 274) - Me.Panel1.TabIndex = 2 - ' - 'MemoLabel - ' - Me.MemoLabel.AutoSize = True - Me.MemoLabel.Dock = System.Windows.Forms.DockStyle.Top - Me.MemoLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.MemoLabel.Location = New System.Drawing.Point(0, 52) - Me.MemoLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.MemoLabel.MaximumSize = New System.Drawing.Size(180, 0) - Me.MemoLabel.Name = "MemoLabel" - Me.MemoLabel.Size = New System.Drawing.Size(53, 21) - Me.MemoLabel.TabIndex = 2 - Me.MemoLabel.Text = "Memo" - Me.MemoLabel.UseMnemonic = False - ' - 'ProfLabel - ' - Me.ProfLabel.AutoSize = True - Me.ProfLabel.Dock = System.Windows.Forms.DockStyle.Top - Me.ProfLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.ProfLabel.Location = New System.Drawing.Point(0, 27) - Me.ProfLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.ProfLabel.MaximumSize = New System.Drawing.Size(180, 0) - Me.ProfLabel.Name = "ProfLabel" - Me.ProfLabel.Size = New System.Drawing.Size(93, 25) - Me.ProfLabel.TabIndex = 1 - Me.ProfLabel.Text = "ProfName" - Me.ProfLabel.UseMnemonic = False - ' - 'TitleLabel - ' - Me.TitleLabel.AutoSize = True - Me.TitleLabel.Dock = System.Windows.Forms.DockStyle.Top - Me.TitleLabel.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.TitleLabel.Location = New System.Drawing.Point(0, 0) - Me.TitleLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) - Me.TitleLabel.MaximumSize = New System.Drawing.Size(180, 0) - Me.TitleLabel.Name = "TitleLabel" - Me.TitleLabel.Size = New System.Drawing.Size(55, 27) - Me.TitleLabel.TabIndex = 0 - Me.TitleLabel.Text = "Title" - Me.TitleLabel.UseMnemonic = False - ' - 'TopNotchPanel - ' - Me.TopNotchPanel.Dock = System.Windows.Forms.DockStyle.Top - Me.TopNotchPanel.Location = New System.Drawing.Point(0, 0) - Me.TopNotchPanel.Margin = New System.Windows.Forms.Padding(4) - Me.TopNotchPanel.Name = "TopNotchPanel" - Me.TopNotchPanel.Size = New System.Drawing.Size(180, 2) - Me.TopNotchPanel.TabIndex = 3 - ' - 'TopPanel - ' - Me.TopPanel.Controls.Add(Me.TopTimeLabel) - Me.TopPanel.Controls.Add(Me.ChkBox1) - Me.TopPanel.Dock = System.Windows.Forms.DockStyle.Top - Me.TopPanel.Location = New System.Drawing.Point(0, 2) - Me.TopPanel.Margin = New System.Windows.Forms.Padding(4) - Me.TopPanel.Name = "TopPanel" - Me.TopPanel.Size = New System.Drawing.Size(180, 24) - Me.TopPanel.TabIndex = 3 - ' - 'ChkBox1 - ' - Me.ChkBox1.Dock = System.Windows.Forms.DockStyle.Left - Me.ChkBox1.Image = Global.uTable.My.Resources.Resources.check1_w_96 - Me.ChkBox1.Location = New System.Drawing.Point(0, 0) - Me.ChkBox1.Margin = New System.Windows.Forms.Padding(4) - Me.ChkBox1.Name = "ChkBox1" - Me.ChkBox1.Size = New System.Drawing.Size(24, 24) - Me.ChkBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom - Me.ChkBox1.TabIndex = 3 - Me.ChkBox1.TabStop = False - ' 'CellControl ' Me.AutoScaleDimensions = New System.Drawing.SizeF(120.0!, 120.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi Me.BackColor = System.Drawing.Color.Transparent - Me.Controls.Add(Me.Panel1) - Me.Controls.Add(Me.BottomTimeLabel) - Me.Controls.Add(Me.TopPanel) - Me.Controls.Add(Me.TopNotchPanel) Me.DoubleBuffered = True Me.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.ForeColor = System.Drawing.Color.White - Me.Margin = New System.Windows.Forms.Padding(4) + Me.Margin = New System.Windows.Forms.Padding(0) Me.Name = "CellControl" - Me.Size = New System.Drawing.Size(180, 325) - Me.Panel1.ResumeLayout(False) - Me.Panel1.PerformLayout() - Me.TopPanel.ResumeLayout(False) - CType(Me.ChkBox1, System.ComponentModel.ISupportInitialize).EndInit() + Me.Size = New System.Drawing.Size(180, 180) Me.ResumeLayout(False) - Me.PerformLayout() End Sub - - Friend WithEvents TopTimeLabel As Label - Friend WithEvents BottomTimeLabel As Label - Friend WithEvents Panel1 As Panel - Friend WithEvents MemoLabel As Label - Friend WithEvents ProfLabel As Label - Friend WithEvents TitleLabel As Label - Friend WithEvents TopNotchPanel As Panel - Friend WithEvents TopPanel As Panel - Friend WithEvents ChkBox1 As PictureBox End Class diff --git a/utable/UserControls/CellControl.resx b/utable/UserControls/CellControl.resx index 4848987..1af7de1 100644 --- a/utable/UserControls/CellControl.resx +++ b/utable/UserControls/CellControl.resx @@ -117,4 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + \ No newline at end of file diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index c0bf8cd..5920853 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -1,16 +1,18 @@ -Public Class CellControl +Imports System.Drawing.Text + +Public Class CellControl Public defHeight As Integer = 0 Public defLoc As Integer = 0 Public alwaysExpand As Boolean = False Public checked As Boolean = False Public dayNum As Integer = 0 - Dim doExpand As Boolean = True - Dim showChkBox As Boolean = True - Dim blackText As Boolean = False - Dim hovered As Boolean = False + Public StartText As String = "" + Public EndText As String = "" + Public CourseTitle As String = "" + Public ProfessorText As String = "" + Public MemoText As String = "" - '설정값 Public FadeEffect As String = "" Public CustomFont As String = "" Public CustomFontName As String = "" @@ -21,266 +23,320 @@ Public ShowMemo As String = "" Public ShowProf As String = "" Public _ShowChkBox As String = "" - - '(실험적) 컬러 전환 크기 - Dim deltaColor_R As Integer = 1 - Dim deltaColor_G As Integer = 1 - Dim deltaColor_B As Integer = 1 - Dim fadeInProgress As Boolean = False + Public UsesSharedFadeClock As Boolean = False Public goalColor As Color = Nothing Public Event FadeStarted As EventHandler - Private Sub UserControl1_SizeChanged(sender As Object, e As EventArgs) Handles MyBase.SizeChanged - TitleLabel.MaximumSize() = New Size(Width, 0) - ProfLabel.MaximumSize() = New Size(Width, 0) - MemoLabel.MaximumSize() = New Size(Width, 0) - 'TopTimeLabel.MinimumSize() = New Size(Width, 0) - BottomTimeLabel.MinimumSize() = New Size(Width, 0) + Private ReadOnly Property HorizontalPadding As Integer + Get + Return ScaleValue(2) + End Get + End Property + + Private ReadOnly Property VerticalPadding As Integer + Get + Return ScaleValue(3) + End Get + End Property + + Private ReadOnly Property NotchHeight As Integer + Get + Return ScaleValue(2) + End Get + End Property + + Private ReadOnly Property CheckSize As Integer + Get + Return ScaleValue(19) + End Get + End Property + + Private doExpand As Boolean = True + Private showChkBox As Boolean = True + Private showMemoText As Boolean = True + Private showProfessor As Boolean = True + Private blackText As Boolean = False + Private hovered As Boolean = False + Private checkHovered As Boolean = False + Private checkPressed As Boolean = False + Private titleHovered As Boolean = False + Private fadeInProgress As Boolean = False + Private deltaColor_R As Integer = 1 + Private deltaColor_G As Integer = 1 + Private deltaColor_B As Integer = 1 + Private titleBounds As Rectangle = Rectangle.Empty + Private titleHoverBounds As Rectangle = Rectangle.Empty + + Private timeFont As Font + Private titleFont As Font + Private titleStrikeoutFont As Font + Private bodyFont As Font + Private memoFont As Font + + Public Sub New() + InitializeComponent() + SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer, True) End Sub - Private Sub TitleLabel_Click(sender As Object, e As EventArgs) Handles TitleLabel.Click - If Name = "DemoCellControl" Then Exit Sub - - Dim appearPoint As New Point(Cursor.Position) - 'SetCourse.Close() - ViewCourse.Close() - - If appearPoint.X + ViewCourse.Width > TableForm.Location.X + TableForm.Width Then - appearPoint.X = TableForm.Location.X + TableForm.Width - ViewCourse.Width - End If - - If appearPoint.Y + ViewCourse.Height > TableForm.Location.Y + TableForm.Height Then - appearPoint.Y = TableForm.Location.Y + TableForm.Height - ViewCourse.Height + Private Sub CellControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load + If Not AutoTextColor = "0" Then + blackText = CheckProperColor(goalColor) + Else + blackText = (_BlackText = "1") End If - '셀 생성 시 보관한 원본 과목 데이터를 사용한다. 클릭마다 파일을 읽고 전체 XML을 - '다시 파싱하던 경로를 제거한다. - ViewCourse.olddata = TryCast(Tag, String) - If String.IsNullOrEmpty(ViewCourse.olddata) Then Exit Sub + ForeColor = If(blackText, Color.Black, Color.White) + alwaysExpand = (_AlwaysExpand = "1") + doExpand = Not (ExpandCell = "0") + showMemoText = Not (ShowMemo = "0") + showProfessor = Not (ShowProf = "0") + showChkBox = Not (_ShowChkBox = "0") + CreateRenderFonts() - ViewCourse.blacktext = blackText + If alwaysExpand Then ForceExpand() - ViewCourse.SetDesktopLocation(appearPoint.X, appearPoint.Y) - ViewCourse.Show() + If Not FadeEffect = "0" AndAlso UsesSharedFadeClock Then + BackColor = If(dayNum Mod 2 = 0, TableForm.MonPanel.BackColor, TableForm.TuePanel.BackColor) + BeginFade() + Else + BackColor = goalColor + End If End Sub - Private Sub TopTimeLabel_MouseEnter(sender As Object, e As EventArgs) Handles MyBase.MouseEnter, TopTimeLabel.MouseEnter, - TitleLabel.MouseEnter, ProfLabel.MouseEnter, Panel1.MouseEnter, MemoLabel.MouseEnter, BottomTimeLabel.MouseEnter, - ChkBox1.MouseEnter, TopNotchPanel.MouseEnter - SetHovered(True) - End Sub + Protected Overrides Sub OnPaint(e As PaintEventArgs) + MyBase.OnPaint(e) + If ClientSize.Width <= 0 OrElse ClientSize.Height <= 0 Then Return - Private Sub CellControl_MouseLeave(sender As Object, e As EventArgs) Handles MyBase.MouseLeave, TopTimeLabel.MouseLeave, - TitleLabel.MouseLeave, ProfLabel.MouseLeave, Panel1.MouseLeave, MemoLabel.MouseLeave, BottomTimeLabel.MouseLeave, - ChkBox1.MouseLeave, TopNotchPanel.MouseLeave - If Not RectangleToScreen(ClientRectangle).Contains(Cursor.Position) Then SetHovered(False) - End Sub + EnsureRenderFonts() + e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit - Private Sub TitleLabel_MouseEnter(sender As Object, e As EventArgs) Handles TitleLabel.MouseEnter - TitleLabel.BackColor = ControlPaint.Light(BackColor, 0.5) - End Sub + Using notchBrush As New SolidBrush(ControlPaint.Light(BackColor, 0.3)) + e.Graphics.FillRectangle(notchBrush, New Rectangle(0, 0, ClientSize.Width, NotchHeight)) + End Using - Private Sub TitleLabel_MouseLeave(sender As Object, e As EventArgs) Handles TitleLabel.MouseLeave - TitleLabel.BackColor = Color.Transparent - End Sub + Dim contentWidth As Integer = Math.Max(1, ClientSize.Width - HorizontalPadding * 2) + Dim y As Integer = NotchHeight + VerticalPadding + Dim checkBounds As New Rectangle(HorizontalPadding, y, CheckSize, CheckSize) + Dim timeX As Integer = HorizontalPadding - Private Sub CellControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load - If CustomFont = "1" Then - If Not CustomFontName = "" Then - Dim fntname = CustomFontName - - ChangeFont(Me, fntname, FontStyle.Regular) - ChangeFont(TopTimeLabel, fntname, FontStyle.Bold) - ChangeFont(BottomTimeLabel, fntname, FontStyle.Bold) - ChangeFont(TitleLabel, fntname, FontStyle.Bold) - ChangeFont(ProfLabel, fntname, FontStyle.Regular) - ChangeFont(MemoLabel, fntname, FontStyle.Regular) - End If + If showChkBox Then + DrawCheckBox(e.Graphics, checkBounds) + timeX += CheckSize + 3 End If - If Not AutoTextColor = "0" Then - blackText = CheckProperColor(goalColor) - ElseIf _BlackText = "1" Then - blackText = True - End If + Using leftFormat As New StringFormat(StringFormatFlags.NoWrap), textBrush As New SolidBrush(ForeColor) + leftFormat.LineAlignment = StringAlignment.Center + e.Graphics.DrawString(StartText, timeFont, textBrush, New RectangleF(timeX, y, Math.Max(1, ClientSize.Width - timeX - HorizontalPadding), CheckSize), leftFormat) + End Using - If blackText Then - Me.ForeColor = Color.Black - TopTimeLabel.ForeColor = Color.Black - BottomTimeLabel.ForeColor = Color.Black - TitleLabel.ForeColor = Color.Black - ProfLabel.ForeColor = Color.Black - MemoLabel.ForeColor = Color.Black - End If + y += Math.Max(CheckSize, timeFont.Height) + VerticalPadding + titleBounds = New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(CourseTitle, titleFont, contentWidth)) + titleHoverBounds = titleBounds - alwaysExpand = (_AlwaysExpand = "1") - doExpand = Not (ExpandCell = "0") - MemoLabel.Visible = Not (ShowMemo = "0") - ProfLabel.Visible = Not (ShowProf = "0") - showChkBox = Not (_ShowChkBox = "0") + If titleHovered Then + Using titleHoverBrush As New SolidBrush(ControlPaint.Light(BackColor, 0.25)) + e.Graphics.FillRectangle(titleHoverBrush, titleHoverBounds) + End Using + End If - ChkBox1.Visible = showChkBox - If showChkBox Then CheckStateUpdate() + DrawWrappedText(e.Graphics, CourseTitle, If(checked, titleStrikeoutFont, titleFont), titleBounds) + y += titleBounds.Height - If alwaysExpand Then - Dim fullheight As Integer = TopTimeLabel.Height + TitleLabel.Height + BottomTimeLabel.Height - If ProfLabel.Visible Then fullheight += ProfLabel.Height - If MemoLabel.Visible Then fullheight += MemoLabel.Height + If showProfessor AndAlso Not String.IsNullOrEmpty(ProfessorText) Then + Dim professorBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(ProfessorText, bodyFont, contentWidth)) + DrawWrappedText(e.Graphics, ProfessorText, bodyFont, professorBounds) + y += professorBounds.Height + End If - If Height < fullheight Then - Height = fullheight - End If + If showMemoText AndAlso Not String.IsNullOrEmpty(MemoText) Then + Dim memoBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(MemoText, memoFont, contentWidth)) + DrawWrappedText(e.Graphics, MemoText, memoFont, memoBounds) End If - If Not FadeEffect = "0" Then - If dayNum Mod 2 = 0 Then - BackColor = TableForm.MonPanel.BackColor - Else - BackColor = TableForm.TuePanel.BackColor - End If + Using rightFormat As New StringFormat(StringFormatFlags.NoWrap), textBrush As New SolidBrush(ForeColor) + rightFormat.Alignment = StringAlignment.Far + rightFormat.LineAlignment = StringAlignment.Far + e.Graphics.DrawString(EndText, timeFont, textBrush, New RectangleF(HorizontalPadding, NotchHeight, contentWidth, ClientSize.Height - NotchHeight - VerticalPadding), rightFormat) + End Using + End Sub - BeginFade() + Protected Overrides Sub OnPaintBackground(e As PaintEventArgs) + Using backgroundBrush As New SolidBrush(BackColor) + e.Graphics.FillRectangle(backgroundBrush, ClientRectangle) + End Using + End Sub - Else - BackColor = goalColor - End If + Protected Overrides Sub OnMouseEnter(e As EventArgs) + MyBase.OnMouseEnter(e) + Dim mouseLocation As Point = PointToClient(Cursor.Position) + checkHovered = showChkBox AndAlso New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize).Contains(mouseLocation) + titleHovered = titleHoverBounds.Contains(mouseLocation) + SetHovered(True) + Invalidate() + End Sub + Protected Overrides Sub OnMouseLeave(e As EventArgs) + MyBase.OnMouseLeave(e) + checkHovered = False + checkPressed = False + titleHovered = False + SetHovered(False) + Invalidate() + End Sub + Protected Overrides Sub OnMouseMove(e As MouseEventArgs) + MyBase.OnMouseMove(e) + Dim newCheckHovered As Boolean = showChkBox AndAlso New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize).Contains(e.Location) + Dim newTitleHovered As Boolean = titleHoverBounds.Contains(e.Location) + If checkHovered <> newCheckHovered OrElse titleHovered <> newTitleHovered Then + checkHovered = newCheckHovered + titleHovered = newTitleHovered + Invalidate() + End If End Sub - Public Sub ForceExpand() - Dim fullheight As Integer = TopTimeLabel.Height + TitleLabel.Height + BottomTimeLabel.Height - If ProfLabel.Visible Then fullheight += ProfLabel.Height - If MemoLabel.Visible Then fullheight += MemoLabel.Height + Protected Overrides Sub OnMouseDown(e As MouseEventArgs) + MyBase.OnMouseDown(e) + If e.Button <> MouseButtons.Left Then Return - If Height < fullheight Then - Height = fullheight + Dim checkBounds As New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize) + If showChkBox AndAlso checkBounds.Contains(e.Location) Then + checkPressed = True + Invalidate() End If End Sub - Sub ChangeFont(ctrl As Control, fntname As String, style As FontStyle) - ctrl.Font = New Font(fntname, ctrl.Font.Size, style) + Protected Overrides Sub OnMouseUp(e As MouseEventArgs) + MyBase.OnMouseUp(e) + If checkPressed Then + checkPressed = False + Invalidate() + End If End Sub - 'Private Sub TopNotchPanel_Paint(sender As Object, e As PaintEventArgs) Handles TopNotchPanel.Paint - ' TopNotchPanel.BackColor = ControlPaint.Light(BackColor, 0.3) - 'End Sub + Protected Overrides Sub OnMouseClick(e As MouseEventArgs) + MyBase.OnMouseClick(e) + If e.Button <> MouseButtons.Left Then Return + + Dim checkBounds As New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize) + If showChkBox AndAlso (checkBounds.Contains(e.Location) OrElse e.Location.Y < titleBounds.Top) Then + ToggleCheck() + ElseIf titleBounds.Contains(e.Location) Then + OpenCourseDetails() + ElseIf showChkBox Then + ToggleCheck() + End If + End Sub Private Sub SetHovered(value As Boolean) - If hovered = value Then Exit Sub + If hovered = value Then Return hovered = value + Dim previousBounds As Rectangle = Bounds If hovered Then - Dim fullheight As Integer = TopTimeLabel.Height + TitleLabel.Height + BottomTimeLabel.Height - If ProfLabel.Visible Then fullheight += ProfLabel.Height - If MemoLabel.Visible Then fullheight += MemoLabel.Height - BringToFront() - If doExpand Then - '확장을 하려는데 아래공간이 모자랄때 - If Location.Y + fullheight > TableForm.TimeTable.Height Then - Height = fullheight - Location() = New Point(0, TableForm.TimeTable.Height - fullheight) - ElseIf defHeight < fullheight Then - Height = fullheight + Dim fullHeight As Integer = GetRequiredHeight() + Dim parentHeight As Integer = If(Parent Is Nothing, Height, Parent.ClientSize.Height) + + If Location.Y + fullHeight > parentHeight Then + Height = fullHeight + Location = New Point(0, Math.Max(0, parentHeight - fullHeight)) + ElseIf defHeight < fullHeight Then + Height = fullHeight End If End If - Else - '쪼그라들때 - If doExpand And Not alwaysExpand Then - Height = defHeight - End If - - Location() = New Point(0, defLoc) - 'If Not Name = "DemoCellControl" Then TableForm.DrawTablePattern(dayNum) + If doExpand AndAlso Not alwaysExpand Then Height = defHeight + Location = New Point(0, defLoc) + End If + Invalidate() + If Parent IsNot Nothing AndAlso previousBounds <> Bounds Then + Parent.Invalidate(previousBounds) + Parent.Invalidate(Bounds) End If End Sub - 'Private Sub TopTimeLabel_SizeChanged(sender As Object, e As EventArgs) Handles TopTimeLabel.SizeChanged - ' TopPanel.Height = TopTimeLabel.Height - 'End Sub + Public Sub ForceExpand() + Dim fullHeight As Integer = GetRequiredHeight() + If Height < fullHeight Then Height = fullHeight + End Sub + + Private Sub ToggleCheck() + checked = Not checked + If Name <> "DemoCellControl" Then ModifyCheck(Name, checked) + Invalidate() + End Sub - 'Private Sub TopPanel_SizeChanged(sender As Object, e As EventArgs) Handles TopPanel.SizeChanged - ' ChkBox1.Width = TopPanel.Height - 'End Sub + Private Sub OpenCourseDetails() + If Name = "DemoCellControl" Then Return - Private Sub ChkBox1_Click(sender As Object, e As EventArgs) Handles ChkBox1.Click, TopTimeLabel.Click, - Panel1.Click, ProfLabel.Click, MemoLabel.Click - If showChkBox Then - checked = Not checked - If Not Name = "DemoCellControl" Then ModifyCheck(Name, checked) - CheckStateUpdate() - End If + Dim appearPoint As New Point(Cursor.Position) + ViewCourse.Close() + If appearPoint.X + ViewCourse.Width > TableForm.Location.X + TableForm.Width Then appearPoint.X = TableForm.Location.X + TableForm.Width - ViewCourse.Width + If appearPoint.Y + ViewCourse.Height > TableForm.Location.Y + TableForm.Height Then appearPoint.Y = TableForm.Location.Y + TableForm.Height - ViewCourse.Height + + ViewCourse.olddata = TryCast(Tag, String) + If String.IsNullOrEmpty(ViewCourse.olddata) Then Return + + ViewCourse.blacktext = blackText + ViewCourse.SetDesktopLocation(appearPoint.X, appearPoint.Y) + ViewCourse.Show() End Sub - Private Sub Chk_Enter(sender As Object, e As EventArgs) Handles ChkBox1.MouseEnter, TopTimeLabel.MouseEnter, - Panel1.MouseEnter, ProfLabel.MouseEnter, MemoLabel.MouseEnter, ChkBox1.MouseUp, TopTimeLabel.MouseUp, - Panel1.MouseUp, ProfLabel.MouseUp, MemoLabel.MouseUp - If showChkBox Then - ChkBox1.BackColor = ControlPaint.Light(BackColor, 0.3) - End If + Private Function GetRequiredHeight() As Integer + EnsureRenderFonts() + Dim contentWidth As Integer = Math.Max(1, ClientSize.Width - HorizontalPadding * 2) + Dim result As Integer = NotchHeight + VerticalPadding + Math.Max(CheckSize, timeFont.Height) + VerticalPadding + result += MeasureTextHeight(CourseTitle, titleFont, contentWidth) + If showProfessor Then result += MeasureTextHeight(ProfessorText, bodyFont, contentWidth) + If showMemoText Then result += MeasureTextHeight(MemoText, memoFont, contentWidth) + result += timeFont.Height + VerticalPadding + Return result + End Function + + Private Function MeasureTextHeight(value As String, fontToMeasure As Font, width As Integer) As Integer + If String.IsNullOrEmpty(value) Then Return 0 + Return TextRenderer.MeasureText(value, fontToMeasure, New Size(Math.Max(1, width), Integer.MaxValue), TextFormatFlags.WordBreak Or TextFormatFlags.NoPadding).Height + End Function + + Private Sub DrawWrappedText(graphics As Graphics, value As String, fontToDraw As Font, bounds As Rectangle) + If String.IsNullOrEmpty(value) OrElse bounds.Height <= 0 Then Return + Using textBrush As New SolidBrush(ForeColor), textFormat As New StringFormat() + textFormat.Trimming = StringTrimming.EllipsisCharacter + graphics.DrawString(value, fontToDraw, textBrush, bounds, textFormat) + End Using End Sub - Private Sub Chk_Down(sender As Object, e As EventArgs) Handles ChkBox1.MouseDown, TopTimeLabel.MouseDown, - Panel1.MouseDown, ProfLabel.MouseDown, MemoLabel.MouseDown, ChkBox1.MouseLeave, TopTimeLabel.MouseLeave, - Panel1.MouseLeave, ProfLabel.MouseLeave, MemoLabel.MouseLeave - If showChkBox Then - ChkBox1.BackColor = Color.Transparent + Private Sub DrawCheckBox(graphics As Graphics, bounds As Rectangle) + If checkHovered OrElse checkPressed Then + Using hoverBrush As New SolidBrush(ControlPaint.Light(BackColor, 0.3)) + graphics.FillRectangle(hoverBrush, bounds) + End Using End If - End Sub - Private Sub CheckStateUpdate() + Dim image As Image If checked Then - If blackText Then - If TableForm.currentDPI = 96 Then - ChkBox1.Image = My.Resources.check1_b_96 - Else - ChkBox1.Image = My.Resources.check1_b - End If - Else - If TableForm.currentDPI = 96 Then - ChkBox1.Image = My.Resources.check1_w_96 - Else - ChkBox1.Image = My.Resources.check1_w - End If - End If - TitleLabel.Font = New Font(TitleLabel.Font.Name, TitleLabel.Font.Size, FontStyle.Strikeout) + image = If(blackText, If(TableForm.currentDPI = 96, My.Resources.check1_b_96, My.Resources.check1_b), If(TableForm.currentDPI = 96, My.Resources.check1_w_96, My.Resources.check1_w)) Else - If blackText Then - If TableForm.currentDPI = 96 Then - ChkBox1.Image = My.Resources.check0_b_96 - Else - ChkBox1.Image = My.Resources.check0_b - End If - Else - If TableForm.currentDPI = 96 Then - ChkBox1.Image = My.Resources.check0_w_96 - Else - ChkBox1.Image = My.Resources.check0_w - End If - End If - TitleLabel.Font = New Font(TitleLabel.Font.Name, TitleLabel.Font.Size, FontStyle.Bold) + image = If(blackText, If(TableForm.currentDPI = 96, My.Resources.check0_b_96, My.Resources.check0_b), If(TableForm.currentDPI = 96, My.Resources.check0_w_96, My.Resources.check0_w)) End If + + graphics.DrawImage(image, bounds) End Sub - Public Sub ModifyCheck(name As String, checked As Boolean) + Public Sub ModifyCheck(name As String, value As Boolean) Dim data As String = readTable() - Dim olddata As String = TryCast(Tag, String) - If String.IsNullOrEmpty(olddata) Then Exit Sub + If String.IsNullOrEmpty(olddata) Then Return Dim newdata As String = olddata - - If Not newdata = "" Then - If newdata.Contains("") Then - Dim tmp As String = "" + getTableData(newdata, "checked") + "" - newdata = newdata.Replace(tmp, "" + checked.ToString + "") - Else 'check 데이터가 없을때 - newdata += vbTab + "" + checked.ToString + "" + vbCrLf - End If + If newdata.Contains("") Then + Dim tmp As String = "" + getTableData(newdata, "checked") + "" + newdata = newdata.Replace(tmp, "" + value.ToString + "") + Else + newdata += vbTab + "" + value.ToString + "" + vbCrLf End If writeTable(data.Replace(olddata, newdata)) @@ -298,45 +354,49 @@ Public Function AdvanceFade() As Boolean If Not fadeInProgress Then Return False - Dim R As Byte = BackColor.R - Dim G As Byte = BackColor.G - Dim B As Byte = BackColor.B - - If goalColor.R - deltaColor_R > R Then - R += deltaColor_R - ElseIf goalColor.R + deltaColor_R < R Then - R -= deltaColor_R - Else - R = goalColor.R - End If - - If goalColor.G - deltaColor_G > G Then - G += deltaColor_G - ElseIf goalColor.G + deltaColor_G < G Then - G -= deltaColor_G - Else - G = goalColor.G - End If + Dim red As Byte = MoveToward(BackColor.R, goalColor.R, deltaColor_R) + Dim green As Byte = MoveToward(BackColor.G, goalColor.G, deltaColor_G) + Dim blue As Byte = MoveToward(BackColor.B, goalColor.B, deltaColor_B) + BackColor = Color.FromArgb(red, green, blue) - If goalColor.B - deltaColor_B > B Then - B += deltaColor_B - ElseIf goalColor.B + deltaColor_B < B Then - B -= deltaColor_B - Else - B = goalColor.B - End If - - BackColor = Color.FromArgb(R, G, B) - TopNotchPanel.BackColor = ControlPaint.Light(BackColor, 0.3) + If goalColor = BackColor Then fadeInProgress = False + Return fadeInProgress + End Function - If goalColor = BackColor Then - fadeInProgress = False - End If + Private Function MoveToward(currentValue As Byte, targetValue As Byte, delta As Integer) As Byte + If targetValue - delta > currentValue Then Return CByte(currentValue + delta) + If targetValue + delta < currentValue Then Return CByte(currentValue - delta) + Return targetValue + End Function - Return fadeInProgress + Private Function ScaleValue(value As Integer) As Integer + Return Math.Max(1, CInt(Math.Round(value * DeviceDpi / 96.0))) End Function - Private Sub TopPanel_Paint(sender As Object, e As PaintEventArgs) Handles TopPanel.Paint + Private Sub EnsureRenderFonts() + If timeFont Is Nothing Then CreateRenderFonts() + End Sub + + Private Sub CreateRenderFonts() + DisposeRenderFonts() + Dim familyName As String = If(CustomFont = "1" AndAlso Not String.IsNullOrWhiteSpace(CustomFontName), CustomFontName, Font.FontFamily.Name) + timeFont = New Font(familyName, 9.75F, FontStyle.Bold) + titleFont = New Font(familyName, 11.25F, FontStyle.Bold) + titleStrikeoutFont = New Font(familyName, 11.25F, FontStyle.Bold Or FontStyle.Strikeout) + bodyFont = New Font(familyName, 9.75F, FontStyle.Regular) + memoFont = New Font(familyName, 9.0F, FontStyle.Regular) + End Sub + Private Sub DisposeRenderFonts() + If timeFont IsNot Nothing Then timeFont.Dispose() + If titleFont IsNot Nothing Then titleFont.Dispose() + If titleStrikeoutFont IsNot Nothing Then titleStrikeoutFont.Dispose() + If bodyFont IsNot Nothing Then bodyFont.Dispose() + If memoFont IsNot Nothing Then memoFont.Dispose() + timeFont = Nothing + titleFont = Nothing + titleStrikeoutFont = Nothing + bodyFont = Nothing + memoFont = Nothing End Sub End Class From 635282f2613423ea99dbedcdee05fde5494baad6 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Wed, 19 Aug 2026 16:28:57 +0900 Subject: [PATCH 04/24] refactor: adjust rendering properties and update resource files for improved UI consistency --- utable/Forms/TableForm.Designer.vb | 1 - utable/Forms/TableForm.vb | 2 +- utable/My Project/app.manifest | 2 +- utable/Resources/check0_b.png | Bin 1955 -> 1637 bytes utable/Resources/check0_b_96.png | Bin 2019 -> 1623 bytes utable/Resources/check0_w.png | Bin 1955 -> 1640 bytes utable/Resources/check0_w_96.png | Bin 1762 -> 1636 bytes utable/Resources/check1_b.png | Bin 2226 -> 1747 bytes utable/Resources/check1_b_96.png | Bin 1918 -> 1713 bytes utable/Resources/check1_w.png | Bin 2230 -> 1748 bytes utable/Resources/check1_w_96.png | Bin 1912 -> 1717 bytes utable/Resources/unused/check0_b.png | Bin 0 -> 1955 bytes utable/Resources/unused/check0_b_96.png | Bin 0 -> 2019 bytes utable/Resources/unused/check0_w.png | Bin 0 -> 1955 bytes utable/Resources/unused/check0_w_96.png | Bin 0 -> 1762 bytes utable/Resources/unused/check1_b.png | Bin 0 -> 2226 bytes utable/Resources/unused/check1_b_96.png | Bin 0 -> 1918 bytes utable/Resources/unused/check1_w.png | Bin 0 -> 2230 bytes utable/Resources/unused/check1_w_96.png | Bin 0 -> 1912 bytes utable/UserControls/CellControl.vb | 6 +++--- utable/uTable.vbproj | 5 ++--- 21 files changed, 7 insertions(+), 9 deletions(-) create mode 100644 utable/Resources/unused/check0_b.png create mode 100644 utable/Resources/unused/check0_b_96.png create mode 100644 utable/Resources/unused/check0_w.png create mode 100644 utable/Resources/unused/check0_w_96.png create mode 100644 utable/Resources/unused/check1_b.png create mode 100644 utable/Resources/unused/check1_b_96.png create mode 100644 utable/Resources/unused/check1_w.png create mode 100644 utable/Resources/unused/check1_w_96.png diff --git a/utable/Forms/TableForm.Designer.vb b/utable/Forms/TableForm.Designer.vb index ce255d6..0e6fbfa 100644 --- a/utable/Forms/TableForm.Designer.vb +++ b/utable/Forms/TableForm.Designer.vb @@ -487,7 +487,6 @@ Partial Class TableForm Me.BT1_menu.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BT1MenuTitle, Me.ToolStripSeparator4, Me.ClearCheckBoxItem, Me.ChangeThemeItem, Me.ColorSettingItem, Me.OpacitySelectItem, Me.GetFromETItem, Me.ToolStripSeparator2, Me.TopMostItem, Me.SnapToEdgeItem, Me.ShowMemoItem, Me.ToolStripSeparator1, Me.OptionItem, Me.ExitItem}) Me.BT1_menu.Name = "BT1_menu" Me.BT1_menu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System - Me.BT1_menu.RightToLeft = System.Windows.Forms.RightToLeft.Yes Me.BT1_menu.ShowImageMargin = False Me.BT1_menu.Size = New System.Drawing.Size(202, 374) ' diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index c691abd..e63f39d 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -18,7 +18,7 @@ Public Class TableForm Dim courseRecords As New List(Of TableCourse) Private ReadOnly cellsByCourse As New Dictionary(Of TableCourse, CellControl) Private courseLayoutQueued As Boolean = False - Private WithEvents courseFadeTimer As New Timer With {.Interval = 10} + Private WithEvents courseFadeTimer As New Timer With {.Interval = 30} '슬라이딩 애니메이션용 변수 Dim poscount As Integer = 0 diff --git a/utable/My Project/app.manifest b/utable/My Project/app.manifest index f7f79d0..7147613 100644 --- a/utable/My Project/app.manifest +++ b/utable/My Project/app.manifest @@ -52,7 +52,7 @@ - false + true diff --git a/utable/Resources/check0_b.png b/utable/Resources/check0_b.png index d176368887181dc8bae8edce67b9c00daf3107ef..d7105607ed22d69dd6bf6b93022c7ec6b196bcfa 100644 GIT binary patch delta 596 zcma)(yGz7C5XN&!eBuKu!9vgT3PtyBca!Yx2E+#mdW9mQVjHgfWa3wDhQ$| zn##h;%0{sh1RE>CUJy$a8~=sFXl0>t12bPSKR)$ zDlG(&gi#K-NqoQ!GXS2)G2kARlt@!zA4ZPW*@g^53I+khrU0fiEkG$_Kp^f5kI|Uq z(Xp0JQ^7bSrUXX73_yrU0F?s8w6p>&42uLLdeX8H*2mpa;8b!cbeBVWr4Y>j8aaB< z!T0r!%UrjVJf%BE2j%P2=fW?FIPS3mUuHQ}^_U=_YR$VFy sZS(Xcp-(lBYU+`e(L20mX8jEC2ui delta 796 zcma))Pe@cj9LM+1b2Up;DvGRpZ(C5&acAbu%=-h9xx45d6cZ`POYb@J*oATzcg+aW zD59c*l^0RBOoO@z3lS(C@)ApR2`@+$wYJEOmU9vAe)T3SI~FGb_v^cN zl|uOG)MBGQJ^u5AG+*6qHptx5C%P@8RHqw;4A7YGn5L+?D3={>|*_H~;zT Vx9+@KklH*izGPw`zR+{>;xEgi-E{x} diff --git a/utable/Resources/check0_b_96.png b/utable/Resources/check0_b_96.png index 8cfb82242afe6682674d4277dd06d091ac4cfcf1..6e3ce4daa471c3d66de4fed803c74cffc20c1490 100644 GIT binary patch delta 620 zcmaFNf1O9MGr-TCmrII^fq{Y7)59eQNQ;0l2OE&=?Ud!6s3^|FdUB#wkA|h5p>DE) zfsul_rJlJ(lDV0&g^q%efsv`cfswwUp{}8&m7#%^fyKtR?2MB&nKUMUU^JZ^$Ry8V zWTt02xtvMf)=byH0;nv+(98;m3=FjmEUgR-loTp*3#@!oOA=EOOA=iYOHyr>fX1RL zo_v8xS^z`vHrv|)?*2CCC=F564$a4tMZK0JnZ4n$f^qo(qAmDuEh9V zNr7l1TiI+UC$QTFlvW>j4NL$go-U3d7QM*{5`_(doqy*`Ju?VQ;0Qa=blyyfp>6$& tcZUL*-FaoEu_c|Znj#`}rTpOoc7}W}W<|N*=3PMj44$rjF6*2UngCd%sZsy{ delta 897 zcma)5K~EDw7%go9(?Ea_BZvvxO%oLvIWRdQ@j$?-5FsH^4m0y*^5%VS-kbT@_jw@usJb}c zpShSxBoh6Vo24bS4tK`69yPbB*4ypug=FvDZD;E;nX*8DFwtx_g)9>@HmMm9(LAJi zWCCP47&?aWVz;+TW|K(%=$e#E$?@J|u>)grJvr_`04XroGJ=kYuR-QOsA~`EO=m&G z9`|Ce;WIc5LgrnOo&00X6dFT za#Z-H2nhkck`I`vfQ)erP(W#53fuCzC!eNrIR+_$HnxFD5CmK(pocI6BoLu*o6s-> zk-MqUg4SG(;;6kEHJwVaSZzgNZB5B)Uv8$yh zSg(slJlhCFJC0iIf;Qdaq2qchN7Y)b;PJ>84v&HwcaD#mgcIAu7zmXj2)T-raiU&O z5Fi=~!^0d+=R0JILOZMuX)ooESxyO%Z@O>Hb?=QmQU7D2Qkq?+L&2|ugX^i6FtyN^ v+C2E;zZ#v(jF7bt&u_inAK2L{e9z1$-hbU6{Pt&b~$JsOsJhPufH z21W|zmU`wEN#21cg(21fdZhPsB9R)z*v1{NFNHZ!sqndzBME@zU@Fw-@# z07``znpqi{TA5gC8(3Ny7$_-JazN%G`L%ql7tDQQWTDak3iNfs6cx`qZO$+}5t zDdxH+M&>{>%?-`c42&n=V%DE*#1iUCjAA8);u6=g607oz)I6+yo_vNy7aV+(SY2I- z@euJQPVQr?N_07Rz5y7wuAVNAArXh)Uf(Flpuls)VAKB#=b5%R@IB$YvTIV$IZ=fK z=Lf!pj7)kBTLncNkidcajmKm^m~04bIM1xk8nAYwtf~7kpn(jYu6{1-oD!MF|7$4VZ3sYLrC^FXBbs@{1`~J_o0ZH9mbPp}IY()goIP<0z(v@~fsl!Gh z78I<^DLh4cF1!d8v{A3G1kk0jVRM}(Tg~tLY&kvL`D)C zNkjnRCWR)(i$7)>_NG$xE8$-cb@o934S0IVO*OYpEOTP&vGo!N?V@n zi(`nl7aJzz{@YMTWXIuVD4yVixY&jW*bHJo7!|-m(g92|iMU|U=C(iBcp^cV!*oI^ zkc1)NI#dV3VF9OYBg2*0q7L$_jfn)7m@jmd6p zvRLv;Opp&H?U4epo%1*$r!4D|?w6o;g*N6FY3E zd*(k8o2 HDE) zfsul_rJlJ(lDV0&g^q%efsv`cfswwUp{}8&m7#%^fyKtR?2MB&nKUMUU^JZ^$Ry8V zWTt02xtvMf)=byH0;nv+(9Fuv)XLOA+rZMwz(7f%BDcWGH?<@&C9x#YC9x#cRtab< zy5h-anWP0U1ivyVsU)YRB$=gJ80n^1CZ*{bnwc2uTBey9=q8z&rJ5RAq!}ir7*Dol z)>28cFgH)NG&9jPuuL`3wM;QF(X~u9vD8hnNJ>jgO-{BnPf42G$*f{!Y?)@BYG{zG zYo2UsqHAGcVxenkYMQ8PV4Rv}YHX00YG`7nq>z$qm6l(Wn^tiC@yg=E3qoiNX^6U<>@TCkkC8L;_6C_HYom@wj@O@a9LX21v`TCfWHhKT{h^$C(ZRD!#EEUJDz91Mapmv^1{D0uMZy%!O^c^0o0yod)&-Oyqw3idEFkKupcoA4 zIWu}{G?7STDp$(Y_#7XsW9fMBE;;WvawoL(%7*{&tUcoZj)<+h-VAaq;(60LhKQPw zn#cmk@d@-XezQF?q;eWk--o7ERm+ad&ktmtTGO&F1dsykEhFe#C*%sk? z@0?+(T5Gszg@2H0*}`@$LV%3z1A&Nr^n@MHjdMf;i5U^7U=Cn0Lf?z_uxHEz z%3lvqTf>*i>D9us_)AVz%5%%~IRCl3dolSICSQ$hrao@L>O1G==a+2r{!aGB&b~$JsOsJhPufH z21W|zmU`wEN#21cg(21fdZhPsB9R)z*v1{NFNvNKNBWYU=YfzfnwAd@_c zk(r+9qY3^QE=3!t(PLo+KQQ!66_Z39ay0|O<6irfM#-_(-Cl*E!mm&B4(TP2{e zcopZTWTsW(R66+|ld_VfrDal@S(0w5NwS5mshNqnu0>i}if(F(v9XDHqNSN-+GJ;D zZIzTXQ`01KplvCsi59w+X(oxf7N%*5x~9gdMh2#q$rfn_Ns}irs|1-DCtD;Nr&{VJ zTBHIEG&V8QO)>&{+rT(AInBr*DJj)BNl76k*(xo+C^xahRw*+#F+EkkATM1>p&%o_ zB)>Q#zre~lKPSJ)H$MgFN8`yHEMcz18CzW9T2^9Jo{^e|HEbp)vg$&D`8tc63t^vI zDk%_c;#5}K$pvh-^~>wE-T~9Z22U5qkch)?Z*&VbDDW@`Z2o_tkZG%YMsbALi^UgY zy1Z)=8<<5{f>s7{IcSs%oO17J-}Qk>o2}FTt4PE8ue%!mSpD)-&@JE0?eY3T8N*AD zDMbso!+A>{Yc(pyh+gPQK0cwM;PkQ?Zac)cvt+!@W0}t8*1c?9u%#y5 delta 1219 zcmcc2yGc;7Gr-TCmrII^fq{Y7)59eQNE?AL2OE%-v+vkAQBjBw7Hq86}xmm>Q=grdb*pO`gE4qGFL^kZhKiWUiZN zZkP&^PSmxuFf!ClvoJ9;Pc}0$Of@o@{Ek^tR3RnVDlNY#H?hQ4DKj@QJypLTFMTo_ zOPDJ$Mkpy1m$;UdSe0j_=3)2oLKa3mTHt}l45A14>AmB zgaU*GF;+$&VG4!=CLd>YsNW*8=Sd#Nt)sHjnS%k+?< zlY#FZL{{a_|8v5&e7elM=NI`KTO&F^NP?3ws$q80nj@^4%yC{D#h%O77y7!-;EB8( zsO)$`?LkJ*M~8pT3%ag6Y7)74gK6jYrF*Iio#(U&xXZT1+^q0Wc)oH`o!2p^B{9to zzKuVa!g^agDvcas8!NuQzHHGkC0S%gpu)Wd%jE9ZKlO4uf)(a9HgP2$x}e42%lb(0 zMEC2CTE`M0r#sA-A4#>9ta)=+p+5S6hszP34~ra38t3#gtnzf!TJ&CvDT(n%fI?p5 znwAWPJfR799qN4>BUtu{c4Q|+%<7YVRJ?YAPQjh0P3No?rZxDpF5*jBvWIu2zjlgI z%A9zwj;9ljH*zN&m_N7Sr^Ehl3#W3=tn_Orec=9auCeCy`*W5kG9)W<-&()uy+c&J zL~#3$!*3QIUw)t~DaeDD$$-_y)n$s)vKJwW48n@0!mNobDr?eC35M4xE8NXWTi#wf z!%$#DE) zfsul_rJlJ(lDV0&g^q%efsv`cfswwUp{}8&m7#%^fyKtR?2MB&nKUMUU^JZ^$Ry8V zWTt02xtvKp!%Ww}0;nv+(98;mj4iYcEUgR-loTp*3#@!oOA=EOOA=iYOHyr>fX3og zoS%}JR*6&T1UPQ%n=hQ&J~SVpa(uyW4N$uIKFPXYSUcrph|m@9F{7MHk|l~|Q$q~>7_oym!;x{!dr&f@Ay zjNg?Mh&FO6tL@}HY_|3GT{+8u$)eBG#WBR9_w1EKz6J#jwt#)FuEnjneQ|z)g(G*) z>9uFQ?z;#Y7_ZH_9rTua!i<^kHdua`;eHvZ&`71B%QO}~3s&(&zbv8<55 z3r7|$=HZ~i@o%XO`$Q|n*^8z#njC+>CgA_|0l@85=MtSSVZd|GSY*G`NQy?&+q%4A2VO` z`%q{y-FvR4`e?PsT*1Zt8rzgkt{hP=rAUeW6Id7G=cx;A90JLGLnKmZjWsr?9QIIcnY z3{*7;!@<#9LF+Lco$HRC)EzSsmZ0>%*nRJ9tlf3^%NvfULNc(SP!JcC1JxuP7=&R! ziLOwA1!tJKoxal%&N0EJOaMo50VpCSP>{@k%;PerlFle6?u;)I!4jk@RS{qY#()^a z04h`|;3k4XF@%U4lDq0VayZDDChTHPcM@SMr)SMrA)j>v{!D5=h;Xpvq=p@BWYEm- zo%x<0)NQ|D@YH~n=k`dcB^+}@{swo=A4=^f`o60C?bCE+VyrM|J9cT%E@(-c4du+d z(~)Op$+3&2_F!9)3$0VnZtt#G?K-y&Q{y(XxVCc@Q%>WU5MW}8fZ!w!R8GqcWDF_7 zlyt&TTWkYXMr={HS!26l24qmDvJ6zpAt0DApeup{$sh{FLKmte{R7*?Zh4IDINB@B zrZQ=kSkWptmYpvx3=^3N;?EO1W w)-Gor4vHpc>gtz)hWa5L9AEc+U3`ZszODb57+q$!%SAoisX%Y?Y3HT!KLGC?82|tP diff --git a/utable/Resources/check1_w.png b/utable/Resources/check1_w.png index da392647cc5a0f7455709b9b0d5b43f6a14aadd4..e85ea7673d3af29689704a8b52932b7ce158b918 100644 GIT binary patch delta 768 zcmdlcc!gK7Gr-TCmrII^fq{Y7)59eQNNa#F2OE&=`L`o?qM|qx>&b~$JsOsJhPufH z21W|zmU`wEN#21cg(21fdZhPsB9R)z*v1{NFNvNKNBWYU=YfzfnwAd@_c zk(r+9qY3^QE=3!t(PLo+KQQ!4`_Z39ay0|O<6irfM#-_(-Cl*E!mm&B4(TP2{e zcopZTWTsW(R66+|lafkus!@u$p{b#+nW;gdu6eSFg>F)!ak6eAkdAW--}? zSxd#-+`z=b(kxlm(!|IVsM;h^H_^~CO*hdpG0ngbYiXI7q-$wvnP!k^X>Mj>lBlGRl5CZhUzD3zVyl#yo0y)eUyzrsq)?EN zUy@&(kzZitoS&0lyC5oJx60Q8a)manXYFpuwE~9V*TRvEJ2_0!WcDuOIat( zI(3q5Rl!XMQ$68~L#d2QEy7w{nz_9VTMKmcSTyd&a-ZYf!gtr!$4?``C}#Gxf(1|a zalH0DHaUsyK_8QD<1Jfteo9fUaloboFyt=akR{0JGxl&;S4c delta 1226 zcmcb@yG>BBGr-TCmrII^fq{Y7)59eQNE?AL2OE%-v+vkAQBjlbra1^(sYxOlT&m}Qq58m4a|+rl2cMA zJ27ji7+EHpC!3p@>86<{8tR%@m?Y^Y8Ks!%T3DE-ni-j-8K#<=PM*N5qLP@HVqu(? zY^s}Tnv$w(X=!4tn`8)bQIesVS+be2Wuke?8bh! zdFhkcSi)S1F+xe9xWu)r#Hu_aH4nR=7qaLILqkwOBRn%NCBM8_Q%QfaB8$Z2Z!C(g z#CV-3Tj253%W7L+kda@KU!0L&VC9mZT$-DjSK^qLoLXFxUsPZYXtqsB7D6c~od$%YnbsYZz=DTX%s zAj5!0C_q>cV`cOareHW=@^My&dfA53zYGkFZ#`WcLo)8Yy=m*sY$(D0A$W~geAl7} zk4`I<1ni8J`NQI;bgqt}-B0Yzqg6^x9YKP3j;wp677?JXskB2wiR12)`3@HI9r%)V z*zC0av`png{H%LVe>xWJoUQ;g#a`uu0RKl23Rl$ixSb@%wC-tZiBfg0rbm57rDE=M zfqGsgmlZ8*oSb?kpLE@8owFk1jm?rZUtfKe-gvfW_fjb#DW^B8K08-^7T_!FSTnzC zvjE!#-j&K7{3jMo7GMi(n4{bgo>LpuYS2-4LbR!((Z_Q44b3E$qGkm-#m9FH&-v;G zB?vp-5zdI&^NQEdq-95I&&5O4C2!u;E1i&9qOY37^r=OGV}pqDiF0{eL5pj}HP4wd zS2_z^pPw?gUX!%XGp0y>YW?$EW8r}dCM_urn-^z33tXIa z^F)FCGai*Vw%RatEnzI{T)H#Y@GTh>vW5QqfbhFbP tD=md>_FpD!332#i?gEJge!-{g%%0}|EN!}YX99~S22WQ%mvv4FO#qM~t9$?e diff --git a/utable/Resources/check1_w_96.png b/utable/Resources/check1_w_96.png index 99d128f47eb9c907fcacc80b213bb25f95323c87..e6ae3c55ec5edfd243cf29c14ee56be1d21ae7fa 100644 GIT binary patch delta 735 zcmeytx0P41Gr-TCmrII^fq{Y7)59eQNQ;0l2OE&=?Ud!6s3^|FdUB#wkA|h5p>DE) zfsul_rJlJ(lDV0&g^q%efsv`cfswwUp{}8&m7#%^fyKtR?2MB&nKUMUU^JZ^$Ry8V zWTt02xtvKp!%Ww}0;nv+(98;mj7+r+EUgR-loTp*3#@!oOA=EOOA=iYOHyr>fX3og zoS%}JR*6&Tp&%o_ zB)>Q#zre~lKPSJ)H$MgFN#n^JEMcz18CzW9T2^9Jo{^e|HE<>;vg$&@`8tc63lUyd zQXtyIsjRk>_psU4FZ<5Z4ono2JY5_^EPA(2xyXA!fx~5CS+;ob?$^_Kl`>qbJ3URm za#kH+c(G*N;w}3dIeMI2Rw^>BNDgIn5cYb54}1uo$1 zW{SPR5s~|mb=qzFs@oadk=s^fN1p9;)pefxMs%l7@O#O73!ffesqdcNf9TwH?bShU q7X%7cJIdw98y&j3Ea6o}3;#cXwZHC9zjO=eJO)o!KbLh*2~7aoBi{}H delta 925 zcma)*Pe>F|9LM*s#^OdKm1W`Xx=@zSee>R%*_pu=cO5YgiI^3gYUaH+y5R0CJEI02 z6mMZs+gv(yC^{GvfmA1n>R=GnL5OY!Ap~|1qOzzCjep2U4<6ooKOVpL`Tf4XFYj^m z$Iker-kz>#X{3slWi)XRs(770*+h_STNB1o7h zN^=ZBK~6zAC14;(DuF6O(@WKUHy#k&&;Dk&FVIlk(Xo=IxWj>l33-?i!Tcc8IRrV?ke2@uB!C#VWOFauJki<0Eo#ULPU zV%>B9=*wWOFn(8^j@V^?SNL6fu@b_DIvNGO>PgXML qaDDc}kp1Y!^{L8>oA+mze;%}w@rPeNo{cVg<$T?pJ=%lZ`H4TA{$tHBYyY8O71#@1v3q9?Nlgwl{+-@c& z+1tGXs|cdvOCLm3eDF^NeJK<~s0y|(a;R^8DvI<$`X&YOT_^j`v)&y?!G&xlGv9pk z{eHi1X5Ly}I(6v4BL^f&I#gY()WjR*&-9e|e|+}M%i^^^UVJ{4q=OIU&xG{BhYw5A z)DK~OC0lWyb$s3~5XwDP=(S^kmZTH2y_oo`ER#Lf2&0m6`6Mf|uOr%dx2ni}NVZy1=JN&R6?a)K z^MuK`U;-Z+Shnl}Gpl9UU!3 z{qfwmleGGgQeR~)))uf-2!rFL;wH~{+T=ULF&-XA6FcREk&GvG&Re5GEst2_ayc)I zEkAiSj41D>$8u4_Ijll5R#K3LG@um$D%LgI!O%hGQ=*}11E?#kKuJda1BRM|k%RPq zfjJF>^}Rq{*QrKnMk1e8DIh;4z;8)5C#HHCu-!?CTQq zE8Oqo8O-MooJ+YcIL1kGEDshb);)koPy`qn2oOeuK`}KotXY;}4Wblm&n2PQJfyXi zHCIIiBVx$F5F%h1C;+x?6oHL(3y>marirjlb*9MFcLJWYiLgr8CJm;>QA3gcQB&qE zp17QfoaK0V=@U`9g*exEQ9Q%(15nvWP)@TmkeMj53(*v*nC5jWG_ZkchaAdwcXW2 z=9xALG++pC1B?v|c!6$$BC`xkbuXX>9!39usUIfSZ<46N#8Im%cOTE*Ous`l?%alV zS&i-EQS(D~U^Vql7xu@t9LS=a{Vs9J=gsl;B0k0!6pKWv5^?=pd)*Di)%8lXGGA~0 z`umN`hozb6dryB2zkKw^uMXDE-TeIO;x+%>_rcB2jzIm33(~~C$>|GE%$&b|{kL2D zH~%<(rL*so`{y3ooI>B8zjAmYO#XUd^5T7S&I9t?t9|gOv3Yxj-TDaK^VrYay!g|( lkH7!)jU!5b@W!7LFTefHd%s+oJeFUh)rFH}16gP_a%5j3zH3u}7uiPBch=v9V3R*hoXc5rSQlJaZ`*pcf5=AGmlGkI;o8SAr_kPdsv;N-B&DA$n z6-Bw(+wJbd{dRM`^aA{sedlMmy+0Gh0Dw%?RGnmLUIml3HpLBK-MrQ{b!t){wzbX1p?VPZ)s8HMYPTH3h-Is;-y)7_ z`Tl#Vfr+PMPbVfKPGI4{Fx2@^18*gdgZ=Kkxi5HXYood@0$m@E$E~s1lI2h*zVGYU z&ETQlsmK7k0$VcWR|K6>&{~!QDbq!v`U{V0r_&V1SKoS&$HT(1ZNmo1O?koju4G3|CYtjz*>cGMW0}ps6Ok!m z+eQ&c6!|8g`98N0VV=iK;W`ng3$zc)1UiSN=TRG091wl%5$swnGHrqp7l1>F$+?a z`u{?Go>n%Z`B1?iaMNcz-KJQ#qsFPw{<2m{b~I@7W$%DHld9hbyoAT zZqebg!0Q+HrfGBAH7yGXXetzQ=q{hz@EKx^c(E|3#mU-~MVbKCbTxz3ul7H+JU1m4 zhhg+I%jL|2hvn21ITW&lnfUy_Z+-#u^qpRJbKu=#|D2s|tbB)8!dF%vp8Xzu|N6bv qJ9hf(w;%rW=+&=3U;k@$NBQNCN3Z?)+pVo;4EMJ8y5DSl^6)=27 z-Q+Iup`w)PTVAz3=2_`WL5t>H@ui?pga|(PAc$`w;)B1ve@00|D7dgYvoqg(zwh^F zWG*VuY=Zl zw%&ZjV=^pIF599o2xCAi%B7`2OxcFW)VAmZQCa)#{=TLLysTX(O`{oCMK@U9NyN3C zl@{CCU@q5|UQ#a&JRk@~M%6*si&Ae;)`q+u^z&g|Q-_djqpVeOgX(&7RjtZIsHET^ zX4*t8x&_m*Y}b8J#fDkZjgoF!$aFm0@Gw@#j|SEf?t5#sm&dl?udH>mEcSH0-|rXt zRzW5m-E>`7H?WQ|0t8C8ql^wvlrByfY9eJx5NCmm)SQvFr|2}RZ-JGu-Xn?A)^;rPHId5Ago*l=h_c0rG0vuImQ*l>jGVRJo_%WTHdJ}uqM9@qoEh&4GM6FI()M_J&3sM zr!VB9#%n~4W}>WN0~^RFAzW%1u18D{I~Sp27$ayCN`cdi9tkda#=pUm2mba+pv|V& zh|-KkOf+g`4MY`!fP0Q*`Is3dV#L76BDRaDDL5j62-9(i!5lW!1?y{)ZRQco*N-Zf zO9mX%AbA$yq-fbT5*9I$&+Q^|IRS4OGfTd})aK@d?bRfJ%|m;KS#wn!7_q=MF-?q$ zHujP0+9l)?tB7cc2*<(1;+D`<&OBcxA%#*2LfR2}9CbAHA2n6klSxx@h`Dumyiu*L zCejajFp;iZy`nbiRmXH42jPO5S3bx9cb9_{(!4$1OV`BP<6bXNhXv|UmhX$Q#`1y? zTpJGuM}+xg4vlF0T>+%WRLKaINqDcX)peFNE0+lUZI zYumRRR1!s-aH|c8CKK=fFZJW#vM!A}0*+c;dwhRRR`kbYblveRa)JHUmxC`e^;HWSH5^| z@0oAzomqRM6kc9D{pAh+%~N;g{<=8-8g5?AyF<^?xhvX&iL2*hvG)*ZXhe+5Tz?MaO&*Lc-O@C7~9Ei zBB4r1MdHFAPyv@-xWJ7QNF2E!#F2jjapi)_n_cg=R7uE%mAoF?Z@%}<_r7`a`R>lU zS1-SDS<|$ugYEtv+^?zYr8W4kcDuj8?d5X&gId$Bz=tc>UeF$W`MRd9-A{-6&3^n| z$YkE3T#iI%mX{E%Y3m!alClHQ=p!*si^%x%ho1~R<&kmEjY(Yg#3bE5sl?rrogq6p zU;#HaZtLr_5CC$~P<@tXMIFu}V~!ibSiQ!EJ`ZUQB4bk})c50Ey(cT7yPYm#rsL{f z&@sEV69jMT7BPKHd~Djt?1l~rElY2I24t&v67KciX>-9-WK5c-3^ATgr=6+Yk<}QR zK@ebKVaq}gf$HO;p)*v}Hz&JiXG|yMjb+dH@Sz5lQ0@uUiQs6yN%fpI^ts_x1HD*WDysG4>)b;1tKL+ljHx4i{o%m)D10|81y3pxOCE# zhd%M`uHz?&1#E;o)8UBvBaQ+=lCJHWp69VSBbeWp>`-Z-<}VD(B?F9=jv=uvN?eyC z-|sTSEhj+K@c@kx%Qq9#NgQX1_I{PZo}t;ZsEQQ_#DMrF@f;7?u1OFV4n@=iFqesB z}`6a_zDuc!51dS(;ctg2bJGTfo#l_0lIx2WxM*Rvf5 z3Gfz5IG8NpF6=|bOg|A8b-1};k)=yM3s`)9{yFn!+T?OF^js{UAO}? zIV2(Mg%6fy_(rB zE(A@HL{F7k<$i(Tq@a%FC9j~12E!m4f|f3ihFPvoP+_l7D~2q{&2ReJQkwXSrD6+ z5Uz5U!*)p)V1v$xas*|7O|*_M>M5FD0+Tpl#&9!6=uyJRP&h-9aN!LUdqqKoKtl3a@sG)-eTiIF45hVptWW5Ue4x zUa4NSAb@Fn2*Gd)Hl!qE0)k;tHjEKETpLm{(Cr>SG#E@pD{65A$dcguKw!nw7#6S! zL6PcZPAF~=LTW)WS=cYBlH!*h!Y%dpzZmjdDq*-@4vfjebC?hif&g0qvBVg^Fk_bm zR+VIrBn6U%DoQA!VhSTg`8 zy_DIY_pv^jB$H@mvIr&*8@L};W5okRGmmpd)@wpp6Cr55S(G-D1nM(WMiXZw34x># z_+DPS6p&=M!~=Ep2+VVgPm+Tyu#gyJYXvMEs?`h)(7`C2q5`Vdn3y7!!iGdEAi`t5 zV^~hJy9MF2Itx6Kcr89ay@~OR!$$l903E0J@i0a5sS#EdY_&k?iTDe97=MLr7@z&b zYXASTPl(I;*-)(jR!|Jl#)D~VO}sSNf2(${>PhF=fTy*ZDhVZu*xHmP%N}f0y@5BF z^b~~(dIPW(Pnl4fH-Kr$afI0?kSxU$aTWnF$tMnrjh}yv`F*QqvNKqUwPtD$01v5M zLt((i+o=TBI3`)~DmzZ|VtlDYQP>ed;BgQt#{4J{lV zc_e(5Kdw7ch_qJ~7MXfKIXZ3bc~{3&UB}cSgEr;Jts57Ni{2a=U$n=!CkU*diA-L1-3I!4f)@^WxZ?P z*KvNhWzN7a>0jUe=-9>$_O8~PlKETL3`U*smBZbyt@xqnz?v;qcWgBMrMFt0jihyL zEPDI;Idl7VZqCt_@TF#&+qU-3<^@M9;XSdIO!oV;g^)XAc;=aH*V{MQ)LBIrCY>A3 z+p6}AT!dflcREf^|D)SV#k$^sK6cp(yKfezd(%eC;ZUrn)enVcbzZb?cE_qFfA?5> z-TJCsZ$CM#eA>AreBpA-&(Ff|HJ4>{&)^Rn%RXGTm?!zJbt~F0cb<-At?%EvyZLWx z-(UOR&3eDFw7Gt$%M7Som<&9dSYb%=|lY;-U?@SI-aq# z*s;Cn;8jmPG&|ogJA<9Pu*dh)%vG-yz!zWf9ctUOlgR1w)tr*P+IXQnFC%SdhVgE% zzI1Z;pY@&oQw!m5)}5u1wgzV&n}2ZL;?aQ{ZCe&R?jQWnl+~M~odZv79(mE_xU_HA QdF`vhRb1*gQnbG1A7s+^b^rhX literal 0 HcmV?d00001 diff --git a/utable/Resources/unused/check1_b_96.png b/utable/Resources/unused/check1_b_96.png new file mode 100644 index 0000000000000000000000000000000000000000..215fe748371f1b577a4012b7ba6b2d78345271c8 GIT binary patch literal 1918 zcmcIlJB-{!7rP#_QuMMRNQfd(lR6ro|fuN>%dY{N=kkL_>1|NFoH zpP4JowU>@9o?4V7=~!d6zAo-3%IlE@@t-yAAH?m^boKRIl8%ZGM^7A)KD>5Pk{0%& z)>g6Qzv2-dtC;eDslzxGXh}MAZkS@S%?deS?I@`#clW+llGoI5L@8G3>sW(AgqaW~1mVO1INdSYC@))aXJDYmQ1av4zG@|$vvXG}(_ z4Tx?bx#Ft2ZCbATylg<-(V(O0CeUrqf}UZ>lb<4@Wi<5G>n~1XiKnX4DT>t7w83DY z4osD2ZB2JwSA<7(gIEzLOMq2$Fnh%1~!H$)dD~cp{gKIN-gastBdy6ykJ7o8%Ll zgkjncr<$(9GNmcd_Ya50@eG|8D}51VqW3`JytR`uZJp)3ml3woXGyU%4VHFz!SfD( zfVSD^e<-A67BKB)-H|<%XspX(!JP}NKGK(ZwT$6{XD!aV(?T_;5#?H~EQ~8Zvl%6n z5AtVAQIk2Wjtf>*3}`?A9bmXE9eQTPgZ6XK^&lKWebEXO7kEA}r%|{w57hU)Mv@me zA*@laDngVRMbvXcBQOI8fl7!daG6CxU=b4_3p*$@LrP3C(k15CIq8*Vl=H`(OF0o7 zGebQ}P0M7q1*l#L0n#l79Ni|srWKna0~188Dch@ABo+_u?q@AkQNd^$5V^?J0SQbK zSb-G)7rF>gro+$)!mtt;ihMtAjdywGb1K|56`2w*(*Gc8TpWkU;gk;Q71 zi|Xx6mdFKmaWd*g@~H1TOv-kzDx|D4qsruUbbx#F=^WDzI!urrQv5M2=V39x8Cz}( zr|0P}?Lqo;+&^6Xe6|07)lbYN9h|h8*g-X=%ttHtnsI5ge^>2()u)}aEPA@EX+Ck{ zJZ{fCIeT!Unuwx`X<2}oNCzRcD!`>kEK5RkCu9b;s6JNFjsA-(fCOS>+rUK>0?V`r zz)nbk5m*i~Lmaw>v5#w%+gPG@crGL4z8!dKjM&!8;p{vS53>`ACBno^oHyU!e)CIl zRz;2aa?5$=$Di?{bo{Y}=AE1Bul=X}E0Dar@z=*+-9V?M_Pb&8@||y1zPonop{2J} z<7RYyxck}0r{Dha-fMCH>cSJ(pS*FY-Mktcf8nDKKL2xj{*6n2111M$ng9R* literal 0 HcmV?d00001 diff --git a/utable/Resources/unused/check1_w.png b/utable/Resources/unused/check1_w.png new file mode 100644 index 0000000000000000000000000000000000000000..da392647cc5a0f7455709b9b0d5b43f6a14aadd4 GIT binary patch literal 2230 zcmcIme{2(F7%qd1ITSm22m05ElPDta4z$kV zHF+f$2ne1{GFZK-s+QZ-$k9Anx&kSQG7umHR2GSbf?_)s)6XNMiLm1)J5HD}!pcx4h9r^12ZhyS-p|yy zUr5w~R}M6wsuF|akx0ZCF&jm>0Vimh#!Vznk{FD@l;*I?MzOF`kVJ3;g_8wI6~r*2 zA+kQPNp+x*X?zMHDFqu=5;8%-@F*+cgwdo;DH-VXP8}KwrJ@ye{oZUkYqAX!Wbh^nXr#7A&TJ^mL%o=X*!n&jZ5JUoYkAP7Nh1;!GS{3OOD z16CE~T2Txp8&#dGh`3x@V`yY~oe<{5h+@!4C31k9Re=K~O{57k*)h^yOPCl7$(X4k z6U~@R@lY?c0?(@K^nyiR@HbB<)azv`!ivg2w(t=3rpGg$9|-^mIz{v2VT$NiBdiRZ4N&Rn_zQm&e}&yR zHT&uH{{Lm45SI(E;RXO#P#o3fgKKL|yfyfLyLP|pN#{7Br?s9c2_?$ehLk2N9&S{# zg}2zu6omn^g~0qg^ghj7pusqfu=@eYQall75fqYq;;{JC^(UD>uxcig;Zm#(Q+ois zNbMpZ3@0wbJ>r?IO?P#=8P_V@&f37mYZn}=W)h2LUtc}2U|r{dUoL%vs@215pT0Az z3LV*^+flV({L*2xwWDU<=+}dnmeyA`N51Vk@j>1bdFA6vDu)l>)X&|}G0@j}RG)q}>!X5>?AU>K_35u{ z?Jel23%0!4ZtB<)d$KjUt&%?Rqro>z-i|EJzdTBec(I!GInL3X!0Vr1%_69_q4utG zUu{;$$I)0`Kfc75cB2i+_+)-TY$TjnbFgKhVZ=7G*`+_&p?%J(E4Oo%cbZRMK4s{C+aUM+ zael>TFYPKiGlS2)(SGN*<@-;arP}ziPxrSJv=p~`M=}fd`6Ib~X^pYHKOlt%yLasF z=;SLu*6+$c-BYn$N<&{Wf4{rhBzZ5%B%x_LJF|8NoORh9>Gq^A@4d`~o#e$N zyED69N^jDGtS6Bk1P>Jyq3CUq>Y;+Dhk|(PK?*{>c<>+S#h01Pbd~Ko7Y8zVNxt{} zzTfxzz4zW+UAZt{dAuS?(tK;VxhC!l#r5!U@t>|*--z2I$?~O4l1_*ZCl=XPzPr*PyPL$J%DJ=hnV~BPB9>!$81>`K9eT=$*A?U9wW`P?NWSSQ^&+6W(O#7s zJY}+3wE)o#Q?5Bx-9m=rydXnOw^hwnbp&+FH8dAOx%??2T1o?Vt@&aZOFVf>FV7QK zRR@DXb%3fo?W(%tII0F!2!TL=Y$wj~5X9N(2}6@*Bn^{15ASZL>H(Jqecd zc+RsPe}J~>=YJ@qWEwEpO8X;wC{bCTMS?pMSbd}~aT_VaIZr#B_a}u~okWxyjiNA) z{OrXrrhJe+SBNU-uqMu#r$7yAK(hgKI=bc}%QdyC zapzJ_1jp1+7X~QMAu$272n2{42soyO0fGT04uYoT)Fx~%r=eIpxPOqfP(=kJ@g2)@ z2myvo0ua~+12#bx@R46b2CEqqF-5)~x54{7ZF4HzMT$&`8}Ky3qJ_c;cbS^R-J+od zyU1cW%tZBe$|bVSUN1-eP#*Q2i%HS$o)CcL$@b=N_XRH1HtG+as^l;o|Vh2@~A|JKbYsRHf|6R5FRiAWDUG#KO z)2wu25qGDaoISizkx5O_Nd{oZ)ImUv8gQs7mL(y&9WaOus*hFl!~f#)HKJkDvP7#> z4H(EE0NVit&^K%o1vqdZJis-|Z7fkeI+qdhzz#eyMr`ZFaC)AIhv|vLVqsz`&YS4b z#hN&)NUK@z*k6D9UA^+Te6o4_{9AXf{ + + - @@ -422,8 +423,6 @@ - - From 41b07ec47657fe4ebc93c6a347961b4c777fd564 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Wed, 19 Aug 2026 16:52:49 +0900 Subject: [PATCH 05/24] ui: add end time backdrop drawing logic for enhanced visual clarity --- utable/UserControls/CellControl.vb | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index e6bc00e..2d54439 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -153,6 +153,16 @@ Public Class CellControl DrawWrappedText(e.Graphics, MemoText, memoFont, memoBounds) End If + Dim backdropHeight As Integer = ScaleValue(40) + Dim backdropTop As Integer = Math.Max(NotchHeight, ClientSize.Height - backdropHeight) + Dim backdropTrim As Integer = Math.Min(ScaleValue(2), Math.Max(0, ClientSize.Height - backdropTop - 1)) + Dim endTimeBackdrop As New Rectangle(0, + backdropTop + backdropTrim, + ClientSize.Width, + Math.Max(1, ClientSize.Height - backdropTop - backdropTrim)) + + DrawEndTimeBackdrop(e.Graphics, endTimeBackdrop) + Using rightFormat As New StringFormat(StringFormatFlags.NoWrap), textBrush As New SolidBrush(ForeColor) rightFormat.Alignment = StringAlignment.Far rightFormat.LineAlignment = StringAlignment.Far @@ -309,6 +319,29 @@ Public Class CellControl End Using End Sub + Private Sub DrawEndTimeBackdrop(graphics As Graphics, bounds As Rectangle) + If bounds.Width <= 0 OrElse bounds.Height <= 0 Then Return + + Dim lastRow As Integer = Math.Max(1, bounds.Height - 1) + For row As Integer = 0 To bounds.Height - 1 + Dim progress As Double = row / CDbl(lastRow) + Dim alpha As Integer + If progress <= 0.05 Then + alpha = 0 + ElseIf progress < 1.0 Then + alpha = CInt(Math.Round(255 * ((progress - 0.05) / 0.95))) + Else + alpha = 255 + End If + + If alpha = 0 Then Continue For + + Using backdropBrush As New SolidBrush(Color.FromArgb(alpha, BackColor)) + graphics.FillRectangle(backdropBrush, bounds.X, bounds.Y + row, bounds.Width, 1) + End Using + Next + End Sub + Private Sub DrawCheckBox(graphics As Graphics, bounds As Rectangle) If checkHovered OrElse checkPressed Then Using hoverBrush As New SolidBrush(ControlPaint.Light(BackColor, 0.3)) From c0a9412807315f7d622e60bf92f21c192eac4414 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Wed, 19 Aug 2026 19:08:27 +0900 Subject: [PATCH 06/24] ui: animate timetable cell expansion --- utable/Forms/TableForm.vb | 1 + utable/UserControls/CellControl.vb | 97 ++++++++++++++++++++++++------ 2 files changed, 79 insertions(+), 19 deletions(-) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index e63f39d..c2c61d1 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -1134,6 +1134,7 @@ Public Class TableForm cell.Width = parentPanel.ClientSize.Width cell.Height = part * parentPanel.Height cell.defHeight = part * parentPanel.Height + cell.defLoc = cell.Location.Y cell.dayNum = day 'MsgBox(part * Panel1.Height) diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index 2d54439..1b85059 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -1,4 +1,5 @@ Imports System.Drawing.Text +Imports System.Diagnostics Public Class CellControl Public defHeight As Integer = 0 @@ -67,6 +68,12 @@ Public Class CellControl Private titleBounds As Rectangle = Rectangle.Empty Private titleHoverBounds As Rectangle = Rectangle.Empty + Private Const HoverAnimationDurationMilliseconds As Integer = 180 + Private ReadOnly hoverAnimationTimer As New Timer With {.Interval = 15} + Private ReadOnly hoverAnimationClock As New Stopwatch() + Private hoverAnimationStartBounds As Rectangle + Private hoverAnimationTargetBounds As Rectangle + Private timeFont As Font Private titleFont As Font Private titleStrikeoutFont As Font @@ -75,7 +82,8 @@ Public Class CellControl Public Sub New() InitializeComponent() - SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer, True) + SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw, True) + AddHandler hoverAnimationTimer.Tick, AddressOf HoverAnimationTimer_Tick End Sub Private Sub CellControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load @@ -241,38 +249,89 @@ Public Class CellControl Private Sub SetHovered(value As Boolean) If hovered = value Then Return hovered = value - Dim previousBounds As Rectangle = Bounds If hovered Then BringToFront() - If doExpand Then - Dim fullHeight As Integer = GetRequiredHeight() - Dim parentHeight As Integer = If(Parent Is Nothing, Height, Parent.ClientSize.Height) - - If Location.Y + fullHeight > parentHeight Then - Height = fullHeight - Location = New Point(0, Math.Max(0, parentHeight - fullHeight)) - ElseIf defHeight < fullHeight Then - Height = fullHeight - End If - End If - Else - If doExpand AndAlso Not alwaysExpand Then Height = defHeight - Location = New Point(0, defLoc) End If + If doExpand AndAlso Not alwaysExpand Then + StartHoverAnimation(If(hovered, GetExpandedBounds(), GetDefaultBounds())) + End If + + Invalidate() + End Sub + + Private Function GetDefaultBounds() As Rectangle + Return New Rectangle(0, defLoc, Width, defHeight) + End Function + + Private Function GetExpandedBounds() As Rectangle + Dim fullHeight As Integer = GetRequiredHeight() + Dim defaultBounds As Rectangle = GetDefaultBounds() + + If defaultBounds.Height >= fullHeight Then Return defaultBounds + + Dim parentHeight As Integer = If(Parent Is Nothing, defaultBounds.Bottom, Parent.ClientSize.Height) + Dim targetY As Integer = defaultBounds.Y + If defaultBounds.Bottom - defaultBounds.Height + fullHeight > parentHeight Then + targetY = Math.Max(0, parentHeight - fullHeight) + End If + + Return New Rectangle(0, targetY, defaultBounds.Width, fullHeight) + End Function + + Private Sub StartHoverAnimation(targetBounds As Rectangle) + If Bounds = targetBounds Then + hoverAnimationTimer.Stop() + Return + End If + + hoverAnimationStartBounds = Bounds + hoverAnimationTargetBounds = targetBounds + hoverAnimationClock.Restart() + hoverAnimationTimer.Start() + End Sub + + Private Sub HoverAnimationTimer_Tick(sender As Object, e As EventArgs) + Dim progress As Double = Math.Min(1.0, hoverAnimationClock.Elapsed.TotalMilliseconds / HoverAnimationDurationMilliseconds) + 'Smoothstep: ease-in-out with zero speed at both ends. + Dim easedProgress As Double = progress * progress * (3.0 - 2.0 * progress) + Dim previousBounds As Rectangle = Bounds + + Bounds = InterpolateBounds(hoverAnimationStartBounds, hoverAnimationTargetBounds, easedProgress) + 'Resizing otherwise repaints only the newly exposed area, leaving text from prior frames behind. Invalidate() - If Parent IsNot Nothing AndAlso previousBounds <> Bounds Then - Parent.Invalidate(previousBounds) - Parent.Invalidate(Bounds) + If Parent IsNot Nothing Then + Parent.Invalidate(Rectangle.Union(previousBounds, Bounds)) + End If + + If progress >= 1.0 Then + hoverAnimationTimer.Stop() End If End Sub + Private Function InterpolateBounds(fromBounds As Rectangle, toBounds As Rectangle, progress As Double) As Rectangle + Return New Rectangle(InterpolateValue(fromBounds.X, toBounds.X, progress), + InterpolateValue(fromBounds.Y, toBounds.Y, progress), + InterpolateValue(fromBounds.Width, toBounds.Width, progress), + InterpolateValue(fromBounds.Height, toBounds.Height, progress)) + End Function + + Private Function InterpolateValue(fromValue As Integer, toValue As Integer, progress As Double) As Integer + Return CInt(Math.Round(fromValue + (toValue - fromValue) * progress)) + End Function + Public Sub ForceExpand() + hoverAnimationTimer.Stop() Dim fullHeight As Integer = GetRequiredHeight() If Height < fullHeight Then Height = fullHeight End Sub + Protected Overrides Sub OnHandleDestroyed(e As EventArgs) + hoverAnimationTimer.Stop() + MyBase.OnHandleDestroyed(e) + End Sub + Private Sub ToggleCheck() checked = Not checked If Name <> "DemoCellControl" Then ModifyCheck(Name, checked) From 93ccd2b2c08287450f943d35b9878d2955fba4bc Mon Sep 17 00:00:00 2001 From: pdjdev Date: Wed, 19 Aug 2026 19:58:12 +0900 Subject: [PATCH 07/24] refactor: replace Panel with CompositedTablePanel for improved rendering performance --- utable/Forms/TableForm.Designer.vb | 28 ++++++++++----------- utable/UserControls/CellControl.vb | 18 ++++++++----- utable/UserControls/CompositedTablePanel.vb | 19 ++++++++++++++ utable/uTable.vbproj | 5 +++- 4 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 utable/UserControls/CompositedTablePanel.vb diff --git a/utable/Forms/TableForm.Designer.vb b/utable/Forms/TableForm.Designer.vb index 0e6fbfa..8c4a123 100644 --- a/utable/Forms/TableForm.Designer.vb +++ b/utable/Forms/TableForm.Designer.vb @@ -24,13 +24,13 @@ Partial Class TableForm Private Sub InitializeComponent() Me.components = New System.ComponentModel.Container() Me.TimeTable = New System.Windows.Forms.TableLayoutPanel() - Me.SunPanel = New System.Windows.Forms.Panel() - Me.SatPanel = New System.Windows.Forms.Panel() - Me.FriPanel = New System.Windows.Forms.Panel() - Me.ThuPanel = New System.Windows.Forms.Panel() - Me.WedPanel = New System.Windows.Forms.Panel() - Me.TuePanel = New System.Windows.Forms.Panel() - Me.MonPanel = New System.Windows.Forms.Panel() + Me.SunPanel = New uTable.CompositedTablePanel() + Me.SatPanel = New uTable.CompositedTablePanel() + Me.FriPanel = New uTable.CompositedTablePanel() + Me.ThuPanel = New uTable.CompositedTablePanel() + Me.WedPanel = New uTable.CompositedTablePanel() + Me.TuePanel = New uTable.CompositedTablePanel() + Me.MonPanel = New uTable.CompositedTablePanel() Me.TopPanel = New System.Windows.Forms.Panel() Me.TitleEditBT = New System.Windows.Forms.PictureBox() Me.TableTitleLabel = New System.Windows.Forms.Label() @@ -1002,11 +1002,11 @@ Partial Class TableForm Friend WithEvents TueLabel As Label Friend WithEvents RefreshBT As Button Friend WithEvents MainPanel As Panel - Friend WithEvents FriPanel As Panel - Friend WithEvents ThuPanel As Panel - Friend WithEvents WedPanel As Panel - Friend WithEvents TuePanel As Panel - Friend WithEvents MonPanel As Panel + Friend WithEvents FriPanel As CompositedTablePanel + Friend WithEvents ThuPanel As CompositedTablePanel + Friend WithEvents WedPanel As CompositedTablePanel + Friend WithEvents TuePanel As CompositedTablePanel + Friend WithEvents MonPanel As CompositedTablePanel Friend WithEvents MinBT As PictureBox Friend WithEvents TableTitleLabel As Label Friend WithEvents TitleEditBT As PictureBox @@ -1029,8 +1029,8 @@ Partial Class TableForm Friend WithEvents hideani As Timer Friend WithEvents ExitItem As ToolStripMenuItem Friend WithEvents ToolStripSeparator1 As ToolStripSeparator - Friend WithEvents SunPanel As Panel - Friend WithEvents SatPanel As Panel + Friend WithEvents SunPanel As CompositedTablePanel + Friend WithEvents SatPanel As CompositedTablePanel Friend WithEvents SunLabel As Label Friend WithEvents SatLabel As Label Friend WithEvents ColorSettingItem As ToolStripMenuItem diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index 1b85059..f9e7f11 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -42,7 +42,7 @@ Public Class CellControl Private ReadOnly Property NotchHeight As Integer Get - Return ScaleValue(2) + Return ScaleValue(3) End Get End Property @@ -69,7 +69,7 @@ Public Class CellControl Private titleHoverBounds As Rectangle = Rectangle.Empty Private Const HoverAnimationDurationMilliseconds As Integer = 180 - Private ReadOnly hoverAnimationTimer As New Timer With {.Interval = 15} + Private ReadOnly hoverAnimationTimer As New Timer With {.Interval = 10} Private ReadOnly hoverAnimationClock As New Stopwatch() Private hoverAnimationStartBounds As Rectangle Private hoverAnimationTargetBounds As Rectangle @@ -116,7 +116,8 @@ Public Class CellControl If ClientSize.Width <= 0 OrElse ClientSize.Height <= 0 Then Return EnsureRenderFonts() - e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit + 'ClearType sub-pixels become distorted when this control is composed off-screen. + e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit Using notchBrush As New SolidBrush(ControlPaint.Light(BackColor, 0.3)) e.Graphics.FillRectangle(notchBrush, New Rectangle(0, 0, ClientSize.Width, NotchHeight)) @@ -195,6 +196,11 @@ Public Class CellControl Protected Overrides Sub OnMouseLeave(e As EventArgs) MyBase.OnMouseLeave(e) + + 'Moving an upward-expanding cell can cause WinForms to raise MouseLeave even + 'though the pointer is still inside its newly calculated bounds. + If ClientRectangle.Contains(PointToClient(Cursor.Position)) Then Return + checkHovered = False checkPressed = False titleHovered = False @@ -302,7 +308,7 @@ Public Class CellControl 'Resizing otherwise repaints only the newly exposed area, leaving text from prior frames behind. Invalidate() If Parent IsNot Nothing Then - Parent.Invalidate(Rectangle.Union(previousBounds, Bounds)) + Parent.Invalidate(Rectangle.Union(previousBounds, Bounds), True) End If If progress >= 1.0 Then @@ -473,8 +479,8 @@ Public Class CellControl DisposeRenderFonts() Dim familyName As String = If(CustomFont = "1" AndAlso Not String.IsNullOrWhiteSpace(CustomFontName), CustomFontName, Font.FontFamily.Name) timeFont = New Font(familyName, 9.75F, FontStyle.Bold) - titleFont = New Font(familyName, 11.25F, FontStyle.Bold) - titleStrikeoutFont = New Font(familyName, 11.25F, FontStyle.Bold Or FontStyle.Strikeout) + titleFont = New Font(familyName, 11.0F, FontStyle.Bold) + titleStrikeoutFont = New Font(familyName, 11.0F, FontStyle.Bold Or FontStyle.Strikeout) bodyFont = New Font(familyName, 9.75F, FontStyle.Regular) memoFont = New Font(familyName, 9.0F, FontStyle.Regular) End Sub diff --git a/utable/UserControls/CompositedTablePanel.vb b/utable/UserControls/CompositedTablePanel.vb new file mode 100644 index 0000000..d909850 --- /dev/null +++ b/utable/UserControls/CompositedTablePanel.vb @@ -0,0 +1,19 @@ +Public Class CompositedTablePanel + Inherits Panel + + Private Const WsExComposited As Integer = &H2000000 + + Public Sub New() + SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw, True) + DoubleBuffered = True + End Sub + + Protected Overrides ReadOnly Property CreateParams As CreateParams + Get + Dim parameters As CreateParams = MyBase.CreateParams + 'Compose the grid and every course cell off-screen before presenting a frame. + parameters.ExStyle = parameters.ExStyle Or WsExComposited + Return parameters + End Get + End Property +End Class diff --git a/utable/uTable.vbproj b/utable/uTable.vbproj index 397fd98..9040b60 100644 --- a/utable/uTable.vbproj +++ b/utable/uTable.vbproj @@ -327,6 +327,9 @@ UserControl + + Component + SettingMenu.vb @@ -448,4 +451,4 @@ Static - \ No newline at end of file + From e25caeb2ca54b2a886ce93e928963e65800a4d7a Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 00:57:27 +0900 Subject: [PATCH 08/24] ui: rollback ViewCourse title rendering method to single line --- utable/Forms/TableForm.vb | 4 +- utable/Forms/ViewCourse.Designer.vb | 26 +++++------ utable/Forms/ViewCourse.vb | 52 ++++++++++++++++----- utable/Modules/GUIModule.vb | 33 +++++++------ utable/Modules/ThemeColors.vb | 6 +-- utable/UserControls/CellControl.vb | 43 +++++++++++------ utable/UserControls/CompositedTablePanel.vb | 2 +- 7 files changed, 104 insertions(+), 62 deletions(-) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index c2c61d1..ca53764 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -149,7 +149,7 @@ Public Class TableForm Set(ByVal value As ResizeDirection) _resizeDir = value - 'Change cursor + '커서 변경 Select Case value Case ResizeDirection.Left Me.Cursor = Cursors.SizeWE @@ -238,7 +238,7 @@ Public Class TableForm End Sub Private Sub TableForm_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove - 'Calculate which direction to resize based on mouse position + '마우스 위치를 기준으로 크기를 조정할 방향을 계산 If e.Location.X < BorderWidth And e.Location.Y < BorderWidth Then resizeDir = ResizeDirection.TopLeft diff --git a/utable/Forms/ViewCourse.Designer.vb b/utable/Forms/ViewCourse.Designer.vb index e8d5dfd..7ab0d3c 100644 --- a/utable/Forms/ViewCourse.Designer.vb +++ b/utable/Forms/ViewCourse.Designer.vb @@ -58,7 +58,7 @@ Partial Class ViewCourse Me.BodyPanel.Dock = System.Windows.Forms.DockStyle.Fill Me.BodyPanel.ForeColor = System.Drawing.Color.Black Me.BodyPanel.Location = New System.Drawing.Point(4, 0) - Me.BodyPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.BodyPanel.Margin = New System.Windows.Forms.Padding(4) Me.BodyPanel.Name = "BodyPanel" Me.BodyPanel.Size = New System.Drawing.Size(314, 206) Me.BodyPanel.TabIndex = 27 @@ -69,7 +69,7 @@ Partial Class ViewCourse Me.Panel4.Controls.Add(Me.TableLayoutPanel2) Me.Panel4.Dock = System.Windows.Forms.DockStyle.Fill Me.Panel4.Location = New System.Drawing.Point(0, 12) - Me.Panel4.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.Panel4.Margin = New System.Windows.Forms.Padding(4) Me.Panel4.Name = "Panel4" Me.Panel4.Padding = New System.Windows.Forms.Padding(9, 0, 9, 0) Me.Panel4.Size = New System.Drawing.Size(314, 194) @@ -81,7 +81,7 @@ Partial Class ViewCourse Me.MemoTB.Dock = System.Windows.Forms.DockStyle.Fill Me.MemoTB.Font = New System.Drawing.Font("Noto Sans KR", 10.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.MemoTB.Location = New System.Drawing.Point(9, 0) - Me.MemoTB.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.MemoTB.Margin = New System.Windows.Forms.Padding(4) Me.MemoTB.Name = "MemoTB" Me.MemoTB.Size = New System.Drawing.Size(296, 153) Me.MemoTB.TabIndex = 2 @@ -96,7 +96,7 @@ Partial Class ViewCourse Me.TableLayoutPanel2.Controls.Add(Me.CancelBT, 0, 0) Me.TableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom Me.TableLayoutPanel2.Location = New System.Drawing.Point(9, 153) - Me.TableLayoutPanel2.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.TableLayoutPanel2.Margin = New System.Windows.Forms.Padding(4) Me.TableLayoutPanel2.Name = "TableLayoutPanel2" Me.TableLayoutPanel2.RowCount = 1 Me.TableLayoutPanel2.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) @@ -141,7 +141,7 @@ Partial Class ViewCourse Me.ShadowPanel.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch Me.ShadowPanel.Dock = System.Windows.Forms.DockStyle.Top Me.ShadowPanel.Location = New System.Drawing.Point(0, 0) - Me.ShadowPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.ShadowPanel.Margin = New System.Windows.Forms.Padding(4) Me.ShadowPanel.Name = "ShadowPanel" Me.ShadowPanel.Size = New System.Drawing.Size(314, 12) Me.ShadowPanel.TabIndex = 3 @@ -153,7 +153,7 @@ Partial Class ViewCourse Me.TitlePanel.Controls.Add(Me.Panel3) Me.TitlePanel.Dock = System.Windows.Forms.DockStyle.Fill Me.TitlePanel.Location = New System.Drawing.Point(4, 4) - Me.TitlePanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.TitlePanel.Margin = New System.Windows.Forms.Padding(4) Me.TitlePanel.Name = "TitlePanel" Me.TitlePanel.Size = New System.Drawing.Size(314, 66) Me.TitlePanel.TabIndex = 0 @@ -166,7 +166,7 @@ Partial Class ViewCourse Me.TableLayoutPanel1.Controls.Add(Me.UpperTitleLabel, 0, 0) Me.TableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill Me.TableLayoutPanel1.Location = New System.Drawing.Point(0, 0) - Me.TableLayoutPanel1.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.TableLayoutPanel1.Margin = New System.Windows.Forms.Padding(4) Me.TableLayoutPanel1.Name = "TableLayoutPanel1" Me.TableLayoutPanel1.RowCount = 2 Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 60.37736!)) @@ -207,7 +207,7 @@ Partial Class ViewCourse Me.Panel3.Controls.Add(Me.CloseBT) Me.Panel3.Dock = System.Windows.Forms.DockStyle.Right Me.Panel3.Location = New System.Drawing.Point(259, 0) - Me.Panel3.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.Panel3.Margin = New System.Windows.Forms.Padding(4) Me.Panel3.Name = "Panel3" Me.Panel3.Size = New System.Drawing.Size(55, 66) Me.Panel3.TabIndex = 0 @@ -217,7 +217,7 @@ Partial Class ViewCourse Me.EditBT.Dock = System.Windows.Forms.DockStyle.Fill Me.EditBT.Image = Global.uTable.My.Resources.Resources.bt_edit_w Me.EditBT.Location = New System.Drawing.Point(0, 35) - Me.EditBT.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.EditBT.Margin = New System.Windows.Forms.Padding(4) Me.EditBT.Name = "EditBT" Me.EditBT.Size = New System.Drawing.Size(55, 31) Me.EditBT.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom @@ -229,7 +229,7 @@ Partial Class ViewCourse Me.CloseBT.Dock = System.Windows.Forms.DockStyle.Top Me.CloseBT.Image = Global.uTable.My.Resources.Resources.closeicon_w Me.CloseBT.Location = New System.Drawing.Point(0, 0) - Me.CloseBT.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.CloseBT.Margin = New System.Windows.Forms.Padding(4) Me.CloseBT.Name = "CloseBT" Me.CloseBT.Size = New System.Drawing.Size(55, 35) Me.CloseBT.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom @@ -242,7 +242,7 @@ Partial Class ViewCourse Me.UpperPanel.Controls.Add(Me.TitlePanel) Me.UpperPanel.Dock = System.Windows.Forms.DockStyle.Top Me.UpperPanel.Location = New System.Drawing.Point(0, 0) - Me.UpperPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.UpperPanel.Margin = New System.Windows.Forms.Padding(4) Me.UpperPanel.Name = "UpperPanel" Me.UpperPanel.Padding = New System.Windows.Forms.Padding(4, 4, 4, 0) Me.UpperPanel.Size = New System.Drawing.Size(322, 70) @@ -254,7 +254,7 @@ Partial Class ViewCourse Me.MainPanel.Controls.Add(Me.BodyPanel) Me.MainPanel.Dock = System.Windows.Forms.DockStyle.Fill Me.MainPanel.Location = New System.Drawing.Point(0, 70) - Me.MainPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.MainPanel.Margin = New System.Windows.Forms.Padding(4) Me.MainPanel.Name = "MainPanel" Me.MainPanel.Padding = New System.Windows.Forms.Padding(4, 0, 4, 4) Me.MainPanel.Size = New System.Drawing.Size(322, 210) @@ -271,7 +271,7 @@ Partial Class ViewCourse Me.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None Me.Icon = Global.uTable.My.Resources.Resources.ptable_icon - Me.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) + Me.Margin = New System.Windows.Forms.Padding(4) Me.MinimumSize = New System.Drawing.Size(188, 150) Me.Name = "ViewCourse" Me.StartPosition = System.Windows.Forms.FormStartPosition.Manual diff --git a/utable/Forms/ViewCourse.vb b/utable/Forms/ViewCourse.vb index fd4e295..7f1aab9 100644 --- a/utable/Forms/ViewCourse.vb +++ b/utable/Forms/ViewCourse.vb @@ -9,8 +9,13 @@ Public Class ViewCourse Dim listcount As Integer = 0 Dim colormode As String = Nothing - Dim maxTitleSize As Single = 15.0! + Dim maxTitleSize As Single = 14.0! Dim maxSubSize As Single = 9.0! + Private Const MinimumTitleSize As Single = 11.0! + Private Const TitleEllipsis As String = "..." + + '창 크기가 바뀌면 원본 제목을 기준으로 다시 맞춘다. + Private courseTitle As String = String.Empty Public olddata As String = Nothing Public blacktext As Boolean = False @@ -68,7 +73,7 @@ Public Class ViewCourse Set(ByVal value As ResizeDirection) _resizeDir = value - 'Change cursor + '커서를 변경한다 Select Case value Case ResizeDirection.Left Me.Cursor = Cursors.SizeWE @@ -252,8 +257,9 @@ Public Class ViewCourse Try Dim course As TableCourse = getTableCourse(olddata) - UpperTitleLabel.Text = xmlDecode(course.Name) - Text = UpperTitleLabel.Text + courseTitle = NormalizeCourseTitle(xmlDecode(course.Name)) + Text = courseTitle + UpperTitleLabel.Text = courseTitle SubTitleLabel.Text = xmlDecode(course.Professor) + ", " Dim startt As Integer = Convert.ToInt32(course.Start) @@ -313,7 +319,7 @@ Public Class ViewCourse End Try - AdjustText(UpperTitleLabel, maxTitleSize) + LayoutCourseTitle() AdjustText(SubTitleLabel, maxSubSize) '글꼴이 자꾸 시스템 기본값으로 바뀌는것을 방지 @@ -324,11 +330,31 @@ Public Class ViewCourse End Sub - Private Sub AdjustText(lblQueue As Label, maxSize As Single) + Private Function NormalizeCourseTitle(value As String) As String + If String.IsNullOrWhiteSpace(value) Then Return String.Empty + + Return value.Replace(vbCrLf, " ").Replace(vbCr, " ").Replace(vbLf, " ").Trim() + End Function + + Private Sub LayoutCourseTitle() + If UpperTitleLabel.Width <= 0 OrElse UpperTitleLabel.Height <= 0 Then Exit Sub + + UpperTitleLabel.Text = courseTitle + If String.IsNullOrEmpty(courseTitle) Then Exit Sub + + AdjustText(UpperTitleLabel, maxTitleSize, MinimumTitleSize) + Dim shortened As String = courseTitle + While shortened.Length > 0 AndAlso Not TextFits(UpperTitleLabel, UpperTitleLabel.Text, UpperTitleLabel.Font) + shortened = shortened.Substring(0, Math.Max(0, shortened.Length - 1)).TrimEnd() + UpperTitleLabel.Text = shortened + TitleEllipsis + End While + End Sub + + Private Sub AdjustText(lblQueue As Label, maxSize As Single, Optional minimumSize As Single = 6.0!) If String.IsNullOrEmpty(lblQueue.Text) OrElse lblQueue.Width <= 0 OrElse lblQueue.Height <= 0 Then Exit Sub '0.1pt씩 Font/MeasureText를 반복 생성하던 선형 탐색 대신 이분 탐색을 사용한다. - Dim lower As Single = 6.0! + Dim lower As Single = minimumSize Dim upper As Single = maxSize Dim best As Single = lower @@ -336,8 +362,7 @@ Public Class ViewCourse Dim candidate As Single = (lower + upper) / 2 Dim fits As Boolean Using testFont As New Font(lblQueue.Font.Name, candidate, lblQueue.Font.Style) - Dim textSize As Size = TextRenderer.MeasureText(lblQueue.Text, testFont) - fits = textSize.Width + 3 <= lblQueue.Width AndAlso textSize.Height + 3 <= lblQueue.Height + fits = TextFits(lblQueue, lblQueue.Text, testFont) End Using If fits Then @@ -348,12 +373,17 @@ Public Class ViewCourse End If Next - If lblQueue Is UpperTitleLabel AndAlso best < 9.0! Then best = 9.0! lblQueue.Font = New Font(lblQueue.Font.Name, best, lblQueue.Font.Style) End Sub + Private Function TextFits(label As Label, value As String, fontToMeasure As Font) As Boolean + Dim textSize As Size = TextRenderer.MeasureText(value, fontToMeasure) + Return textSize.Width + 3 <= label.ClientSize.Width - label.Padding.Horizontal AndAlso + textSize.Height + 3 <= label.ClientSize.Height - label.Padding.Vertical + End Function + Private Sub ViewCourse_SizeChanged(sender As Object, e As EventArgs) Handles MyBase.SizeChanged - AdjustText(UpperTitleLabel, maxTitleSize) + LayoutCourseTitle() AdjustText(SubTitleLabel, maxSubSize) End Sub diff --git a/utable/Modules/GUIModule.vb b/utable/Modules/GUIModule.vb index c58a48d..349281d 100644 --- a/utable/Modules/GUIModule.vb +++ b/utable/Modules/GUIModule.vb @@ -72,24 +72,24 @@ Module GUIModule Public Sub SnapToDesktopBorder(ByVal clientForm As Form, ByVal LParam As IntPtr, ByVal widthAdjustment As Integer) If clientForm Is Nothing Then - ' Satisfies rule: Validate parameters + ' 규칙 준수: 매개 변수 검증 Throw New ArgumentNullException("clientForm") End If - ' Snap client to the top, left, bottom or right desktop border - ' as the form is moved near that border. + ' 폼이 데스크톱 경계에 가까워지면 클라이언트 영역을 + ' 위쪽, 왼쪽, 아래쪽 또는 오른쪽 경계에 맞춘다 Try - ' Marshal the LPARAM value which is a WINDOWPOS struct + ' WINDOWPOS 구조체인 LPARAM 값을 마샬링 Dim NewPosition As New WINDOWPOS NewPosition = CType(Runtime.InteropServices.Marshal.PtrToStructure( LParam, GetType(WINDOWPOS)), WINDOWPOS) If NewPosition.y = 0 OrElse NewPosition.x = 0 Then - Return ' Nothing to do! + Return ' 수행할 작업이 없다 End If - ' Adjust the client size for borders and caption bar + ' 테두리와 제목 표시줄을 고려하여 클라이언트 크기를 조정한다 Dim ClientRect As Rectangle = clientForm.RectangleToScreen(clientForm.ClientRectangle) ClientRect.Width += @@ -98,38 +98,37 @@ Module GUIModule SystemInformation.CaptionHeight - dpicalc(clientForm, 31)) ' 임의로 포인트 변경 - ' Now get the screen working area (without taskbar) + ' 이제 작업 표시줄을 제외한 화면 작업 영역을 가져온다 Dim WorkingRect As Rectangle = Screen.GetWorkingArea(clientForm.ClientRectangle) - ' Left border + ' 왼쪽 경계 If NewPosition.x >= WorkingRect.X - mSnapOffset AndAlso NewPosition.x <= WorkingRect.X + mSnapOffset Then NewPosition.x = WorkingRect.X End If - ' Get screen bounds and taskbar height - ' (when taskbar is horizontal) + ' 화면 경계와 작업 표시줄 높이를 가져온다 + ' (작업 표시줄이 가로 방향인 경우) Dim ScreenRect As Rectangle = Screen.GetBounds(Screen.PrimaryScreen.Bounds) Dim TaskbarHeight As Integer = ScreenRect.Height - WorkingRect.Height - ' Top border (check if taskbar is on top - ' or bottom via WorkingRect.Y) + ' 위쪽 경계 (WorkingRect.Y를 통해 작업 표시줄이 위쪽인지 아래쪽인지 확인) If NewPosition.y >= -mSnapOffset AndAlso (WorkingRect.Y > 0 AndAlso NewPosition.y <= (TaskbarHeight + mSnapOffset)) OrElse (WorkingRect.Y <= 0 AndAlso NewPosition.y <= (mSnapOffset)) Then If TaskbarHeight > 0 Then - NewPosition.y = WorkingRect.Y ' Horizontal Taskbar + NewPosition.y = WorkingRect.Y ' 가로 작업 표시줄 Else - NewPosition.y = 0 ' Vertical Taskbar + NewPosition.y = 0 ' 세로 작업 표시줄 End If End If - ' Right border + ' 오른쪽 경계 If NewPosition.x + ClientRect.Width <= WorkingRect.Right + mSnapOffset AndAlso NewPosition.x + ClientRect.Width >= @@ -138,7 +137,7 @@ Module GUIModule SystemInformation.FrameBorderSize.Width) End If - ' Bottom border + ' 아래쪽 경계 If NewPosition.y + ClientRect.Height <= WorkingRect.Bottom + mSnapOffset AndAlso NewPosition.y + ClientRect.Height >= @@ -147,7 +146,7 @@ Module GUIModule SystemInformation.FrameBorderSize.Height) End If - ' Marshal it back + ' 다시 마샬링 Runtime.InteropServices.Marshal.StructureToPtr(NewPosition, LParam, True) Catch ex As ArgumentException diff --git a/utable/Modules/ThemeColors.vb b/utable/Modules/ThemeColors.vb index a122e37..b1564b0 100644 --- a/utable/Modules/ThemeColors.vb +++ b/utable/Modules/ThemeColors.vb @@ -2,7 +2,7 @@ Imports System.Drawing Imports System.Windows.Forms ''' -''' Immutable color palette used by the application's light and dark themes. +''' 애플리케이션의 라이트 테마와 다크 테마에서 사용하는 색상 팔레트 ''' Public NotInheritable Class ThemeColors Public Shared ReadOnly Light As New ThemeColors( @@ -56,8 +56,8 @@ Public NotInheritable Class ThemeColors End Class ''' -''' Draws every level of a ToolStrip menu with the active application palette. -''' Windows' system renderer ignores BackColor/ForeColor on drop-down menus. +''' 활성 애플리케이션 팔레트를 사용하여 ToolStrip 메뉴의 모든 계층을 그립니다 +''' Windows 시스템 렌더러는 드롭다운 메뉴의 BackColor/ForeColor를 무시합니다 ''' Public NotInheritable Class MenuThemeRenderer Inherits ToolStripProfessionalRenderer diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index f9e7f11..0b14049 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -116,7 +116,7 @@ Public Class CellControl If ClientSize.Width <= 0 OrElse ClientSize.Height <= 0 Then Return EnsureRenderFonts() - 'ClearType sub-pixels become distorted when this control is composed off-screen. + 'ClearType 대신 가독성 좋은 AntiAliasGridFit 사용 e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit Using notchBrush As New SolidBrush(ControlPaint.Light(BackColor, 0.3)) @@ -139,7 +139,7 @@ Public Class CellControl End Using y += Math.Max(CheckSize, timeFont.Height) + VerticalPadding - titleBounds = New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(CourseTitle, titleFont, contentWidth)) + titleBounds = New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(e.Graphics, CourseTitle, titleFont, contentWidth)) titleHoverBounds = titleBounds If titleHovered Then @@ -152,13 +152,13 @@ Public Class CellControl y += titleBounds.Height If showProfessor AndAlso Not String.IsNullOrEmpty(ProfessorText) Then - Dim professorBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(ProfessorText, bodyFont, contentWidth)) + Dim professorBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(e.Graphics, ProfessorText, bodyFont, contentWidth)) DrawWrappedText(e.Graphics, ProfessorText, bodyFont, professorBounds) y += professorBounds.Height End If If showMemoText AndAlso Not String.IsNullOrEmpty(MemoText) Then - Dim memoBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(MemoText, memoFont, contentWidth)) + Dim memoBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(e.Graphics, MemoText, memoFont, contentWidth)) DrawWrappedText(e.Graphics, MemoText, memoFont, memoBounds) End If @@ -197,8 +197,8 @@ Public Class CellControl Protected Overrides Sub OnMouseLeave(e As EventArgs) MyBase.OnMouseLeave(e) - 'Moving an upward-expanding cell can cause WinForms to raise MouseLeave even - 'though the pointer is still inside its newly calculated bounds. + '위쪽으로 확장되는 셀을 이동하면 새로 계산된 영역 안에 포인터가 있어도 + 'WinForms가 MouseLeave를 발생시킬 수 있음 If ClientRectangle.Contains(PointToClient(Cursor.Position)) Then Return checkHovered = False @@ -300,12 +300,12 @@ Public Class CellControl Private Sub HoverAnimationTimer_Tick(sender As Object, e As EventArgs) Dim progress As Double = Math.Min(1.0, hoverAnimationClock.Elapsed.TotalMilliseconds / HoverAnimationDurationMilliseconds) - 'Smoothstep: ease-in-out with zero speed at both ends. + 'Smoothstep: 양 끝에서 속도가 0이 되는 ease-in-out 보간 Dim easedProgress As Double = progress * progress * (3.0 - 2.0 * progress) Dim previousBounds As Rectangle = Bounds Bounds = InterpolateBounds(hoverAnimationStartBounds, hoverAnimationTargetBounds, easedProgress) - 'Resizing otherwise repaints only the newly exposed area, leaving text from prior frames behind. + Invalidate() If Parent IsNot Nothing Then Parent.Invalidate(Rectangle.Union(previousBounds, Bounds), True) @@ -364,26 +364,39 @@ Public Class CellControl EnsureRenderFonts() Dim contentWidth As Integer = Math.Max(1, ClientSize.Width - HorizontalPadding * 2) Dim result As Integer = NotchHeight + VerticalPadding + Math.Max(CheckSize, timeFont.Height) + VerticalPadding - result += MeasureTextHeight(CourseTitle, titleFont, contentWidth) - If showProfessor Then result += MeasureTextHeight(ProfessorText, bodyFont, contentWidth) - If showMemoText Then result += MeasureTextHeight(MemoText, memoFont, contentWidth) + + Using graphics As Graphics = CreateGraphics() + result += MeasureTextHeight(graphics, CourseTitle, titleFont, contentWidth) + If showProfessor Then result += MeasureTextHeight(graphics, ProfessorText, bodyFont, contentWidth) + If showMemoText Then result += MeasureTextHeight(graphics, MemoText, memoFont, contentWidth) + End Using + result += timeFont.Height + VerticalPadding Return result End Function - Private Function MeasureTextHeight(value As String, fontToMeasure As Font, width As Integer) As Integer + Private Function MeasureTextHeight(graphics As Graphics, value As String, fontToMeasure As Font, width As Integer) As Integer If String.IsNullOrEmpty(value) Then Return 0 - Return TextRenderer.MeasureText(value, fontToMeasure, New Size(Math.Max(1, width), Integer.MaxValue), TextFormatFlags.WordBreak Or TextFormatFlags.NoPadding).Height + + Using textFormat As StringFormat = CreateWrappedTextFormat() + Return CInt(Math.Ceiling(graphics.MeasureString(value, fontToMeasure, Math.Max(1, width), textFormat).Height)) + End Using End Function Private Sub DrawWrappedText(graphics As Graphics, value As String, fontToDraw As Font, bounds As Rectangle) If String.IsNullOrEmpty(value) OrElse bounds.Height <= 0 Then Return - Using textBrush As New SolidBrush(ForeColor), textFormat As New StringFormat() - textFormat.Trimming = StringTrimming.EllipsisCharacter + + Using textBrush As New SolidBrush(ForeColor), textFormat As StringFormat = CreateWrappedTextFormat() graphics.DrawString(value, fontToDraw, textBrush, bounds, textFormat) End Using End Sub + Private Shared Function CreateWrappedTextFormat() As StringFormat + Dim textFormat As New StringFormat() + textFormat.Trimming = StringTrimming.EllipsisCharacter + Return textFormat + End Function + Private Sub DrawEndTimeBackdrop(graphics As Graphics, bounds As Rectangle) If bounds.Width <= 0 OrElse bounds.Height <= 0 Then Return diff --git a/utable/UserControls/CompositedTablePanel.vb b/utable/UserControls/CompositedTablePanel.vb index d909850..bf0a88c 100644 --- a/utable/UserControls/CompositedTablePanel.vb +++ b/utable/UserControls/CompositedTablePanel.vb @@ -11,7 +11,7 @@ Public Class CompositedTablePanel Protected Overrides ReadOnly Property CreateParams As CreateParams Get Dim parameters As CreateParams = MyBase.CreateParams - 'Compose the grid and every course cell off-screen before presenting a frame. + '화면에 프레임을 표시하기 전에 격자와 모든 과목 셀을 오프스크린에서 합성한다 parameters.ExStyle = parameters.ExStyle Or WsExComposited Return parameters End Get From 3cc0c260688101010dd83af16cbe052c9adc858f Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 01:12:04 +0900 Subject: [PATCH 09/24] setting: add ExpandAnimation option --- utable/Forms/OptionForm.Designer.vb | 20 +++++++++++++++++--- utable/Forms/OptionForm.vb | 12 ++++++++++-- utable/Forms/TableForm.vb | 7 +++++++ utable/UserControls/CellControl.vb | 15 ++++++++++++++- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/utable/Forms/OptionForm.Designer.vb b/utable/Forms/OptionForm.Designer.vb index 9ccd709..f358129 100644 --- a/utable/Forms/OptionForm.Designer.vb +++ b/utable/Forms/OptionForm.Designer.vb @@ -62,6 +62,7 @@ Partial Class OptionForm Me.CustomFontChk = New System.Windows.Forms.CheckBox() Me.Label5 = New System.Windows.Forms.Label() Me.BlackTextChk = New System.Windows.Forms.CheckBox() + Me.ExpandAnimationChk = New System.Windows.Forms.CheckBox() Me.AlwaysExpandChk = New System.Windows.Forms.CheckBox() Me.Label4 = New System.Windows.Forms.Label() Me.ExpandCellChk = New System.Windows.Forms.CheckBox() @@ -413,6 +414,7 @@ Partial Class OptionForm Me.TabPage_Table.Controls.Add(Me.CustomFontChk) Me.TabPage_Table.Controls.Add(Me.Label5) Me.TabPage_Table.Controls.Add(Me.BlackTextChk) + Me.TabPage_Table.Controls.Add(Me.ExpandAnimationChk) Me.TabPage_Table.Controls.Add(Me.AlwaysExpandChk) Me.TabPage_Table.Controls.Add(Me.Label4) Me.TabPage_Table.Controls.Add(Me.ExpandCellChk) @@ -498,7 +500,7 @@ Partial Class OptionForm Me.PrevTablePanel.Location = New System.Drawing.Point(369, 15) Me.PrevTablePanel.Margin = New System.Windows.Forms.Padding(4) Me.PrevTablePanel.Name = "PrevTablePanel" - Me.PrevTablePanel.Size = New System.Drawing.Size(154, 154) + Me.PrevTablePanel.Size = New System.Drawing.Size(154, 190) Me.PrevTablePanel.TabIndex = 23 ' 'PrevTableArea @@ -507,7 +509,7 @@ Partial Class OptionForm Me.PrevTableArea.Location = New System.Drawing.Point(0, 31) Me.PrevTableArea.Margin = New System.Windows.Forms.Padding(4) Me.PrevTableArea.Name = "PrevTableArea" - Me.PrevTableArea.Size = New System.Drawing.Size(154, 123) + Me.PrevTableArea.Size = New System.Drawing.Size(154, 159) Me.PrevTableArea.TabIndex = 24 ' 'DayLabel @@ -628,7 +630,7 @@ Partial Class OptionForm 'BlackTextChk ' Me.BlackTextChk.AutoSize = True - Me.BlackTextChk.Location = New System.Drawing.Point(216, 218) + Me.BlackTextChk.Location = New System.Drawing.Point(200, 218) Me.BlackTextChk.Margin = New System.Windows.Forms.Padding(4) Me.BlackTextChk.Name = "BlackTextChk" Me.BlackTextChk.Size = New System.Drawing.Size(134, 25) @@ -636,6 +638,17 @@ Partial Class OptionForm Me.BlackTextChk.Text = "색 반전 (검은색)" Me.BlackTextChk.UseVisualStyleBackColor = True ' + 'ExpandAnimationChk + ' + Me.ExpandAnimationChk.AutoSize = True + Me.ExpandAnimationChk.Location = New System.Drawing.Point(361, 218) + Me.ExpandAnimationChk.Margin = New System.Windows.Forms.Padding(4) + Me.ExpandAnimationChk.Name = "ExpandAnimationChk" + Me.ExpandAnimationChk.Size = New System.Drawing.Size(168, 25) + Me.ExpandAnimationChk.TabIndex = 30 + Me.ExpandAnimationChk.Text = "확장/축소 애니메이션" + Me.ExpandAnimationChk.UseVisualStyleBackColor = True + ' 'AlwaysExpandChk ' Me.AlwaysExpandChk.AutoSize = True @@ -1572,6 +1585,7 @@ Partial Class OptionForm Friend WithEvents CustomFontChk As CheckBox Friend WithEvents Label5 As Label Friend WithEvents BlackTextChk As CheckBox + Friend WithEvents ExpandAnimationChk As CheckBox Friend WithEvents AlwaysExpandChk As CheckBox Friend WithEvents Label4 As Label Friend WithEvents ExpandCellChk As CheckBox diff --git a/utable/Forms/OptionForm.vb b/utable/Forms/OptionForm.vb index 9ecb720..67119b5 100644 --- a/utable/Forms/OptionForm.vb +++ b/utable/Forms/OptionForm.vb @@ -158,6 +158,9 @@ Public Class OptionForm '표확장 기본값 = 1 ExpandCellChk.Checked = Not (GetINI("SETTING", "ExpandCell", "", ININamePath) = "0") + '확장/축소 애니메이션 기본값 = 1 + ExpandAnimationChk.Checked = Not (GetINI("SETTING", "ExpandAnimation", "", ININamePath) = "0") + '고정시작시간 기본값 = 0 FixStartTimeChk.Checked = (GetINI("SETTING", "FixStartTime", "", ININamePath) = "1") @@ -408,6 +411,10 @@ Public Class OptionForm ApplySetting("ExpandCell", ExpandCellChk.Checked) End Sub + Private Sub ExpandAnimationChk_CheckedChanged(sender As Object, e As EventArgs) Handles ExpandAnimationChk.CheckedChanged + ApplySetting("ExpandAnimation", ExpandAnimationChk.Checked) + End Sub + Private Sub FixStartTimeChk_CheckedChanged(sender As Object, e As EventArgs) Handles FixStartTimeChk.CheckedChanged ApplySetting("FixStartTime", FixStartTimeChk.Checked) FixStartTimePicker.Enabled = FixStartTimeChk.Checked @@ -473,7 +480,7 @@ Public Class OptionForm End Sub - Private Sub PrevUpdateEvent(sender As Object, e As EventArgs) Handles ExpandCellChk.CheckedChanged, AlwaysExpandChk.CheckedChanged, + Private Sub PrevUpdateEvent(sender As Object, e As EventArgs) Handles ExpandCellChk.CheckedChanged, ExpandAnimationChk.CheckedChanged, AlwaysExpandChk.CheckedChanged, ShowDayChk.CheckedChanged, ShowProfChk.CheckedChanged, ShowMemoChk.CheckedChanged, BlackTextChk.CheckedChanged, ShowChkBoxChk.CheckedChanged, ShowLinePatternChk.CheckedChanged, AutoTextColorChk.CheckedChanged PrevUpdate() @@ -545,6 +552,7 @@ Public Class OptionForm ._BlackText = GetINI("SETTING", "BlackText", "", ININamePath) ._AlwaysExpand = GetINI("SETTING", "AlwaysExpand", "", ININamePath) .ExpandCell = GetINI("SETTING", "ExpandCell", "", ININamePath) + .ExpandAnimation = GetINI("SETTING", "ExpandAnimation", "", ININamePath) .ShowMemo = GetINI("SETTING", "ShowMemo", "", ININamePath) .ShowProf = GetINI("SETTING", "ShowProf", "", ININamePath) ._ShowChkBox = GetINI("SETTING", "ShowChkBox", "", ININamePath) @@ -626,7 +634,7 @@ Public Class OptionForm End Sub '바로 적용 보여주기 위해 시간표 새로고침 - Private Sub TableRelatedOptionCheckboxes_CheckedChanged(sender As Object, e As EventArgs) Handles ExpandCellChk.CheckedChanged, + Private Sub TableRelatedOptionCheckboxes_CheckedChanged(sender As Object, e As EventArgs) Handles ExpandCellChk.CheckedChanged, ExpandAnimationChk.CheckedChanged, AlwaysExpandChk.CheckedChanged, ShowDayChk.CheckedChanged, ShowMemoChk.CheckedChanged, ShowProfChk.CheckedChanged, BlackTextChk.CheckedChanged, ShowChkBoxChk.CheckedChanged, AutoTextColorChk.CheckedChanged If loaded Then TableForm.updateCell() diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index ca53764..1a47a56 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -48,6 +48,7 @@ Public Class TableForm Public _BlackText As String = "" Public _AlwaysExpand As String = "" Public ExpandCell As String = "" + Public ExpandAnimation As String = "" Public ShowMemo As String = "" Public ShowProf As String = "" Public _ShowChkBox As String = "" @@ -936,6 +937,11 @@ Public Class TableForm _BlackText = GetINI("SETTING", "BlackText", "", ININamePath) _AlwaysExpand = GetINI("SETTING", "AlwaysExpand", "", ININamePath) ExpandCell = GetINI("SETTING", "ExpandCell", "", ININamePath) + ExpandAnimation = GetINI("SETTING", "ExpandAnimation", "", ININamePath) + If String.IsNullOrEmpty(ExpandAnimation) Then + ExpandAnimation = "1" + SetINI("SETTING", "ExpandAnimation", ExpandAnimation, ININamePath) + End If ShowMemo = GetINI("SETTING", "ShowMemo", "", ININamePath) ShowProf = GetINI("SETTING", "ShowProf", "", ININamePath) _ShowChkBox = GetINI("SETTING", "ShowChkBox", "", ININamePath) @@ -1105,6 +1111,7 @@ Public Class TableForm ._BlackText = _BlackText ._AlwaysExpand = _AlwaysExpand .ExpandCell = ExpandCell + .ExpandAnimation = ExpandAnimation .ShowMemo = ShowMemo .ShowProf = ShowProf ._ShowChkBox = _ShowChkBox diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index 0b14049..eb5e9cc 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -21,6 +21,7 @@ Public Class CellControl Public _BlackText As String = "" Public _AlwaysExpand As String = "" Public ExpandCell As String = "" + Public ExpandAnimation As String = "" Public ShowMemo As String = "" Public ShowProf As String = "" Public _ShowChkBox As String = "" @@ -53,6 +54,7 @@ Public Class CellControl End Property Private doExpand As Boolean = True + Private doExpandAnimation As Boolean = True Private showChkBox As Boolean = True Private showMemoText As Boolean = True Private showProfessor As Boolean = True @@ -96,6 +98,7 @@ Public Class CellControl ForeColor = If(blackText, Color.Black, Color.White) alwaysExpand = (_AlwaysExpand = "1") doExpand = Not (ExpandCell = "0") + doExpandAnimation = Not (ExpandAnimation = "0") showMemoText = Not (ShowMemo = "0") showProfessor = Not (ShowProf = "0") showChkBox = Not (_ShowChkBox = "0") @@ -261,7 +264,17 @@ Public Class CellControl End If If doExpand AndAlso Not alwaysExpand Then - StartHoverAnimation(If(hovered, GetExpandedBounds(), GetDefaultBounds())) + Dim targetBounds As Rectangle = If(hovered, GetExpandedBounds(), GetDefaultBounds()) + If doExpandAnimation Then + StartHoverAnimation(targetBounds) + Else + hoverAnimationTimer.Stop() + Dim previousBounds As Rectangle = Bounds + Bounds = targetBounds + If Parent IsNot Nothing Then + Parent.Invalidate(Rectangle.Union(previousBounds, Bounds), True) + End If + End If End If Invalidate() From 3eb2fb38561fa9c1a8e53a224c386d12a101df17 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 01:37:22 +0900 Subject: [PATCH 10/24] refactor: enhance expansion logic in CellControl for improved hover behavior --- utable/UserControls/CellControl.vb | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index eb5e9cc..f7b7a4c 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -263,8 +263,12 @@ Public Class CellControl BringToFront() End If - If doExpand AndAlso Not alwaysExpand Then - Dim targetBounds As Rectangle = If(hovered, GetExpandedBounds(), GetDefaultBounds()) + If doExpand OrElse alwaysExpand Then + '항상 확장 상태도 평소에는 실제 시작 시간(defLoc)을 유지하고, + '호버 중에만 시간표 하단을 넘는 경우 위쪽으로 이동한다. + Dim targetBounds As Rectangle = If(hovered, + GetExpandedBounds(), + If(alwaysExpand, GetAlwaysExpandedBounds(), GetDefaultBounds())) If doExpandAnimation Then StartHoverAnimation(targetBounds) Else @@ -299,6 +303,11 @@ Public Class CellControl Return New Rectangle(0, targetY, defaultBounds.Width, fullHeight) End Function + Private Function GetAlwaysExpandedBounds() As Rectangle + Dim defaultBounds As Rectangle = GetDefaultBounds() + Return New Rectangle(0, defaultBounds.Y, defaultBounds.Width, Math.Max(defaultBounds.Height, GetRequiredHeight())) + End Function + Private Sub StartHoverAnimation(targetBounds As Rectangle) If Bounds = targetBounds Then hoverAnimationTimer.Stop() @@ -342,8 +351,14 @@ Public Class CellControl Public Sub ForceExpand() hoverAnimationTimer.Stop() - Dim fullHeight As Integer = GetRequiredHeight() - If Height < fullHeight Then Height = fullHeight + Dim expandedBounds As Rectangle = GetAlwaysExpandedBounds() + If Bounds <> expandedBounds Then + Dim previousBounds As Rectangle = Bounds + Bounds = expandedBounds + If Parent IsNot Nothing Then + Parent.Invalidate(Rectangle.Union(previousBounds, Bounds), True) + End If + End If End Sub Protected Overrides Sub OnHandleDestroyed(e As EventArgs) From 0bd3999671ab7a483e237c5222332daccfa545e0 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 01:41:40 +0900 Subject: [PATCH 11/24] ui: add color mode support for MemoTB in ViewCourse --- utable/Forms/ViewCourse.vb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utable/Forms/ViewCourse.vb b/utable/Forms/ViewCourse.vb index 7f1aab9..e536dca 100644 --- a/utable/Forms/ViewCourse.vb +++ b/utable/Forms/ViewCourse.vb @@ -239,6 +239,13 @@ Public Class ViewCourse MainPanel.BackColor = theme.Edge MemoTB.ForeColor = theme.Text + + Select Case colormode + Case "Dark" + SetWindowTheme(MemoTB.Handle, "DarkMode_Explorer", Nothing) + Case Else + SetWindowTheme(MemoTB.Handle, "Explorer", Nothing) + End Select End Sub Private Sub ViewCourse_Load(sender As Object, e As EventArgs) Handles Me.Load From e885b540eeb01eb09764df1e4eed024fc74d8da4 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 01:46:27 +0900 Subject: [PATCH 12/24] ui: enhance text color transition logic in CellControl --- utable/UserControls/CellControl.vb | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index f7b7a4c..6854f00 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -67,6 +67,10 @@ Public Class CellControl Private deltaColor_R As Integer = 1 Private deltaColor_G As Integer = 1 Private deltaColor_B As Integer = 1 + Private goalTextColor As Color = Color.Empty + Private deltaTextColor_R As Integer = 1 + Private deltaTextColor_G As Integer = 1 + Private deltaTextColor_B As Integer = 1 Private titleBounds As Rectangle = Rectangle.Empty Private titleHoverBounds As Rectangle = Rectangle.Empty @@ -486,6 +490,14 @@ Public Class CellControl deltaColor_R = CInt(Math.Abs((CInt(goalColor.R) - CInt(BackColor.R)) / 10)) deltaColor_G = CInt(Math.Abs((CInt(goalColor.G) - CInt(BackColor.G)) / 10)) deltaColor_B = CInt(Math.Abs((CInt(goalColor.B) - CInt(BackColor.B)) / 10)) + + '텍스트는 초기 배경색에서 최종 글자색으로 함께 전환한다. + goalTextColor = If(blackText, Color.Black, Color.White) + ForeColor = BackColor + deltaTextColor_R = CInt(Math.Abs((CInt(goalTextColor.R) - CInt(ForeColor.R)) / 10)) + deltaTextColor_G = CInt(Math.Abs((CInt(goalTextColor.G) - CInt(ForeColor.G)) / 10)) + deltaTextColor_B = CInt(Math.Abs((CInt(goalTextColor.B) - CInt(ForeColor.B)) / 10)) + fadeInProgress = True RaiseEvent FadeStarted(Me, EventArgs.Empty) End Sub @@ -498,7 +510,13 @@ Public Class CellControl Dim blue As Byte = MoveToward(BackColor.B, goalColor.B, deltaColor_B) BackColor = Color.FromArgb(red, green, blue) - If goalColor = BackColor Then fadeInProgress = False + Dim textRed As Byte = MoveToward(ForeColor.R, goalTextColor.R, deltaTextColor_R) + Dim textGreen As Byte = MoveToward(ForeColor.G, goalTextColor.G, deltaTextColor_G) + Dim textBlue As Byte = MoveToward(ForeColor.B, goalTextColor.B, deltaTextColor_B) + ForeColor = Color.FromArgb(textRed, textGreen, textBlue) + + If goalColor = BackColor AndAlso goalTextColor = ForeColor Then fadeInProgress = False + Invalidate() Return fadeInProgress End Function From 04239baea85bd154108b8a839630e36c23cc0159 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 01:50:48 +0900 Subject: [PATCH 13/24] ui: implement checkbox fade effect in CellControl rendering --- utable/UserControls/CellControl.vb | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index 6854f00..78cb2c6 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -1,4 +1,5 @@ Imports System.Drawing.Text +Imports System.Drawing.Imaging Imports System.Diagnostics Public Class CellControl @@ -71,6 +72,8 @@ Public Class CellControl Private deltaTextColor_R As Integer = 1 Private deltaTextColor_G As Integer = 1 Private deltaTextColor_B As Integer = 1 + Private checkBoxFadeAlpha As Byte = 255 + Private checkBoxFadeAlphaStep As Integer = 26 Private titleBounds As Rectangle = Rectangle.Empty Private titleHoverBounds As Rectangle = Rectangle.Empty @@ -466,7 +469,17 @@ Public Class CellControl image = If(blackText, If(TableForm.currentDPI = 96, My.Resources.check0_b_96, My.Resources.check0_b), If(TableForm.currentDPI = 96, My.Resources.check0_w_96, My.Resources.check0_w)) End If - graphics.DrawImage(image, bounds) + If checkBoxFadeAlpha = 255 Then + graphics.DrawImage(image, bounds) + Return + End If + + Using imageAttributes As New ImageAttributes() + Dim colorMatrix As New ColorMatrix() + colorMatrix.Matrix33 = checkBoxFadeAlpha / 255.0F + imageAttributes.SetColorMatrix(colorMatrix) + graphics.DrawImage(image, bounds, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes) + End Using End Sub Public Sub ModifyCheck(name As String, value As Boolean) @@ -497,6 +510,8 @@ Public Class CellControl deltaTextColor_R = CInt(Math.Abs((CInt(goalTextColor.R) - CInt(ForeColor.R)) / 10)) deltaTextColor_G = CInt(Math.Abs((CInt(goalTextColor.G) - CInt(ForeColor.G)) / 10)) deltaTextColor_B = CInt(Math.Abs((CInt(goalTextColor.B) - CInt(ForeColor.B)) / 10)) + checkBoxFadeAlpha = 0 + checkBoxFadeAlphaStep = CInt(Math.Ceiling(255 / 10.0)) fadeInProgress = True RaiseEvent FadeStarted(Me, EventArgs.Empty) @@ -515,7 +530,9 @@ Public Class CellControl Dim textBlue As Byte = MoveToward(ForeColor.B, goalTextColor.B, deltaTextColor_B) ForeColor = Color.FromArgb(textRed, textGreen, textBlue) - If goalColor = BackColor AndAlso goalTextColor = ForeColor Then fadeInProgress = False + checkBoxFadeAlpha = CByte(Math.Min(255, CInt(checkBoxFadeAlpha) + checkBoxFadeAlphaStep)) + + If goalColor = BackColor AndAlso goalTextColor = ForeColor AndAlso checkBoxFadeAlpha = 255 Then fadeInProgress = False Invalidate() Return fadeInProgress End Function From f303719f292e9bf2672402476274c86d3192ed4b Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 02:08:18 +0900 Subject: [PATCH 14/24] ui: update ToolStripComboBox colors for better dark mode support --- utable/Modules/ThemeColors.vb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/utable/Modules/ThemeColors.vb b/utable/Modules/ThemeColors.vb index b1564b0..f0502a4 100644 --- a/utable/Modules/ThemeColors.vb +++ b/utable/Modules/ThemeColors.vb @@ -79,6 +79,17 @@ Public NotInheritable Class MenuThemeRenderer For Each item As ToolStripItem In menu.Items item.ForeColor = theme.Text + 'ToolStripComboBox는 호스트 항목과 실제 ComboBox의 색상이 별도로 관리된다. + '호스트의 ForeColor만 변경하면 다크 모드에서 드롭다운은 흰 배경을 유지해 + '목록 글자가 보이지 않게 된다. + Dim comboBoxItem As ToolStripComboBox = TryCast(item, ToolStripComboBox) + If comboBoxItem IsNot Nothing Then + comboBoxItem.BackColor = theme.Background + comboBoxItem.ForeColor = theme.Text + comboBoxItem.ComboBox.BackColor = theme.Background + comboBoxItem.ComboBox.ForeColor = theme.Text + End If + Dim menuItem As ToolStripMenuItem = TryCast(item, ToolStripMenuItem) If menuItem IsNot Nothing AndAlso menuItem.HasDropDownItems Then ApplyToStrip(menuItem.DropDown, renderer, theme) From 1676de1832035b27d31226033e2d1e6535c57f36 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 02:17:57 +0900 Subject: [PATCH 15/24] fix: prevent endless cell fade rendering and reduce idle CPU usage --- utable/Forms/TableForm.vb | 22 ++++++-- utable/UserControls/CellControl.Designer.vb | 7 ++- utable/UserControls/CellControl.vb | 62 +++++++++++++++------ 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index 1a47a56..98bb1ec 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -17,6 +17,7 @@ Public Class TableForm Dim courseData As New List(Of String) Dim courseRecords As New List(Of TableCourse) Private ReadOnly cellsByCourse As New Dictionary(Of TableCourse, CellControl) + Private ReadOnly fadingCells As New HashSet(Of CellControl) Private courseLayoutQueued As Boolean = False Private WithEvents courseFadeTimer As New Timer With {.Interval = 30} @@ -926,6 +927,7 @@ Public Class TableForm Public Sub updateCell() courseFadeTimer.Stop() + fadingCells.Clear() SuspendCourseLayouts() Try @@ -1215,17 +1217,29 @@ Public Class TableForm End Sub Private Sub CellFadeStarted(sender As Object, e As EventArgs) + Dim cell As CellControl = TryCast(sender, CellControl) + If cell Is Nothing OrElse cell.IsDisposed Then Exit Sub + + fadingCells.Add(cell) If Not courseFadeTimer.Enabled Then courseFadeTimer.Start() End Sub Private Sub courseFadeTimer_Tick(sender As Object, e As EventArgs) Handles courseFadeTimer.Tick - Dim hasActiveFade As Boolean = False + If fadingCells.Count = 0 Then + courseFadeTimer.Stop() + Exit Sub + End If + + Dim completedCells As New List(Of CellControl) + For Each cell As CellControl In fadingCells + If cell.IsDisposed OrElse Not cell.AdvanceFade() Then completedCells.Add(cell) + Next - For Each cell As CellControl In cellsByCourse.Values - If cell.AdvanceFade() Then hasActiveFade = True + For Each cell As CellControl In completedCells + fadingCells.Remove(cell) Next - If Not hasActiveFade Then courseFadeTimer.Stop() + If fadingCells.Count = 0 Then courseFadeTimer.Stop() End Sub #End Region diff --git a/utable/UserControls/CellControl.Designer.vb b/utable/UserControls/CellControl.Designer.vb index 36c8677..14dc0c3 100644 --- a/utable/UserControls/CellControl.Designer.vb +++ b/utable/UserControls/CellControl.Designer.vb @@ -5,7 +5,12 @@ Partial Class CellControl Protected Overrides Sub Dispose(ByVal disposing As Boolean) Try - If disposing Then DisposeRenderFonts() + If disposing Then + hoverAnimationTimer.Stop() + RemoveHandler hoverAnimationTimer.Tick, AddressOf HoverAnimationTimer_Tick + hoverAnimationTimer.Dispose() + DisposeRenderFonts() + End If Finally MyBase.Dispose(disposing) End Try diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index 78cb2c6..cafd44d 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -332,12 +332,14 @@ Public Class CellControl 'Smoothstep: 양 끝에서 속도가 0이 되는 ease-in-out 보간 Dim easedProgress As Double = progress * progress * (3.0 - 2.0 * progress) Dim previousBounds As Rectangle = Bounds + Dim nextBounds As Rectangle = InterpolateBounds(hoverAnimationStartBounds, hoverAnimationTargetBounds, easedProgress) - Bounds = InterpolateBounds(hoverAnimationStartBounds, hoverAnimationTargetBounds, easedProgress) - - Invalidate() - If Parent IsNot Nothing Then - Parent.Invalidate(Rectangle.Union(previousBounds, Bounds), True) + If previousBounds <> nextBounds Then + Bounds = nextBounds + Invalidate() + If Parent IsNot Nothing Then + Parent.Invalidate(Rectangle.Union(previousBounds, nextBounds), True) + End If End If If progress >= 1.0 Then @@ -500,16 +502,16 @@ Public Class CellControl End Sub Private Sub BeginFade() - deltaColor_R = CInt(Math.Abs((CInt(goalColor.R) - CInt(BackColor.R)) / 10)) - deltaColor_G = CInt(Math.Abs((CInt(goalColor.G) - CInt(BackColor.G)) / 10)) - deltaColor_B = CInt(Math.Abs((CInt(goalColor.B) - CInt(BackColor.B)) / 10)) + deltaColor_R = CalculateFadeStep(BackColor.R, goalColor.R) + deltaColor_G = CalculateFadeStep(BackColor.G, goalColor.G) + deltaColor_B = CalculateFadeStep(BackColor.B, goalColor.B) '텍스트는 초기 배경색에서 최종 글자색으로 함께 전환한다. goalTextColor = If(blackText, Color.Black, Color.White) ForeColor = BackColor - deltaTextColor_R = CInt(Math.Abs((CInt(goalTextColor.R) - CInt(ForeColor.R)) / 10)) - deltaTextColor_G = CInt(Math.Abs((CInt(goalTextColor.G) - CInt(ForeColor.G)) / 10)) - deltaTextColor_B = CInt(Math.Abs((CInt(goalTextColor.B) - CInt(ForeColor.B)) / 10)) + deltaTextColor_R = CalculateFadeStep(ForeColor.R, goalTextColor.R) + deltaTextColor_G = CalculateFadeStep(ForeColor.G, goalTextColor.G) + deltaTextColor_B = CalculateFadeStep(ForeColor.B, goalTextColor.B) checkBoxFadeAlpha = 0 checkBoxFadeAlphaStep = CInt(Math.Ceiling(255 / 10.0)) @@ -523,24 +525,48 @@ Public Class CellControl Dim red As Byte = MoveToward(BackColor.R, goalColor.R, deltaColor_R) Dim green As Byte = MoveToward(BackColor.G, goalColor.G, deltaColor_G) Dim blue As Byte = MoveToward(BackColor.B, goalColor.B, deltaColor_B) - BackColor = Color.FromArgb(red, green, blue) + Dim nextBackColor As Color = Color.FromArgb(red, green, blue) + Dim backgroundChanged As Boolean = (BackColor.ToArgb() <> nextBackColor.ToArgb()) + If backgroundChanged Then BackColor = nextBackColor Dim textRed As Byte = MoveToward(ForeColor.R, goalTextColor.R, deltaTextColor_R) Dim textGreen As Byte = MoveToward(ForeColor.G, goalTextColor.G, deltaTextColor_G) Dim textBlue As Byte = MoveToward(ForeColor.B, goalTextColor.B, deltaTextColor_B) - ForeColor = Color.FromArgb(textRed, textGreen, textBlue) + Dim nextForeColor As Color = Color.FromArgb(textRed, textGreen, textBlue) + Dim textChanged As Boolean = (ForeColor.ToArgb() <> nextForeColor.ToArgb()) + If textChanged Then ForeColor = nextForeColor + Dim previousCheckBoxFadeAlpha As Byte = checkBoxFadeAlpha checkBoxFadeAlpha = CByte(Math.Min(255, CInt(checkBoxFadeAlpha) + checkBoxFadeAlphaStep)) - If goalColor = BackColor AndAlso goalTextColor = ForeColor AndAlso checkBoxFadeAlpha = 255 Then fadeInProgress = False - Invalidate() + Dim backgroundComplete As Boolean = (red = goalColor.R AndAlso green = goalColor.G AndAlso blue = goalColor.B) + Dim textComplete As Boolean = (textRed = goalTextColor.R AndAlso textGreen = goalTextColor.G AndAlso textBlue = goalTextColor.B) + fadeInProgress = Not (backgroundComplete AndAlso textComplete AndAlso checkBoxFadeAlpha = 255) + + 'BackColor/ForeColor 변경은 컨트롤 자체를 무효화한다. 체크박스 알파만 바뀐 경우만 직접 요청한다. + If Not backgroundChanged AndAlso + Not textChanged AndAlso + previousCheckBoxFadeAlpha <> checkBoxFadeAlpha Then + Invalidate() + End If Return fadeInProgress End Function + Private Shared Function CalculateFadeStep(currentValue As Byte, targetValue As Byte) As Integer + Dim distance As Integer = Math.Abs(CInt(targetValue) - CInt(currentValue)) + If distance = 0 Then Return 0 + Return Math.Max(1, CInt(Math.Ceiling(distance / 10.0))) + End Function + Private Function MoveToward(currentValue As Byte, targetValue As Byte, delta As Integer) As Byte - If targetValue - delta > currentValue Then Return CByte(currentValue + delta) - If targetValue + delta < currentValue Then Return CByte(currentValue - delta) - Return targetValue + If currentValue = targetValue Then Return targetValue + + '잘못된 호출에서 delta가 0이어도 애니메이션이 영원히 멈춰 있지 않게 한다. + Dim safeDelta As Integer = Math.Max(1, delta) + If currentValue < targetValue Then + Return CByte(Math.Min(CInt(targetValue), CInt(currentValue) + safeDelta)) + End If + Return CByte(Math.Max(CInt(targetValue), CInt(currentValue) - safeDelta)) End Function Private Function ScaleValue(value As Integer) As Integer From 5811292f45167780bf14fac74d9a1a38bb452b09 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 09:49:14 +0900 Subject: [PATCH 16/24] refactor: consolidate CellControl settings into CellControlSettings class --- utable/Forms/OptionForm.vb | 14 +----- utable/Forms/TableForm.vb | 44 ++---------------- utable/Models/CellControlSettings.vb | 35 +++++++++++++++ utable/UserControls/CellControl.vb | 67 ++++++++++------------------ utable/uTable.vbproj | 1 + 5 files changed, 65 insertions(+), 96 deletions(-) create mode 100644 utable/Models/CellControlSettings.vb diff --git a/utable/Forms/OptionForm.vb b/utable/Forms/OptionForm.vb index 67119b5..f70669c 100644 --- a/utable/Forms/OptionForm.vb +++ b/utable/Forms/OptionForm.vb @@ -533,7 +533,7 @@ Public Class OptionForm Dim cell As New CellControl With cell - .Name = "DemoCellControl" + .IsDemo = True .Dock = DockStyle.Top .Height = PrevTableArea.Height * 0.6 .defHeight = PrevTableArea.Height * 0.6 @@ -545,17 +545,7 @@ Public Class OptionForm .ProfessorText = names(rnd.Next(0, names.Count)) + " 교수님" .MemoText = "메모 내용" - .FadeEffect = GetINI("SETTING", "FadeEffect", "", ININamePath) - .CustomFont = GetINI("SETTING", "CustomFont", "", ININamePath) - .CustomFontName = GetINI("SETTING", "CustomFontName", "", ININamePath) - .AutoTextColor = GetINI("SETTING", "AutoTextColor", "", ININamePath) - ._BlackText = GetINI("SETTING", "BlackText", "", ININamePath) - ._AlwaysExpand = GetINI("SETTING", "AlwaysExpand", "", ININamePath) - .ExpandCell = GetINI("SETTING", "ExpandCell", "", ININamePath) - .ExpandAnimation = GetINI("SETTING", "ExpandAnimation", "", ININamePath) - .ShowMemo = GetINI("SETTING", "ShowMemo", "", ININamePath) - .ShowProf = GetINI("SETTING", "ShowProf", "", ININamePath) - ._ShowChkBox = GetINI("SETTING", "ShowChkBox", "", ININamePath) + .Settings = CellControlSettings.FromIni() .goalColor = Color.DarkSlateGray End With diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index 98bb1ec..28c26e4 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -41,18 +41,7 @@ Public Class TableForm Dim prevNotificationName As String = Nothing Dim prevTime As Date = Nothing - 'CellControl용 설정 변수 - Public FadeEffect As String = "" - Public CustomFont As String = "" - Public CustomFontName As String = "" - Public AutoTextColor As String = "" - Public _BlackText As String = "" - Public _AlwaysExpand As String = "" - Public ExpandCell As String = "" - Public ExpandAnimation As String = "" - Public ShowMemo As String = "" - Public ShowProf As String = "" - Public _ShowChkBox As String = "" + Private cellSettings As New CellControlSettings() 'Dim disablePatternDrawOnce As Boolean = False Public tablePatternSetting As String = Nothing @@ -931,22 +920,7 @@ Public Class TableForm SuspendCourseLayouts() Try - 'CellControl 설정 변수 업데이트 - FadeEffect = GetINI("SETTING", "FadeEffect", "", ININamePath) - CustomFont = GetINI("SETTING", "CustomFont", "", ININamePath) - CustomFontName = GetINI("SETTING", "CustomFontName", "", ININamePath) - AutoTextColor = GetINI("SETTING", "AutoTextColor", "", ININamePath) - _BlackText = GetINI("SETTING", "BlackText", "", ININamePath) - _AlwaysExpand = GetINI("SETTING", "AlwaysExpand", "", ININamePath) - ExpandCell = GetINI("SETTING", "ExpandCell", "", ININamePath) - ExpandAnimation = GetINI("SETTING", "ExpandAnimation", "", ININamePath) - If String.IsNullOrEmpty(ExpandAnimation) Then - ExpandAnimation = "1" - SetINI("SETTING", "ExpandAnimation", ExpandAnimation, ININamePath) - End If - ShowMemo = GetINI("SETTING", "ShowMemo", "", ININamePath) - ShowProf = GetINI("SETTING", "ShowProf", "", ININamePath) - _ShowChkBox = GetINI("SETTING", "ShowChkBox", "", ININamePath) + cellSettings = CellControlSettings.FromIni() TimeTable.Visible = False showSaturday = False @@ -1106,17 +1080,7 @@ Public Class TableForm AddHandler cell.FadeStarted, AddressOf CellFadeStarted With cell - .FadeEffect = FadeEffect - .CustomFont = CustomFont - .CustomFontName = CustomFontName - .AutoTextColor = AutoTextColor - ._BlackText = _BlackText - ._AlwaysExpand = _AlwaysExpand - .ExpandCell = ExpandCell - .ExpandAnimation = ExpandAnimation - .ShowMemo = ShowMemo - .ShowProf = ShowProf - ._ShowChkBox = _ShowChkBox + .Settings = cellSettings .UsesSharedFadeClock = True End With @@ -1191,7 +1155,7 @@ Public Class TableForm cell.Height = CInt(part * parentPanel.ClientSize.Height) cell.defHeight = CInt(part * parentPanel.ClientSize.Height) - If cell.alwaysExpand Then cell.ForceExpand() + If cell.Settings.AlwaysExpand Then cell.ForceExpand() End Sub Private Sub UpdateCourseBounds() diff --git a/utable/Models/CellControlSettings.vb b/utable/Models/CellControlSettings.vb new file mode 100644 index 0000000..0a88375 --- /dev/null +++ b/utable/Models/CellControlSettings.vb @@ -0,0 +1,35 @@ +Public Class CellControlSettings + Public Property FadeEffect As Boolean = True + Public Property UseCustomFont As Boolean = False + Public Property CustomFontName As String = "" + Public Property AutoTextColor As Boolean = True + Public Property BlackText As Boolean = False + Public Property AlwaysExpand As Boolean = False + Public Property ExpandOnHover As Boolean = True + Public Property ExpandAnimation As Boolean = True + Public Property ShowMemo As Boolean = True + Public Property ShowProfessor As Boolean = True + Public Property ShowCheckBox As Boolean = True + + Public Shared Function FromIni() As CellControlSettings + Return New CellControlSettings With { + .FadeEffect = ReadEnabled("FadeEffect", True), + .UseCustomFont = ReadEnabled("CustomFont", False), + .CustomFontName = GetINI("SETTING", "CustomFontName", "", ININamePath), + .AutoTextColor = ReadEnabled("AutoTextColor", True), + .BlackText = ReadEnabled("BlackText", False), + .AlwaysExpand = ReadEnabled("AlwaysExpand", False), + .ExpandOnHover = ReadEnabled("ExpandCell", True), + .ExpandAnimation = ReadEnabled("ExpandAnimation", True), + .ShowMemo = ReadEnabled("ShowMemo", True), + .ShowProfessor = ReadEnabled("ShowProf", True), + .ShowCheckBox = ReadEnabled("ShowChkBox", True) + } + End Function + + Private Shared Function ReadEnabled(settingName As String, defaultValue As Boolean) As Boolean + Dim value As String = GetINI("SETTING", settingName, "", ININamePath) + If String.IsNullOrWhiteSpace(value) Then Return defaultValue + Return value <> "0" + End Function +End Class diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index cafd44d..dea14da 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -5,7 +5,6 @@ Imports System.Diagnostics Public Class CellControl Public defHeight As Integer = 0 Public defLoc As Integer = 0 - Public alwaysExpand As Boolean = False Public checked As Boolean = False Public dayNum As Integer = 0 @@ -15,17 +14,8 @@ Public Class CellControl Public ProfessorText As String = "" Public MemoText As String = "" - Public FadeEffect As String = "" - Public CustomFont As String = "" - Public CustomFontName As String = "" - Public AutoTextColor As String = "" - Public _BlackText As String = "" - Public _AlwaysExpand As String = "" - Public ExpandCell As String = "" - Public ExpandAnimation As String = "" - Public ShowMemo As String = "" - Public ShowProf As String = "" - Public _ShowChkBox As String = "" + Public Property Settings As New CellControlSettings() + Public Property IsDemo As Boolean = False Public UsesSharedFadeClock As Boolean = False Public goalColor As Color = Nothing Public Event FadeStarted As EventHandler @@ -54,11 +44,6 @@ Public Class CellControl End Get End Property - Private doExpand As Boolean = True - Private doExpandAnimation As Boolean = True - Private showChkBox As Boolean = True - Private showMemoText As Boolean = True - Private showProfessor As Boolean = True Private blackText As Boolean = False Private hovered As Boolean = False Private checkHovered As Boolean = False @@ -96,24 +81,18 @@ Public Class CellControl End Sub Private Sub CellControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load - If Not AutoTextColor = "0" Then + If Settings.AutoTextColor Then blackText = CheckProperColor(goalColor) Else - blackText = (_BlackText = "1") + blackText = Settings.BlackText End If ForeColor = If(blackText, Color.Black, Color.White) - alwaysExpand = (_AlwaysExpand = "1") - doExpand = Not (ExpandCell = "0") - doExpandAnimation = Not (ExpandAnimation = "0") - showMemoText = Not (ShowMemo = "0") - showProfessor = Not (ShowProf = "0") - showChkBox = Not (_ShowChkBox = "0") CreateRenderFonts() - If alwaysExpand Then ForceExpand() + If Settings.AlwaysExpand Then ForceExpand() - If Not FadeEffect = "0" AndAlso UsesSharedFadeClock Then + If Settings.FadeEffect AndAlso UsesSharedFadeClock Then BackColor = If(dayNum Mod 2 = 0, TableForm.MonPanel.BackColor, TableForm.TuePanel.BackColor) BeginFade() Else @@ -138,7 +117,7 @@ Public Class CellControl Dim checkBounds As New Rectangle(HorizontalPadding, y, CheckSize, CheckSize) Dim timeX As Integer = HorizontalPadding - If showChkBox Then + If Settings.ShowCheckBox Then DrawCheckBox(e.Graphics, checkBounds) timeX += CheckSize + 3 End If @@ -161,13 +140,13 @@ Public Class CellControl DrawWrappedText(e.Graphics, CourseTitle, If(checked, titleStrikeoutFont, titleFont), titleBounds) y += titleBounds.Height - If showProfessor AndAlso Not String.IsNullOrEmpty(ProfessorText) Then + If Settings.ShowProfessor AndAlso Not String.IsNullOrEmpty(ProfessorText) Then Dim professorBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(e.Graphics, ProfessorText, bodyFont, contentWidth)) DrawWrappedText(e.Graphics, ProfessorText, bodyFont, professorBounds) y += professorBounds.Height End If - If showMemoText AndAlso Not String.IsNullOrEmpty(MemoText) Then + If Settings.ShowMemo AndAlso Not String.IsNullOrEmpty(MemoText) Then Dim memoBounds As New Rectangle(HorizontalPadding, y, contentWidth, MeasureTextHeight(e.Graphics, MemoText, memoFont, contentWidth)) DrawWrappedText(e.Graphics, MemoText, memoFont, memoBounds) End If @@ -198,7 +177,7 @@ Public Class CellControl Protected Overrides Sub OnMouseEnter(e As EventArgs) MyBase.OnMouseEnter(e) Dim mouseLocation As Point = PointToClient(Cursor.Position) - checkHovered = showChkBox AndAlso New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize).Contains(mouseLocation) + checkHovered = Settings.ShowCheckBox AndAlso New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize).Contains(mouseLocation) titleHovered = titleHoverBounds.Contains(mouseLocation) SetHovered(True) Invalidate() @@ -220,7 +199,7 @@ Public Class CellControl Protected Overrides Sub OnMouseMove(e As MouseEventArgs) MyBase.OnMouseMove(e) - Dim newCheckHovered As Boolean = showChkBox AndAlso New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize).Contains(e.Location) + Dim newCheckHovered As Boolean = Settings.ShowCheckBox AndAlso New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize).Contains(e.Location) Dim newTitleHovered As Boolean = titleHoverBounds.Contains(e.Location) If checkHovered <> newCheckHovered OrElse titleHovered <> newTitleHovered Then checkHovered = newCheckHovered @@ -234,7 +213,7 @@ Public Class CellControl If e.Button <> MouseButtons.Left Then Return Dim checkBounds As New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize) - If showChkBox AndAlso checkBounds.Contains(e.Location) Then + If Settings.ShowCheckBox AndAlso checkBounds.Contains(e.Location) Then checkPressed = True Invalidate() End If @@ -253,11 +232,11 @@ Public Class CellControl If e.Button <> MouseButtons.Left Then Return Dim checkBounds As New Rectangle(HorizontalPadding, NotchHeight + VerticalPadding, CheckSize, CheckSize) - If showChkBox AndAlso (checkBounds.Contains(e.Location) OrElse e.Location.Y < titleBounds.Top) Then + If Settings.ShowCheckBox AndAlso (checkBounds.Contains(e.Location) OrElse e.Location.Y < titleBounds.Top) Then ToggleCheck() ElseIf titleBounds.Contains(e.Location) Then OpenCourseDetails() - ElseIf showChkBox Then + ElseIf Settings.ShowCheckBox Then ToggleCheck() End If End Sub @@ -270,13 +249,13 @@ Public Class CellControl BringToFront() End If - If doExpand OrElse alwaysExpand Then + If Settings.ExpandOnHover OrElse Settings.AlwaysExpand Then '항상 확장 상태도 평소에는 실제 시작 시간(defLoc)을 유지하고, '호버 중에만 시간표 하단을 넘는 경우 위쪽으로 이동한다. Dim targetBounds As Rectangle = If(hovered, GetExpandedBounds(), - If(alwaysExpand, GetAlwaysExpandedBounds(), GetDefaultBounds())) - If doExpandAnimation Then + If(Settings.AlwaysExpand, GetAlwaysExpandedBounds(), GetDefaultBounds())) + If Settings.ExpandAnimation Then StartHoverAnimation(targetBounds) Else hoverAnimationTimer.Stop() @@ -377,12 +356,12 @@ Public Class CellControl Private Sub ToggleCheck() checked = Not checked - If Name <> "DemoCellControl" Then ModifyCheck(Name, checked) + If Not IsDemo Then ModifyCheck(checked) Invalidate() End Sub Private Sub OpenCourseDetails() - If Name = "DemoCellControl" Then Return + If IsDemo Then Return Dim appearPoint As New Point(Cursor.Position) ViewCourse.Close() @@ -404,8 +383,8 @@ Public Class CellControl Using graphics As Graphics = CreateGraphics() result += MeasureTextHeight(graphics, CourseTitle, titleFont, contentWidth) - If showProfessor Then result += MeasureTextHeight(graphics, ProfessorText, bodyFont, contentWidth) - If showMemoText Then result += MeasureTextHeight(graphics, MemoText, memoFont, contentWidth) + If Settings.ShowProfessor Then result += MeasureTextHeight(graphics, ProfessorText, bodyFont, contentWidth) + If Settings.ShowMemo Then result += MeasureTextHeight(graphics, MemoText, memoFont, contentWidth) End Using result += timeFont.Height + VerticalPadding @@ -484,7 +463,7 @@ Public Class CellControl End Using End Sub - Public Sub ModifyCheck(name As String, value As Boolean) + Public Sub ModifyCheck(value As Boolean) Dim data As String = readTable() Dim olddata As String = TryCast(Tag, String) If String.IsNullOrEmpty(olddata) Then Return @@ -579,7 +558,7 @@ Public Class CellControl Private Sub CreateRenderFonts() DisposeRenderFonts() - Dim familyName As String = If(CustomFont = "1" AndAlso Not String.IsNullOrWhiteSpace(CustomFontName), CustomFontName, Font.FontFamily.Name) + Dim familyName As String = If(Settings.UseCustomFont AndAlso Not String.IsNullOrWhiteSpace(Settings.CustomFontName), Settings.CustomFontName, Font.FontFamily.Name) timeFont = New Font(familyName, 9.75F, FontStyle.Bold) titleFont = New Font(familyName, 11.0F, FontStyle.Bold) titleStrikeoutFont = New Font(familyName, 11.0F, FontStyle.Bold Or FontStyle.Strikeout) diff --git a/utable/uTable.vbproj b/utable/uTable.vbproj index 9040b60..9ad969c 100644 --- a/utable/uTable.vbproj +++ b/utable/uTable.vbproj @@ -297,6 +297,7 @@ + True From 31bdf70fca27a630a0077e0799fe83de7b04519e Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 09:55:22 +0900 Subject: [PATCH 17/24] refactor: add & move model classes for course and feed entries --- utable/Forms/EveryTimeBrowserNew.vb | 9 --------- utable/Models/EverytimeCourse.vb | 9 +++++++++ utable/Models/ReleaseFeedEntry.vb | 4 ++++ utable/Models/TableCourse.vb | 16 ++++++++++++++++ utable/Modules/DataModule.vb | 22 ---------------------- utable/uTable.vbproj | 3 +++ 6 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 utable/Models/EverytimeCourse.vb create mode 100644 utable/Models/ReleaseFeedEntry.vb create mode 100644 utable/Models/TableCourse.vb diff --git a/utable/Forms/EveryTimeBrowserNew.vb b/utable/Forms/EveryTimeBrowserNew.vb index aa31d31..9149fd0 100644 --- a/utable/Forms/EveryTimeBrowserNew.vb +++ b/utable/Forms/EveryTimeBrowserNew.vb @@ -227,12 +227,3 @@ Public Class EveryTimeBrowserNew Process.Start("https://utable.sw.pbj.kr/everytime-troubleshooting") End Sub End Class -Public Class EverytimeCourse - Public Property Day As Integer - Public Property Name As String - Public Property Professor As String - Public Property Memo As String - Public Property Start As Integer - Public Property Duration As Integer - Public Property ColorNumber As Integer -End Class diff --git a/utable/Models/EverytimeCourse.vb b/utable/Models/EverytimeCourse.vb new file mode 100644 index 0000000..1f3f504 --- /dev/null +++ b/utable/Models/EverytimeCourse.vb @@ -0,0 +1,9 @@ +Public Class EverytimeCourse + Public Property Day As Integer + Public Property Name As String + Public Property Professor As String + Public Property Memo As String + Public Property Start As Integer + Public Property Duration As Integer + Public Property ColorNumber As Integer +End Class diff --git a/utable/Models/ReleaseFeedEntry.vb b/utable/Models/ReleaseFeedEntry.vb new file mode 100644 index 0000000..0fc4d52 --- /dev/null +++ b/utable/Models/ReleaseFeedEntry.vb @@ -0,0 +1,4 @@ +Friend Class ReleaseFeedEntry + Public Property Title As String + Public Property ContentHtml As String +End Class diff --git a/utable/Models/TableCourse.vb b/utable/Models/TableCourse.vb new file mode 100644 index 0000000..6dd7baa --- /dev/null +++ b/utable/Models/TableCourse.vb @@ -0,0 +1,16 @@ +'시간표 과목의 메모리 표현 - 값은 기존 함수와 호환되도록 XML-encoded 상태로 보관 +Friend Class TableCourse + Public Property RawData As String + Public Property Day As String + Public Property Name As String + Public Property Professor As String + Public Property Memo As String + Public Property Start As String + Public Property [End] As String + Public Property Color As String + Public Property Checked As String + + Public Function Identity() As String + Return Day + "-" + Start + "-" + Name + End Function +End Class diff --git a/utable/Modules/DataModule.vb b/utable/Modules/DataModule.vb index 50cd6f5..e3f8811 100644 --- a/utable/Modules/DataModule.vb +++ b/utable/Modules/DataModule.vb @@ -4,28 +4,6 @@ Imports System.Web Imports System.Xml Imports System.Xml.Linq -Friend Class ReleaseFeedEntry - Public Property Title As String - Public Property ContentHtml As String -End Class - -'시간표 과목의 메모리 표현 - 값은 기존 함수와 호환되도록 XML-encoded 상태로 보관 -Friend Class TableCourse - Public Property RawData As String - Public Property Day As String - Public Property Name As String - Public Property Professor As String - Public Property Memo As String - Public Property Start As String - Public Property [End] As String - Public Property Color As String - Public Property Checked As String - - Public Function Identity() As String - Return Day + "-" + Start + "-" + Name - End Function -End Class - Module DataModule Private Const TableFragmentRootName As String = "utable-fragment" Private ReadOnly Utf8WithoutBom As New System.Text.UTF8Encoding(False) diff --git a/utable/uTable.vbproj b/utable/uTable.vbproj index 9ad969c..e27f3e9 100644 --- a/utable/uTable.vbproj +++ b/utable/uTable.vbproj @@ -298,6 +298,9 @@ + + + True From a0293fdb18811d214d9bc5773fb2299495268409 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 10:17:00 +0900 Subject: [PATCH 18/24] fix: prevent premature execution of settings updates in OptionForm and TableForm --- utable/Forms/OptionForm.vb | 5 +++ utable/Forms/TableForm.vb | 63 +++++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/utable/Forms/OptionForm.vb b/utable/Forms/OptionForm.vb index f70669c..65ecf0f 100644 --- a/utable/Forms/OptionForm.vb +++ b/utable/Forms/OptionForm.vb @@ -427,6 +427,7 @@ Public Class OptionForm Private Sub FixStartTimePicker_ValueChanged(sender As Object, e As EventArgs) Handles FixStartTimePicker.ValueChanged + If Not loaded Then Return SetINI("SETTING", "FixStartTimeValue", (FixStartTimePicker.Value.Hour * 60 + FixStartTimePicker.Value.Minute).ToString, ININamePath) End Sub @@ -465,6 +466,8 @@ Public Class OptionForm End Sub Private Sub ShowLinePatternChk_CheckedChanged(sender As Object, e As EventArgs) Handles ShowLinePatternChk.CheckedChanged + If Not loaded Then Return + '줄긋는거는 그냥 Panel의 Drawing만 Refresh하면 되는거라서 테이블을 다시 그리지는 않을거임 If ShowLinePatternChk.Checked Then SetINI("SETTING", "TablePattern", "DottedLine", ININamePath) @@ -631,6 +634,8 @@ Public Class OptionForm End Sub Private Sub D_ThemeRbt_CheckedChanged(sender As Object, e As EventArgs) Handles D_ThemeRbt.CheckedChanged + If Not loaded Then Return + If D_ThemeRbt.Checked Then SetINI("SETTING", "ColorMode", "Dark", ININamePath) Else diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index 28c26e4..281af7b 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -20,6 +20,7 @@ Public Class TableForm Private ReadOnly fadingCells As New HashSet(Of CellControl) Private courseLayoutQueued As Boolean = False Private WithEvents courseFadeTimer As New Timer With {.Interval = 30} + Private WithEvents windowStateSaveTimer As New Timer With {.Interval = 400} '슬라이딩 애니메이션용 변수 Dim poscount As Integer = 0 @@ -30,6 +31,7 @@ Public Class TableForm Dim PrevDisp As String = Nothing Dim PrevDay As Date = Nothing Dim formshown As Boolean = False + Dim windowStateSavePending As Boolean = False Dim snaptoedge As Boolean = False Dim titleEditMode As Boolean = False Dim colorMode As String = Nothing @@ -262,20 +264,35 @@ Public Class TableForm End Sub Private Sub TableForm_LocationChanged(sender As Object, e As EventArgs) Handles MyBase.LocationChanged, MyBase.SizeChanged + If Not formshown OrElse hiding Then Return - If formshown And Not hiding Then - SetINI("SETTING", "WindowPosition", Location.X.ToString + "," + Location.Y.ToString, ININamePath) - SetINI("SETTING", "WindowSize", Width.ToString + "," + Height.ToString, ININamePath) + windowStateSavePending = True + windowStateSaveTimer.Stop() + windowStateSaveTimer.Start() + End Sub - If Not PrevDisp = Screen.FromControl(Me).DeviceName.ToString Then - SetINI("SETTING", "WindowScreen", Screen.FromControl(Me).DeviceName.ToString, ININamePath) - PrevDisp = Screen.FromControl(Me).DeviceName.ToString - End If + Private Sub WindowStateSaveTimer_Tick(sender As Object, e As EventArgs) Handles windowStateSaveTimer.Tick + windowStateSaveTimer.Stop() + SaveWindowState() + End Sub + + Private Sub TableForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing + SaveWindowState() + End Sub + + Private Sub SaveWindowState() + If Not windowStateSavePending Then Return - 'If Application.OpenForms().OfType(Of SetCourse).Any Then _ - ' If Not SetCourse.modifyMode Then SetCourse.SetDesktopLocation(Location.X + Button1.Location.X, Location.Y + DayTable.Location.Y) + SetINI("SETTING", "WindowPosition", Location.X.ToString + "," + Location.Y.ToString, ININamePath) + SetINI("SETTING", "WindowSize", Width.ToString + "," + Height.ToString, ININamePath) + Dim currentDisplay As String = Screen.FromControl(Me).DeviceName + If PrevDisp <> currentDisplay Then + SetINI("SETTING", "WindowScreen", currentDisplay, ININamePath) + PrevDisp = currentDisplay End If + + windowStateSavePending = False End Sub Private Sub TableForm_MouseLeave(sender As Object, e As EventArgs) Handles Me.MouseLeave @@ -452,8 +469,10 @@ Public Class TableForm Private Sub TableForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load + Dim savedDisplay As String = GetINI("SETTING", "WindowScreen", "", ININamePath) + If String.IsNullOrEmpty(savedDisplay) Then savedDisplay = GetINI("SETTING", "WindowDisplay", "", ININamePath) For Each scrn In Screen.AllScreens - If scrn.DeviceName = GetINI("SETTING", "WindowDisplay", "", ININamePath) Then + If scrn.DeviceName = savedDisplay Then Me.Location = scrn.Bounds.Location Exit For End If @@ -1692,7 +1711,7 @@ Public Class TableForm If hasSavedZoom Then MemoRTB.ZoomFactor = savedZoom - UpdateMemoZoomFactor() + UpdateMemoZoomDisplay() End If Catch ex As Exception @@ -1806,8 +1825,7 @@ Public Class TableForm End Sub - Private Sub UpdateMemoZoomFactor() - SetINI("SETTING", "MemoZoom", MemoRTB.ZoomFactor.ToString, ININamePath) + Private Sub UpdateMemoZoomDisplay() MemoZoomNumBT.Text = Convert.ToInt32((MemoRTB.ZoomFactor * 100)).ToString + "%" If MemoRTB.ZoomFactor = 1 Then MemoZoomNumBT.ForeColor = Color.Gray @@ -1816,6 +1834,11 @@ Public Class TableForm End If End Sub + Private Sub SaveMemoZoom() + SetINI("SETTING", "MemoZoom", MemoRTB.ZoomFactor.ToString, ININamePath) + UpdateMemoZoomDisplay() + End Sub + Private Sub MemoMenuBT_Click(sender As Object, e As EventArgs) Handles MemoMenuBT.Click Memo_menu.Show(Cursor.Position.X, Cursor.Position.Y) End Sub @@ -1824,29 +1847,21 @@ Public Class TableForm MemoAutoSave() End Sub - Private Sub MemoRTB_KeyDown(sender As Object, e As KeyEventArgs) Handles MemoRTB.KeyDown, Me.KeyDown - UpdateMemoZoomFactor() - End Sub - - Private Sub MemoRTB_KeyUp(sender As Object, e As KeyEventArgs) Handles MemoRTB.KeyUp, Me.KeyUp - UpdateMemoZoomFactor() - End Sub - Private Sub MemoZoomNumBT_Click(sender As Object, e As EventArgs) Handles MemoZoomNumBT.Click MemoRTB.ZoomFactor = 1 - UpdateMemoZoomFactor() + SaveMemoZoom() End Sub Private Sub MemoZoomBT1_Click(sender As Object, e As EventArgs) Handles MemoZoomBT1.Click If MemoRTB.ZoomFactor > 0.015625 + 0.2 Then MemoRTB.ZoomFactor -= 0.2 - UpdateMemoZoomFactor() + SaveMemoZoom() End If End Sub Private Sub MemoZoomBT2_Click(sender As Object, e As EventArgs) Handles MemoZoomBT2.Click MemoRTB.ZoomFactor += 0.2 - UpdateMemoZoomFactor() + SaveMemoZoom() End Sub Private Sub MemoAutoSave() From b8a87846ba6660487c452b77b580b5a8ac67e14e Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 10:30:26 +0900 Subject: [PATCH 19/24] feat: add HoverEnded event to CellControl and handle hover state in TableForm --- utable/Forms/TableForm.vb | 35 ++++++++++++++++++++++++++++++ utable/UserControls/CellControl.vb | 12 ++++++++++ 2 files changed, 47 insertions(+) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index 281af7b..1d8550c 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -1097,6 +1097,7 @@ Public Class TableForm 'cell.BackColor = color cell.goalColor = color AddHandler cell.FadeStarted, AddressOf CellFadeStarted + AddHandler cell.HoverEnded, AddressOf CellHoverEnded With cell .Settings = cellSettings @@ -1127,6 +1128,7 @@ Public Class TableForm cell.Height = part * parentPanel.Height cell.defHeight = part * parentPanel.Height cell.defLoc = cell.Location.Y + cell.StartMinutes = startt cell.dayNum = day 'MsgBox(part * Panel1.Height) @@ -1186,6 +1188,39 @@ Public Class TableForm resizeCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), cell) End If Next + + For Each panel As Panel In {MonPanel, TuePanel, WedPanel, ThuPanel, FriPanel, SatPanel, SunPanel} + RestoreCourseZOrder(panel) + Next + End Sub + + Private Sub CellHoverEnded(sender As Object, e As EventArgs) + Dim cell As CellControl = TryCast(sender, CellControl) + If cell Is Nothing OrElse cell.IsDisposed Then Exit Sub + + RestoreCourseZOrder(TryCast(cell.Parent, Panel)) + End Sub + + Private Sub RestoreCourseZOrder(panel As Panel) + If panel Is Nothing Then Exit Sub + + 'Controls 컬렉션의 인덱스 0이 가장 앞이다. 늦게 시작하는 셀부터 + '앞에 두어, 항상 확장 셀이 겹쳐도 이른 수업이 뒤에 깔리게 한다. + Dim orderedCells = panel.Controls.OfType(Of CellControl)(). + OrderByDescending(Function(cell) cell.StartMinutes). + ThenByDescending(Function(cell) cell.defLoc). + ToList() + + For index As Integer = 0 To orderedCells.Count - 1 + panel.Controls.SetChildIndex(orderedCells(index), index) + Next + + '다른 셀의 축소 애니메이션이 늦게 끝나더라도 현재 포인터 아래에서 + '확장 중인 셀의 임시 최상단 상태를 시간순 복원이 덮어쓰지 않게 한다. + For Each hoveredCell As CellControl In orderedCells.Where(Function(cell) cell.IsHovered) + hoveredCell.BringToFront() + Next + panel.Invalidate(True) End Sub Private Sub QueueCourseBoundsUpdate() diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index dea14da..4a2d9ae 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -5,6 +5,7 @@ Imports System.Diagnostics Public Class CellControl Public defHeight As Integer = 0 Public defLoc As Integer = 0 + Public StartMinutes As Integer = 0 Public checked As Boolean = False Public dayNum As Integer = 0 @@ -19,6 +20,7 @@ Public Class CellControl Public UsesSharedFadeClock As Boolean = False Public goalColor As Color = Nothing Public Event FadeStarted As EventHandler + Public Event HoverEnded As EventHandler Private ReadOnly Property HorizontalPadding As Integer Get @@ -46,6 +48,11 @@ Public Class CellControl Private blackText As Boolean = False Private hovered As Boolean = False + Public ReadOnly Property IsHovered As Boolean + Get + Return hovered + End Get + End Property Private checkHovered As Boolean = False Private checkPressed As Boolean = False Private titleHovered As Boolean = False @@ -264,7 +271,10 @@ Public Class CellControl If Parent IsNot Nothing Then Parent.Invalidate(Rectangle.Union(previousBounds, Bounds), True) End If + If Not hovered Then RaiseEvent HoverEnded(Me, EventArgs.Empty) End If + ElseIf Not hovered Then + RaiseEvent HoverEnded(Me, EventArgs.Empty) End If Invalidate() @@ -297,6 +307,7 @@ Public Class CellControl Private Sub StartHoverAnimation(targetBounds As Rectangle) If Bounds = targetBounds Then hoverAnimationTimer.Stop() + If Not hovered Then RaiseEvent HoverEnded(Me, EventArgs.Empty) Return End If @@ -323,6 +334,7 @@ Public Class CellControl If progress >= 1.0 Then hoverAnimationTimer.Stop() + If Not hovered Then RaiseEvent HoverEnded(Me, EventArgs.Empty) End If End Sub From 93c0478ed4bf0459839e0459d129f5d0a5464c69 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 10:43:39 +0900 Subject: [PATCH 20/24] refactor: improve XML serialization in SerializeTableFragment function --- utable/Modules/DataModule.vb | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/utable/Modules/DataModule.vb b/utable/Modules/DataModule.vb index e3f8811..cddc6b5 100644 --- a/utable/Modules/DataModule.vb +++ b/utable/Modules/DataModule.vb @@ -1,5 +1,6 @@ Imports System.IO Imports System.Security +Imports System.Text Imports System.Web Imports System.Xml Imports System.Xml.Linq @@ -46,7 +47,30 @@ Module DataModule End Function Private Function SerializeTableFragment(document As XDocument) As String - Return String.Concat(document.Root.Nodes().Select(Function(node) node.ToString(SaveOptions.DisableFormatting))).Trim() + Dim settings As New XmlWriterSettings With { + .ConformanceLevel = ConformanceLevel.Fragment, + .Indent = True, + .IndentChars = vbTab, + .NewLineChars = vbCrLf, + .NewLineHandling = NewLineHandling.None, + .OmitXmlDeclaration = True + } + Dim output As New StringBuilder() + + Using writer As XmlWriter = XmlWriter.Create(output, settings) + For Each element As XElement In document.Root.Elements() + '기존 편집 코드가 만든 공백 노드는 버리고 실제 요소 구조만 다시 들여쓴다. + '값만 있는 요소의 공백은 데이터일 수 있으므로 그대로 보존한다. + Dim formattedElement As New XElement(element) + For Each container As XElement In formattedElement.DescendantsAndSelf().Where(Function(item) item.HasElements) + container.Nodes().OfType(Of XText)().Where(Function(node) String.IsNullOrWhiteSpace(node.Value)).Remove() + Next + + formattedElement.WriteTo(writer) + Next + End Using + + Return output.ToString().TrimEnd(ControlChars.Cr, ControlChars.Lf) End Function Private Function GetElementInnerXml(element As XElement) As String From 89f5945e1fe0c671a0bda8b5dd3a306765867f87 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 11:01:10 +0900 Subject: [PATCH 21/24] refactor: unify timetable data handling with typed models --- utable/Forms/EveryTimeBrowserNew.vb | 22 +-- utable/Forms/OptionForm.vb | 13 +- utable/Forms/SetCourse.vb | 238 ++++++++-------------------- utable/Forms/TableForm.vb | 180 ++++++++------------- utable/Forms/ViewCourse.vb | 77 +++------ utable/Models/TableCourse.vb | 29 +++- utable/Models/TableSchedule.vb | 4 + utable/Modules/DataModule.vb | 152 ++++++++---------- utable/Modules/WinModule.vb | 2 +- utable/UserControls/CellControl.vb | 30 ++-- utable/uTable.vbproj | 1 + 11 files changed, 287 insertions(+), 461 deletions(-) create mode 100644 utable/Models/TableSchedule.vb diff --git a/utable/Forms/EveryTimeBrowserNew.vb b/utable/Forms/EveryTimeBrowserNew.vb index 9149fd0..df03a31 100644 --- a/utable/Forms/EveryTimeBrowserNew.vb +++ b/utable/Forms/EveryTimeBrowserNew.vb @@ -121,7 +121,7 @@ Public Class EveryTimeBrowserNew Private Async Sub TableChecker_Tick(sender As Object, e As EventArgs) Handles TableChecker.Tick Dim courses As List(Of EverytimeCourse) = Await GetCoursesFromPage() - Dim tabledata As String = "" + Dim schedule As New TableSchedule With {.Name = "에타에서 불러온 시간표"} If courses.Count > 0 Then @@ -135,15 +135,15 @@ Public Class EveryTimeBrowserNew color = ControlPaint.Dark(color, 0.2) End If - tabledata += "" + vbCrLf - tabledata += vbTab + "" + course.Day.ToString + "" + vbCrLf - tabledata += vbTab + "" + xmlEncode(course.Name) + "" + vbCrLf - tabledata += vbTab + "" + xmlEncode(course.Professor) + "" + vbCrLf - tabledata += vbTab + "" + xmlEncode(course.Memo) + "" + vbCrLf - tabledata += vbTab + "" + course.Start.ToString + "" + vbCrLf - tabledata += vbTab + "" + (course.Start + course.Duration).ToString + "" + vbCrLf - tabledata += vbTab + "" + ColorTranslator.ToHtml(color) + "" + vbCrLf - tabledata += "" + vbCrLf + schedule.Courses.Add(New TableCourse With { + .Day = course.Day, + .Name = course.Name, + .Professor = course.Professor, + .Memo = course.Memo, + .Start = course.Start, + .End = course.Start + course.Duration, + .Color = ColorTranslator.ToHtml(color) + }) Next Await Task.Delay(3000) @@ -151,7 +151,7 @@ Public Class EveryTimeBrowserNew If MsgBox("불러오기가 완료되었습니다. 바로 적용하시겠습니까?" + vbCr + "기존 시간표는 지워집니다!", vbQuestion + vbYesNo) = vbYes Then - writeTable("에타에서 불러온 시간표" + vbCrLf + tabledata) + SaveSchedule(schedule) TableForm.updateCell() Close() End If diff --git a/utable/Forms/OptionForm.vb b/utable/Forms/OptionForm.vb index 65ecf0f..aa5ffbb 100644 --- a/utable/Forms/OptionForm.vb +++ b/utable/Forms/OptionForm.vb @@ -927,13 +927,14 @@ Public Class OptionForm Private Sub Button2_Click(sender As Object, e As EventArgs) Handles SaveToFileBT.Click If TableSaveRbt.Checked Then - tableData = readTable() + tableData = ReadScheduleData() If Not (tableData.Contains("") Or tableData.Contains("")) Then MsgBox("시간표를 만들어 주세요.", vbInformation) Else Dim title As String = "이름 없는 시간표" - If tableData.Contains("") Then title = getTableData(tableData, "tablename") + Dim scheduleName As String = ParseSchedule(tableData).Name + If Not String.IsNullOrEmpty(scheduleName) Then title = scheduleName SaveFileDialog1.FileName = title SaveFileDialog1.Filter = "uTable 시간표 파일|*.utdata|모든 파일|*.*" @@ -976,7 +977,7 @@ Public Class OptionForm Private Sub Button3_Click(sender As Object, e As EventArgs) Handles CopyToClipboardBT.Click If TableSaveRbt.Checked Then - tableData = readTable() + tableData = ReadScheduleData() If tableData = "" Then MsgBox("시간표를 만들어 주세요.", vbInformation) @@ -1007,7 +1008,7 @@ Public Class OptionForm If clipboardTxt.Contains("") Or clipboardTxt.Contains("") Then If MsgBox("클립보드에서 시간표 서식을 찾았습니다. 불러오시겠습니까?", vbQuestion + vbYesNo) = vbYes Then Try - writeTable(clipboardTxt) + SaveSchedule(ParseSchedule(clipboardTxt)) TableForm.updateCell() Exit Sub Catch ex As Exception @@ -1053,7 +1054,7 @@ Public Class OptionForm If TableSaveRbt.Checked Then Dim data As String = ReadTableFile(OpenFileDialog1.FileName) If data.Contains("") Or data.Contains("") Then - writeTable(data) + SaveSchedule(ParseSchedule(data)) Else MsgBox("올바른 시간표 파일이 아닙니다.", vbCritical) End If @@ -1088,7 +1089,7 @@ Public Class OptionForm End If Try - tableData = readTable() + tableData = ReadScheduleData() If tableData.Contains("") Or tableData.Contains("") Then If MsgBox("현재 시간표를 설정한 위치로 복사하시겠습니까?", vbQuestion + vbYesNo) = vbYes Then diff --git a/utable/Forms/SetCourse.vb b/utable/Forms/SetCourse.vb index 3df067e..6022a60 100644 --- a/utable/Forms/SetCourse.vb +++ b/utable/Forms/SetCourse.vb @@ -1,13 +1,13 @@ Imports System.Runtime.InteropServices Public Class SetCourse - Dim prevData As New List(Of String) + Dim prevCourses As New List(Of TableCourse) Dim daysname As String() = {"월", "화", "수", "목", "금", "토", "일"} Dim listcount As Integer = 0 Dim colormode As String = Nothing Public modifyMode As Boolean = False - Public olddata As String = Nothing + Friend currentCourse As TableCourse = Nothing Public touched As Boolean = False Dim loaded As Boolean = False @@ -100,102 +100,32 @@ Public Class SetCourse Sub GetCourses() PrevSetCombo.Items.Clear() + prevCourses = LoadSchedule().Courses - Dim data As String = readTable() - If data.Contains("") Then - prevData = getTableDatas(data, "course") - - For Each s As String In prevData - Dim itemname As String = getTableData(s, "name") + " (" + daysname(Convert.ToInt16(getTableData(s, "day"))) + "요일)" - PrevSetCombo.Items.Add(itemname) - Next - End If + For Each course As TableCourse In prevCourses + PrevSetCombo.Items.Add(course.Name + " (" + daysname(course.Day) + "요일)") + Next End Sub - Sub SaveCourse(day As Integer, name As String, prof As String, memo As String, start As Integer, endt As Integer, color As Color) - Dim prevData As String = readTable() - Dim tablename As String = Nothing - - If Not prevData.Contains("") Then - tablename = "이름 없는 시간표" - Else - Dim prevName As String = getTableData(prevData, "tablename") - If prevName = "" Then - prevData = prevData.Replace("", "") - tablename = "이름 없는 시간표" - Else - prevData = prevData.Replace("" + prevName + "", "") - tablename = prevName - End If - End If - - Dim data As String = vbCrLf + "" + vbCrLf - data += vbTab + "" + day.ToString + "" + vbCrLf - data += vbTab + "" + xmlEncode(name) + "" + vbCrLf - data += vbTab + "" + xmlEncode(prof) + "" + vbCrLf - data += vbTab + "" + xmlEncode(memo) + "" + vbCrLf - data += vbTab + "" + start.ToString + "" + vbCrLf - data += vbTab + "" + endt.ToString + "" + vbCrLf - data += vbTab + "" + ColorTranslator.ToHtml(color) + "" + vbCrLf - data += "" - - data = "" + tablename + "" + prevData + data - - writeTable(data) + Private Sub FillInputs(course As TableCourse) + CourseNameTB.Text = course.Name + ProfTB.Text = course.Professor + DayCombo.SelectedIndex = course.Day + StartTimePicker.Value = New DateTime(2001, 1, 1, course.Start \ 60, course.Start Mod 60, 0) + EndTimePicker.Value = New DateTime(2001, 1, 1, course.End \ 60, course.End Mod 60, 0) + MemoTB.Text = course.Memo + ColorButton.BackColor = ColorTranslator.FromHtml(course.Color) End Sub - Sub modifyCourse(day As Integer, name As String, prof As String, memo As String, start As Integer, endt As Integer, color As Color) - Dim data As String = readTable() - - Dim newdata As String = vbCrLf - newdata += vbTab + "" + day.ToString + "" + vbCrLf - newdata += vbTab + "" + xmlEncode(name) + "" + vbCrLf - newdata += vbTab + "" + xmlEncode(prof) + "" + vbCrLf - newdata += vbTab + "" + xmlEncode(memo) + "" + vbCrLf - newdata += vbTab + "" + start.ToString + "" + vbCrLf - newdata += vbTab + "" + endt.ToString + "" + vbCrLf - newdata += vbTab + "" + ColorTranslator.ToHtml(color) + "" + vbCrLf - - If olddata.Contains("") Then - newdata += vbTab + getTableData_withkeys(olddata, "checked") + vbCrLf - End If - - writeTable(data.Replace(olddata, newdata)) - End Sub - - Sub modifyAllCourse(name As String, prof As String, memo As String, color As Color) - Dim data As String = readTable() - Dim olddatas As List(Of String) = getTableDatas(data, "course") - - Dim tablename As String = Nothing - - If Not data.Contains("") Then - tablename = "이름 없는 시간표" - Else - If getTableData(data, "tablename") = "" Then - tablename = "이름 없는 시간표" - Else - tablename = getTableData(data, "tablename") - End If - End If - - Dim newdata As String = "" - Dim oldname As String = getTableData(olddata, "name") - - For Each i In olddatas - Dim tmp = i - - If getTableData(i, "name") = oldname Then - tmp = tmp.Replace("" + oldname + "", "" + xmlEncode(name) + "") - tmp = tmp.Replace("" + getTableData(i, "prof") + "", "" + xmlEncode(prof) + "") - tmp = tmp.Replace("" + getTableData(i, "memo") + "", "" + xmlEncode(memo) + "") - tmp = tmp.Replace("" + getTableData(i, "color") + "", "" + ColorTranslator.ToHtml(color) + "") - End If - - newdata += "" + tmp + "" + vbCrLf - Next - - writeTable("" + tablename + "" + vbCrLf + newdata) + Private Sub UpdateCourse(course As TableCourse, day As Integer, name As String, professor As String, + memo As String, startMinutes As Integer, endMinutes As Integer, color As Color) + course.Day = day + course.Name = name + course.Professor = professor + course.Memo = memo + course.Start = startMinutes + course.End = endMinutes + course.Color = ColorTranslator.ToHtml(color) End Sub Private Sub SetCourse_Load(sender As Object, e As EventArgs) Handles MyBase.Load @@ -218,16 +148,9 @@ Public Class SetCourse ApplyBT.Text = "수정" DeleteBT.Visible = True - Dim data = olddata - Try - CourseNameTB.Text = xmlDecode(getTableData(data, "name")) - ProfTB.Text = xmlDecode(getTableData(data, "prof")) - DayCombo.SelectedIndex = Convert.ToInt16(getTableData(data, "day")) - StartTimePicker.Value = New DateTime(2001, 1, 1, Convert.ToInt16(getTableData(data, "start")) \ 60, Convert.ToInt16(getTableData(data, "start")) Mod 60, 0) - EndTimePicker.Value = New DateTime(2001, 1, 1, Convert.ToInt16(getTableData(data, "end")) \ 60, Convert.ToInt16(getTableData(data, "end")) Mod 60, 0) - MemoTB.Text = xmlDecode(getTableData(data, "memo")) - ColorButton.BackColor = ColorTranslator.FromHtml(getTableData(data, "color")) + If currentCourse Is Nothing Then Throw New InvalidOperationException("수정할 수업 정보가 없습니다.") + FillInputs(currentCourse) Catch ex As Exception MsgBox("수업을 불러오는 도중 문제가 발생하였습니다." + vbCr + "해당 수업의 값이 올바른지 확인하고 삭제 후 다시 추가해 주세요.", vbCritical) @@ -278,70 +201,52 @@ Public Class SetCourse Exit Sub End If - For Each s As String In prevData - - If getTableData(s, "day") = DayCombo.SelectedIndex Then - 'MsgBox("인덱스 같음") - Dim itemname As String = xmlDecode(getTableData(s, "name")) + " (" + daysname(Convert.ToInt16(getTableData(s, "day"))) + "요일)" - - If Not ((startt <= Convert.ToInt16(getTableData(s, "start")) And endt <= Convert.ToInt16(getTableData(s, "start"))) _ - Or (startt >= Convert.ToInt16(getTableData(s, "end")) And endt >= Convert.ToInt16(getTableData(s, "end")))) Then - - '수정 모드 아닐때 - If Not modifyMode Then - MsgBox("다른 수업 (" + itemname + ")과 현재 설정한 수업의 시간이 겹칩니다.", vbExclamation) - Exit Sub - - '수정일때 겹치는경우 - Else - '근데 겹치는게 원래 고치려했던놈이 아닐때 - 'If Not getTableData(s, "name") = getTableData(olddata, "name") Then - If Not (getTableData(s, "day") + "-" + getTableData(s, "start") + "-" + getTableData(s, "name")) _ - = (getTableData(olddata, "day") + "-" + getTableData(olddata, "start") + "-" + getTableData(olddata, "name")) Then - MsgBox("다른 수업 (" + itemname + ")과 현재 설정한 수업의 시간이 겹칩니다.", vbExclamation) - Exit Sub - End If - End If - End If + For Each course As TableCourse In prevCourses + If course.Day <> DayCombo.SelectedIndex Then Continue For + If modifyMode AndAlso currentCourse IsNot Nothing AndAlso + course.SourceIndex = currentCourse.SourceIndex AndAlso course.HasSameData(currentCourse) Then Continue For + + If startt < course.End AndAlso endt > course.Start Then + Dim itemname As String = course.Name + " (" + daysname(course.Day) + "요일)" + MsgBox("다른 수업 (" + itemname + ")과 현재 설정한 수업의 시간이 겹칩니다.", vbExclamation) + Exit Sub End If Next TopMost = False + Dim schedule As TableSchedule = LoadSchedule() + If String.IsNullOrEmpty(schedule.Name) Then schedule.Name = "이름 없는 시간표" - If Not modifyMode Then - SaveCourse(DayCombo.SelectedIndex, CourseNameTB.Text, ProfTB.Text, MemoTB.Text, startt, endt, ColorButton.BackColor) - TableForm.updateCell() - 'MsgBox("추가되었습니다.", vbInformation) - Else - '수정 모드일때 - Dim count As Integer = 0 - For Each s As String In prevData - '이전설정 이름이 여러개 이미 있을때 - If getTableData(s, "name") = getTableData(olddata, "name") Then count += 1 - Next - - If count > 1 Then - If MsgBox("같은 이름의 수업이 둘 이상 있습니다." + vbCr + "해당 수업 또한 모두 바꾸시겠습니까? (시간, 요일 제외)", vbQuestion + vbYesNo) = vbYes Then - modifyCourse(DayCombo.SelectedIndex, CourseNameTB.Text, ProfTB.Text, MemoTB.Text, startt, endt, ColorButton.BackColor) - modifyAllCourse(CourseNameTB.Text, ProfTB.Text, MemoTB.Text, ColorButton.BackColor) - TableForm.updateCell() - 'MsgBox("수정되었습니다.", vbInformation) - Close() - Else - modifyCourse(DayCombo.SelectedIndex, CourseNameTB.Text, ProfTB.Text, MemoTB.Text, startt, endt, ColorButton.BackColor) - TableForm.updateCell() - 'MsgBox("수정되었습니다.", vbInformation) - Close() - End If - Else - modifyCourse(DayCombo.SelectedIndex, CourseNameTB.Text, ProfTB.Text, MemoTB.Text, startt, endt, ColorButton.BackColor) - TableForm.updateCell() - 'MsgBox("수정되었습니다.", vbInformation) - Close() + If modifyMode Then + Dim target As TableCourse = FindCourse(schedule, currentCourse) + If target Is Nothing Then + MsgBox("수정할 수업을 현재 시간표에서 찾지 못했습니다. 시간표를 새로고침한 후 다시 시도해 주세요.", vbExclamation) + Exit Sub End If + Dim oldName As String = currentCourse.Name + Dim updateAll As Boolean = schedule.Courses.Where(Function(course) course.Name = oldName).Count() > 1 AndAlso + MsgBox("같은 이름의 수업이 둘 이상 있습니다." + vbCr + "해당 수업 또한 모두 바꾸시겠습니까? (시간, 요일 제외)", vbQuestion + vbYesNo) = vbYes + + UpdateCourse(target, DayCombo.SelectedIndex, CourseNameTB.Text, ProfTB.Text, MemoTB.Text, startt, endt, ColorButton.BackColor) + If updateAll Then + For Each course As TableCourse In schedule.Courses.Where(Function(item) item.Name = oldName AndAlso item IsNot target) + course.Name = CourseNameTB.Text + course.Professor = ProfTB.Text + course.Memo = MemoTB.Text + course.Color = ColorTranslator.ToHtml(ColorButton.BackColor) + Next + End If + Else + Dim course As New TableCourse() + UpdateCourse(course, DayCombo.SelectedIndex, CourseNameTB.Text, ProfTB.Text, MemoTB.Text, startt, endt, ColorButton.BackColor) + schedule.Courses.Add(course) End If + SaveSchedule(schedule) + TableForm.updateCell() + If modifyMode Then Close() + TopMost = True GetCourses() touched = False @@ -355,15 +260,7 @@ Public Class SetCourse End If loaded = False - Dim data = prevData(PrevSetCombo.SelectedIndex) - - CourseNameTB.Text = xmlDecode(getTableData(data, "name")) - ProfTB.Text = xmlDecode(getTableData(data, "prof")) - DayCombo.SelectedIndex = Convert.ToInt16(getTableData(data, "day")) - StartTimePicker.Value = New DateTime(2001, 1, 1, Convert.ToInt16(getTableData(data, "start")) \ 60, Convert.ToInt16(getTableData(data, "start")) Mod 60, 0) - EndTimePicker.Value = New DateTime(2001, 1, 1, Convert.ToInt16(getTableData(data, "end")) \ 60, Convert.ToInt16(getTableData(data, "end")) Mod 60, 0) - MemoTB.Text = xmlDecode(getTableData(data, "memo")) - ColorButton.BackColor = ColorTranslator.FromHtml(getTableData(data, "color")) + FillInputs(prevCourses(PrevSetCombo.SelectedIndex)) loaded = True touched = False @@ -377,14 +274,15 @@ Public Class SetCourse Private Sub DeleteBT_Click(sender As Object, e As EventArgs) Handles DeleteBT.Click If MsgBox("정말로 지우시겠습니까?", vbQuestion + vbYesNo) = vbYes Then - Dim updatedData As String = Nothing - - If Not TryRemoveTableCourse(readTable(), olddata, updatedData) Then + Dim schedule As TableSchedule = LoadSchedule() + Dim target As TableCourse = FindCourse(schedule, currentCourse) + If target Is Nothing Then MsgBox("삭제할 수업을 현재 시간표에서 찾지 못했습니다. 시간표를 새로고침한 후 다시 시도해 주세요.", vbExclamation) Exit Sub End If - writeTable(updatedData) + schedule.Courses.Remove(target) + SaveSchedule(schedule) TableForm.updateCell() Close() End If diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index 1d8550c..cbe08d6 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -14,7 +14,6 @@ Public Class TableForm Dim starttime As Integer = 0 Dim endtime As Integer = 0 Dim updated As Boolean = False - Dim courseData As New List(Of String) Dim courseRecords As New List(Of TableCourse) Private ReadOnly cellsByCourse As New Dictionary(Of TableCourse, CellControl) Private ReadOnly fadingCells As New HashSet(Of CellControl) @@ -658,6 +657,8 @@ Public Class TableForm #Region "버튼/도구상자/키 이벤트" Private Sub AddCourseBT_Click(sender As Object, e As EventArgs) Handles AddCourseBT.Click SetCourse.Close() + SetCourse.modifyMode = False + SetCourse.currentCourse = Nothing SetCourse.SetDesktopLocation(Location.X + AddCourseBT.Location.X + AddCourseBT.Width - SetCourse.Width + MainPanel.Location.X, Location.Y + DayTable.Location.Y + MainPanel.Location.Y) SetCourse.Show() @@ -881,41 +882,41 @@ Public Class TableForm Private Sub AllColorSetItem_Click(sender As Object, e As EventArgs) Handles AllColorSetItem.Click If ColorDialog1.ShowDialog() = DialogResult.OK Then - Dim tmp As String = "" + xmlEncode(TableTitleLabel.Text) + "" + vbCrLf - For Each s As String In courseData - s = s.Replace(getTableData(s, "color"), ColorTranslator.ToHtml(ColorDialog1.Color)) - tmp += "" + s + "" + vbCrLf + Dim schedule As TableSchedule = LoadSchedule() + For Each course As TableCourse In schedule.Courses + course.Color = ColorTranslator.ToHtml(ColorDialog1.Color) Next - writeTable(tmp) + SaveSchedule(schedule) updateCell() End If End Sub Private Sub AllDarkerItem_Click(sender As Object, e As EventArgs) Handles AllDarkerItem.Click - Dim tmp As String = "" + xmlEncode(TableTitleLabel.Text) + "" + vbCrLf - For Each s As String In courseData - Dim oldColor As Color = ColorTranslator.FromHtml(getTableData(s, "color")) - s = s.Replace(getTableData(s, "color"), ColorTranslator.ToHtml(ControlPaint.Dark(oldColor, 0.01))) - tmp += "" + s + "" + vbCrLf + Dim schedule As TableSchedule = LoadSchedule() + For Each course As TableCourse In schedule.Courses + Dim oldColor As Color = ColorTranslator.FromHtml(course.Color) + course.Color = ColorTranslator.ToHtml(ControlPaint.Dark(oldColor, 0.01)) Next - writeTable(tmp) + SaveSchedule(schedule) updateCell() End Sub Private Sub AllBrighterItem_Click(sender As Object, e As EventArgs) Handles AllBrighterItem.Click - Dim tmp As String = "" + xmlEncode(TableTitleLabel.Text) + "" + vbCrLf - For Each s As String In courseData - Dim oldColor As Color = ColorTranslator.FromHtml(getTableData(s, "color")) - s = s.Replace(getTableData(s, "color"), ColorTranslator.ToHtml(ControlPaint.Light(oldColor, 0.3))) - tmp += "" + s + "" + vbCrLf + Dim schedule As TableSchedule = LoadSchedule() + For Each course As TableCourse In schedule.Courses + Dim oldColor As Color = ColorTranslator.FromHtml(course.Color) + course.Color = ColorTranslator.ToHtml(ControlPaint.Light(oldColor, 0.3)) Next - writeTable(tmp) + SaveSchedule(schedule) updateCell() End Sub Private Sub ClearCheckBoxItem_Click(sender As Object, e As EventArgs) Handles ClearCheckBoxItem.Click - Dim data As String = readTable() - writeTable(data.Replace("True", "False")) + Dim schedule As TableSchedule = LoadSchedule() + For Each course As TableCourse In schedule.Courses.Where(Function(item) item.CheckedSpecified) + course.IsChecked = False + Next + SaveSchedule(schedule) updateCell() End Sub @@ -956,28 +957,22 @@ Public Class TableForm cellsByCourse.Clear() updated = False - Dim data = readTable() + Dim schedule As TableSchedule = LoadSchedule() Dim min As Integer = 9999999 Dim max As Integer = 0 - If data.Contains("") Then - TableTitleLabel.Text = xmlDecode(getTableData(data, "tablename")) - Else - TableTitleLabel.Text = "이름 없는 시간표" - End If + TableTitleLabel.Text = If(String.IsNullOrEmpty(schedule.Name), "이름 없는 시간표", schedule.Name) Text = TableTitleLabel.Text - courseData.Clear() courseRecords.Clear() - If data.Contains("") Then - courseRecords = getTableCourses(data) - courseData = courseRecords.Select(Function(course) course.RawData).ToList() + If schedule.Courses.Count > 0 Then + courseRecords = schedule.Courses '최대, 최소계산 For Each course As TableCourse In courseRecords - If Convert.ToInt16(course.Start) < min Then min = Convert.ToInt16(course.Start) - If Convert.ToInt16(course.End) > max Then max = Convert.ToInt16(course.End) + If course.Start < min Then min = course.Start + If course.End > max Then max = course.End Next starttime = min @@ -991,14 +986,12 @@ Public Class TableForm '셀계산 '여기서 xmldecode 하니까 꼭 눈여겨두자 For Each course As TableCourse In courseRecords - Dim cell = addCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), course.Identity(), - xmlDecode(course.Name), xmlDecode(course.Professor), xmlDecode(course.Memo), - ColorTranslator.FromHtml(course.Color), Convert.ToInt16(course.Day), course.Checked, course.RawData) + Dim cell = addCell(course) cellsByCourse.Add(course, cell) - If Convert.ToInt16(course.Day) = 5 Then '토요일 추가시 + If course.Day = 5 Then '토요일 추가시 showSaturday = True - ElseIf Convert.ToInt16(course.Day) = 6 Then '일요일 추가시 + ElseIf course.Day = 6 Then '일요일 추가시 showSunday = True End If Next @@ -1087,7 +1080,14 @@ Public Class TableForm TimeTable.ResumeLayout(True) End Sub - Private Function addCell(startt As Integer, endt As Integer, name As String, title As String, prof As String, memo As String, color As Color, day As Integer, checked As String, rawData As String) As CellControl + Private Function addCell(course As TableCourse) As CellControl + Dim startt As Integer = course.Start + Dim endt As Integer = course.End + Dim title As String = course.Name + Dim prof As String = course.Professor + Dim memo As String = course.Memo + Dim color As Color = ColorTranslator.FromHtml(course.Color) + Dim day As Integer = course.Day Dim timelength As Integer = endtime - starttime Dim part As Double = (endt - startt) / timelength @@ -1155,10 +1155,10 @@ Public Class TableForm cell.CourseTitle = title cell.ProfessorText = prof cell.MemoText = memo - cell.Name = name - cell.Tag = rawData + cell.Name = course.Identity() + cell.Tag = course - cell.checked = (checked = "True") + cell.checked = course.IsChecked parentPanel.Controls.Add(cell) Return cell End Function @@ -1185,7 +1185,7 @@ Public Class TableForm For Each course As TableCourse In courseRecords Dim cell As CellControl = Nothing If cellsByCourse.TryGetValue(course, cell) Then - resizeCell(Convert.ToInt16(course.Start), Convert.ToInt16(course.End), cell) + resizeCell(course.Start, course.End, cell) End If Next @@ -1275,14 +1275,9 @@ Public Class TableForm Else Try - Dim data As String = readTable() - If data.Contains("") Then - Dim oldtitle As String = getTableData(data, "tablename") - data = data.Replace("" + oldtitle + "", "" + xmlEncode(newtitle) + "") - writeTable(data) - Else - writeTable("" + xmlEncode(newtitle) + "" + vbCrLf + data) - End If + Dim schedule As TableSchedule = LoadSchedule() + schedule.Name = newtitle + SaveSchedule(schedule) Catch ex As Exception MsgBox("이름 변경 도중 문제가 발생했습니다." + vbCr + ex.Message, vbCritical) Exit Sub @@ -1439,34 +1434,11 @@ Public Class TableForm Sub CheckNotify() Try - Dim dayData As New List(Of String) - - If courseData.Count > 0 Then - For Each s As String In courseData - Dim tmp As String = getTableData(s, "day") - Dim day As String = "" + Dim dayData As New List(Of TableCourse) - Select Case Today.DayOfWeek - Case DayOfWeek.Monday - day = "0" - Case DayOfWeek.Tuesday - day = "1" - Case DayOfWeek.Wednesday - day = "2" - Case DayOfWeek.Thursday - day = "3" - Case DayOfWeek.Friday - day = "4" - Case DayOfWeek.Saturday - day = "5" - Case DayOfWeek.Sunday - day = "6" - End Select - - If tmp = day Then - dayData.Add(s) - End If - Next + If courseRecords.Count > 0 Then + Dim day As Integer = (CInt(Today.DayOfWeek) + 6) Mod 7 + dayData = courseRecords.Where(Function(course) course.Day = day).ToList() Dim currentTime As Integer = Now.Hour * 60 + Now.Minute Dim notifyTime As List(Of String) = GetINI("SETTING", "NotifyMin", "", ININamePath).Split(",").ToList @@ -1474,26 +1446,26 @@ Public Class TableForm Dim notificationName As String = "" If dayData.Count > 0 Then - For Each s In dayData - Dim targetTime As Integer = Convert.ToInt16(getTableData(s, "start")) + For Each course As TableCourse In dayData + Dim targetTime As Integer = course.Start If targetTime < currentTime Then Continue For '수업 시간 됐을때 If targetTime = currentTime Then - notificationName = getTableData(s, "day") + "-" + getTableData(s, "start") + "-" + getTableData(s, "name") + "-0" + notificationName = course.Identity() + "-0" '수업 시작이 5분 이하 남았고 5분 옵션 체크됐을때 ElseIf notifyTime.Contains("5") And (targetTime - currentTime) <= 5 Then - notificationName = getTableData(s, "day") + "-" + getTableData(s, "start") + "-" + getTableData(s, "name") + "-5" + notificationName = course.Identity() + "-5" '수업 시작이 15분 이하 남았고 15분 옵션 체크됐을때 ElseIf notifyTime.Contains("15") And (targetTime - currentTime) <= 15 Then - notificationName = getTableData(s, "day") + "-" + getTableData(s, "start") + "-" + getTableData(s, "name") + "-15" + notificationName = course.Identity() + "-15" '수업 시작이 30분 이하 남았고 30분 옵션 체크됐을때 ElseIf notifyTime.Contains("30") And (targetTime - currentTime) <= 30 Then - notificationName = getTableData(s, "day") + "-" + getTableData(s, "start") + "-" + getTableData(s, "name") + "-30" + notificationName = course.Identity() + "-30" Else Continue For @@ -1505,7 +1477,7 @@ Public Class TableForm ' 1. 이미 보낸 똑같은 알람 ' 2. 무시 조건에 해당하는 수업 (ex. memo에 (알림 무시) 포함) - Dim memo As String = getTableData(s, "memo") + Dim memo As String = course.Memo If prevNotificationName = notificationName Then Continue For @@ -1541,8 +1513,7 @@ Public Class TableForm End Try End If - NotifyIcon1.ShowBalloonTip(9999, xmlDecode(getTableData(s, "name")) _ - + " (" + xmlDecode(getTableData(s, "prof")) + ")", message, ToolTipIcon.None) + NotifyIcon1.ShowBalloonTip(9999, course.Name + " (" + course.Professor + ")", message, ToolTipIcon.None) Next End If @@ -1555,42 +1526,19 @@ Public Class TableForm Sub TodayCourseNotify() Try - Dim dayData As New List(Of String) + Dim dayData As New List(Of TableCourse) - If courseData.Count > 0 Then - For Each s As String In courseData - Dim tmp As String = getTableData(s, "day") - Dim day As String = "" - - Select Case Today.DayOfWeek - Case DayOfWeek.Monday - day = "0" - Case DayOfWeek.Tuesday - day = "1" - Case DayOfWeek.Wednesday - day = "2" - Case DayOfWeek.Thursday - day = "3" - Case DayOfWeek.Friday - day = "4" - Case DayOfWeek.Saturday - day = "5" - Case DayOfWeek.Sunday - day = "6" - End Select - - If tmp = day Then - dayData.Add(s) - End If - Next + If courseRecords.Count > 0 Then + Dim day As Integer = (CInt(Today.DayOfWeek) + 6) Mod 7 + dayData = courseRecords.Where(Function(course) course.Day = day).ToList() If dayData.Count > 0 Then Dim courses As New List(Of String) Dim time As New List(Of Integer) - For Each s In dayData - courses.Add(xmlDecode(getTableData(s, "name")) + " (" + xmlDecode(getTableData(s, "prof")) + ")") - time.Add(Convert.ToInt16(getTableData(s, "start"))) + For Each course As TableCourse In dayData + courses.Add(course.Name + " (" + course.Professor + ")") + time.Add(course.Start) Next '표시 순서를 시작 시간 오름차순으로 정렬 diff --git a/utable/Forms/ViewCourse.vb b/utable/Forms/ViewCourse.vb index e536dca..9f3690a 100644 --- a/utable/Forms/ViewCourse.vb +++ b/utable/Forms/ViewCourse.vb @@ -17,7 +17,7 @@ Public Class ViewCourse '창 크기가 바뀌면 원본 제목을 기준으로 다시 맞춘다. Private courseTitle As String = String.Empty - Public olddata As String = Nothing + Friend currentCourse As TableCourse = Nothing Public blacktext As Boolean = False Dim touched As Boolean = False @@ -263,14 +263,15 @@ Public Class ViewCourse Dim colorMul As Single = 0.9 Try - Dim course As TableCourse = getTableCourse(olddata) - courseTitle = NormalizeCourseTitle(xmlDecode(course.Name)) + Dim course As TableCourse = currentCourse + If course Is Nothing Then Throw New InvalidOperationException("표시할 수업 정보가 없습니다.") + courseTitle = NormalizeCourseTitle(course.Name) Text = courseTitle UpperTitleLabel.Text = courseTitle - SubTitleLabel.Text = xmlDecode(course.Professor) + ", " + SubTitleLabel.Text = course.Professor + ", " - Dim startt As Integer = Convert.ToInt32(course.Start) - Dim endt As Integer = Convert.ToInt32(course.End) + Dim startt As Integer = course.Start + Dim endt As Integer = course.End SubTitleLabel.Text += (startt \ 60).ToString + ":" If startt Mod 60 = 0 Then @@ -288,9 +289,9 @@ Public Class ViewCourse SubTitleLabel.Text += (endt Mod 60).ToString("D2") End If - SubTitleLabel.Text += ", " + daysname(Convert.ToInt16(course.Day)) + "요일" + SubTitleLabel.Text += ", " + daysname(course.Day) + "요일" - MemoTB.Text = xmlDecode(course.Memo) + MemoTB.Text = course.Memo TitlePanel.BackColor = ColorTranslator.FromHtml(course.Color) Dim c As Color = Color.FromArgb(TitlePanel.BackColor.R * colorMul, @@ -409,7 +410,7 @@ Public Class ViewCourse End If SetCourse.modifyMode = True - SetCourse.olddata = olddata + SetCourse.currentCourse = currentCourse SetCourse.SetDesktopLocation(appearPoint.X, appearPoint.Y) SetCourse.touched = touched @@ -437,26 +438,23 @@ Public Class ViewCourse Sub Apply() Try - Dim data As String = readTable() - Dim count As Integer = 0 - - For Each s As String In getTableDatas(data, "course") - '이전설정 이름이 여러개 이미 있을때 - If getTableData(s, "name") = getTableData(olddata, "name") Then count += 1 - Next - - If count > 1 Then - If MsgBox("같은 이름의 수업이 둘 이상 있습니다." + vbCr + "해당 수업의 메모 또한 모두 바꾸시겠습니까?", vbQuestion + vbYesNo) = vbYes Then - modifyAllCourse(readTable(), MemoTB.Text) - Else - Dim newdata As String = olddata.Replace(getTableData_withkeys(olddata, "memo"), "" + xmlEncode(MemoTB.Text) + "") - writeTable(readTable.Replace(olddata, newdata)) - End If + Dim schedule As TableSchedule = LoadSchedule() + Dim target As TableCourse = FindCourse(schedule, currentCourse) + If target Is Nothing Then Throw New InvalidOperationException("수정할 수업을 현재 시간표에서 찾지 못했습니다.") + + Dim updateAll As Boolean = schedule.Courses.Where(Function(course) course.Name = currentCourse.Name).Count() > 1 AndAlso + MsgBox("같은 이름의 수업이 둘 이상 있습니다." + vbCr + "해당 수업의 메모 또한 모두 바꾸시겠습니까?", vbQuestion + vbYesNo) = vbYes + + If updateAll Then + For Each course As TableCourse In schedule.Courses.Where(Function(item) item.Name = currentCourse.Name) + course.Memo = MemoTB.Text + Next Else - Dim newdata As String = olddata.Replace(getTableData_withkeys(olddata, "memo"), "" + xmlEncode(MemoTB.Text) + "") - writeTable(readTable.Replace(olddata, newdata)) + target.Memo = MemoTB.Text End If + SaveSchedule(schedule) + TableForm.updateCell() Catch ex As Exception @@ -466,33 +464,6 @@ Public Class ViewCourse Close() End Sub - Sub modifyAllCourse(data As String, memo As String) - 'Dim data As String = readTable() - Dim olddatas As List(Of String) = getTableDatas(data, "course") - Dim tablename As String = Nothing - - If Not data.Contains("") Then - tablename = "이름 없는 시간표" - Else - If getTableData(data, "tablename") = "" Then - tablename = "이름 없는 시간표" - Else - tablename = getTableData(data, "tablename") - End If - End If - - Dim newdata As String = "" - Dim oldname As String = getTableData(olddata, "name") - - For Each i In olddatas - Dim tmp As String = i - If getTableData(i, "name") = oldname Then tmp = tmp.Replace("" + getTableData(i, "memo") + "", "" + xmlEncode(memo) + "") - newdata += "" + tmp + "" + vbCrLf - Next - - writeTable("" + tablename + "" + vbCrLf + newdata) - End Sub - Private Sub MemoTB_LinkClicked(sender As Object, e As LinkClickedEventArgs) Handles MemoTB.LinkClicked Process.Start(e.LinkText) End Sub diff --git a/utable/Models/TableCourse.vb b/utable/Models/TableCourse.vb index 6dd7baa..fec7cec 100644 --- a/utable/Models/TableCourse.vb +++ b/utable/Models/TableCourse.vb @@ -1,16 +1,31 @@ -'시간표 과목의 메모리 표현 - 값은 기존 함수와 호환되도록 XML-encoded 상태로 보관 Friend Class TableCourse - Public Property RawData As String - Public Property Day As String + '파일 안에서의 순서. UI가 가리키던 과목을 다시 로드한 시간표에서 찾을 때 사용한다. + '직렬화되는 값은 아니다. + Public Property SourceIndex As Integer = -1 + Public Property Day As Integer Public Property Name As String Public Property Professor As String Public Property Memo As String - Public Property Start As String - Public Property [End] As String + Public Property Start As Integer + Public Property [End] As Integer Public Property Color As String - Public Property Checked As String + Public Property IsChecked As Boolean + Public Property CheckedSpecified As Boolean Public Function Identity() As String - Return Day + "-" + Start + "-" + Name + Return Day.ToString() + "-" + Start.ToString() + "-" + Name + End Function + + Public Function HasSameData(other As TableCourse) As Boolean + Return other IsNot Nothing AndAlso + Day = other.Day AndAlso + Name = other.Name AndAlso + Professor = other.Professor AndAlso + Memo = other.Memo AndAlso + Start = other.Start AndAlso + [End] = other.End AndAlso + Color = other.Color AndAlso + IsChecked = other.IsChecked AndAlso + CheckedSpecified = other.CheckedSpecified End Function End Class diff --git a/utable/Models/TableSchedule.vb b/utable/Models/TableSchedule.vb new file mode 100644 index 0000000..ca54253 --- /dev/null +++ b/utable/Models/TableSchedule.vb @@ -0,0 +1,4 @@ +Friend Class TableSchedule + Public Property Name As String + Public Property Courses As New List(Of TableCourse) +End Class diff --git a/utable/Modules/DataModule.vb b/utable/Modules/DataModule.vb index cddc6b5..284ca9e 100644 --- a/utable/Modules/DataModule.vb +++ b/utable/Modules/DataModule.vb @@ -1,7 +1,5 @@ Imports System.IO -Imports System.Security Imports System.Text -Imports System.Web Imports System.Xml Imports System.Xml.Linq @@ -59,7 +57,7 @@ Module DataModule Using writer As XmlWriter = XmlWriter.Create(output, settings) For Each element As XElement In document.Root.Elements() - '기존 편집 코드가 만든 공백 노드는 버리고 실제 요소 구조만 다시 들여쓴다. + '입력 파일에 남아 있는 서식용 공백은 버리고 실제 요소 구조만 다시 들여쓴다. '값만 있는 요소의 공백은 데이터일 수 있으므로 그대로 보존한다. Dim formattedElement As New XElement(element) For Each container As XElement In formattedElement.DescendantsAndSelf().Where(Function(item) item.HasElements) @@ -73,91 +71,93 @@ Module DataModule Return output.ToString().TrimEnd(ControlChars.Cr, ControlChars.Lf) End Function - Private Function GetElementInnerXml(element As XElement) As String - Return String.Concat(element.Nodes().Select(Function(node) node.ToString(SaveOptions.DisableFormatting))) - End Function - - Private Function GetChildInnerXml(element As XElement, name As String) As String + Private Function GetRequiredChildValue(element As XElement, name As String) As String Dim child As XElement = element.Element(name) - If child Is Nothing Then Return Nothing - Return GetElementInnerXml(child) - End Function - - 'course 바로 아래의 서식용 공백은 비교에서 제외하되, 각 필드의 내용은 보존한다. - Private Function GetCourseComparisonData(element As XElement) As String - Return String.Concat(element.Elements().Select(Function(child) child.ToString(SaveOptions.DisableFormatting))) + If child Is Nothing Then Throw New InvalidDataException("과목에 <" + name + "> 값이 없습니다.") + Return child.Value End Function Private Function ToTableCourse(element As XElement) As TableCourse + Dim checkedElement As XElement = element.Element("checked") Return New TableCourse With { - .RawData = GetElementInnerXml(element).Trim(), - .Day = GetChildInnerXml(element, "day"), - .Name = GetChildInnerXml(element, "name"), - .Professor = GetChildInnerXml(element, "prof"), - .Memo = GetChildInnerXml(element, "memo"), - .Start = GetChildInnerXml(element, "start"), - .End = GetChildInnerXml(element, "end"), - .Color = GetChildInnerXml(element, "color"), - .Checked = GetChildInnerXml(element, "checked") + .Day = Integer.Parse(GetRequiredChildValue(element, "day")), + .Name = GetRequiredChildValue(element, "name"), + .Professor = GetRequiredChildValue(element, "prof"), + .Memo = GetRequiredChildValue(element, "memo"), + .Start = Integer.Parse(GetRequiredChildValue(element, "start")), + .End = Integer.Parse(GetRequiredChildValue(element, "end")), + .Color = GetRequiredChildValue(element, "color"), + .IsChecked = checkedElement IsNot Nothing AndAlso String.Equals(checkedElement.Value, "True", StringComparison.OrdinalIgnoreCase), + .CheckedSpecified = checkedElement IsNot Nothing } End Function - '전체 시간표 XML은 한 번만 파싱해 과목 목록으로 변환한다. - Public Function getTableCourses(datastr As String) As List(Of TableCourse) - Dim document As XDocument = ParseTableFragment(datastr) - Return document.Root.Elements("course").Select(Function(element) ToTableCourse(element)).ToList() - End Function - - '표시 중인 과목의 원본 fragment와 일치하는 course 요소를 XML 구조에서 제거한다. - '문자열 치환과 달리 줄바꿈이나 들여쓰기 형식에 영향을 받지 않는다. - Public Function TryRemoveTableCourse(datastr As String, rawCourseData As String, ByRef result As String) As Boolean - Dim document As XDocument = ParseTableFragment(datastr) - Dim rawCourse As XElement = ParseTableFragment("" + rawCourseData + "").Root.Element("course") - Dim targetData As String = GetCourseComparisonData(rawCourse) - Dim target As XElement = document.Root.Elements("course").FirstOrDefault( - Function(element) GetCourseComparisonData(element) = targetData) - - If target Is Nothing Then - result = datastr - Return False + Private Function ToCourseElement(course As TableCourse) As XElement + Dim element As New XElement("course", + New XElement("day", course.Day), + New XElement("name", course.Name), + New XElement("prof", course.Professor), + New XElement("memo", course.Memo), + New XElement("start", course.Start), + New XElement("end", course.End), + New XElement("color", course.Color)) + + If course.CheckedSpecified Then + element.Add(New XElement("checked", course.IsChecked.ToString())) End If - target.Remove() - result = SerializeTableFragment(document) - Return True + Return element End Function - 'olddata처럼 course 내부 fragment만 가진 기존 화면을 위한 단일 과목 파서 - Public Function getTableCourse(datastr As String) As TableCourse - Dim document As XDocument = ParseTableFragment("" + datastr + "") - Return ToTableCourse(document.Root.Element("course")) + Public Function ParseSchedule(data As String) As TableSchedule + Dim document As XDocument = ParseTableFragment(data) + Dim schedule As New TableSchedule() + Dim nameElement As XElement = document.Root.Element("tablename") + If nameElement IsNot Nothing Then schedule.Name = nameElement.Value + + Dim index As Integer = 0 + For Each element As XElement In document.Root.Elements("course") + Dim course As TableCourse = ToTableCourse(element) + course.SourceIndex = index + schedule.Courses.Add(course) + index += 1 + Next + + Return schedule End Function - '시간표 XML fragment에서 최상위 태그의 내용을 추출한다. - Public Function getTableData(datastr As String, name As String) As String - Dim element As XElement = ParseTableFragment(datastr).Root.Element(name) + Public Function SerializeSchedule(schedule As TableSchedule) As String + If schedule Is Nothing Then Throw New ArgumentNullException(NameOf(schedule)) - If element Is Nothing Then Return Nothing + Dim root As New XElement(TableFragmentRootName) + If schedule.Name IsNot Nothing Then root.Add(New XElement("tablename", schedule.Name)) + For Each course As TableCourse In schedule.Courses + root.Add(ToCourseElement(course)) + Next - Return GetElementInnerXml(element) + Return SerializeTableFragment(New XDocument(root)) End Function - '시간표 XML fragment에서 최상위 태그 전체를 추출한다. - Public Function getTableData_withkeys(datastr As String, name As String) As String - Dim element As XElement = ParseTableFragment(datastr).Root.Element(name) - - If element Is Nothing Then Return Nothing - - Return element.ToString(SaveOptions.DisableFormatting) + Public Function LoadSchedule() As TableSchedule + Dim filePath As String = TableSaveLocation(False) + If Not File.Exists(filePath) Then Return New TableSchedule() + Return ParseSchedule(ReadTableFile(filePath)) End Function - '시간표 XML fragment에서 같은 최상위 태그의 내용을 모두 추출한다. - Public Function getTableDatas(datastr As String, name As String) As List(Of String) - Dim elements = ParseTableFragment(datastr).Root.Elements(name).ToList() + Public Sub SaveSchedule(schedule As TableSchedule) + Dim filePath As String = TableSaveLocation(False) + File.WriteAllText(filePath, SerializeSchedule(schedule), Utf8WithoutBom) + End Sub - If elements.Count = 0 Then Return Nothing + Public Function FindCourse(schedule As TableSchedule, reference As TableCourse) As TableCourse + If schedule Is Nothing OrElse reference Is Nothing Then Return Nothing - Return elements.Select(Function(element) GetElementInnerXml(element).Trim()).ToList() + If reference.SourceIndex >= 0 AndAlso reference.SourceIndex < schedule.Courses.Count Then + Dim indexedCourse As TableCourse = schedule.Courses(reference.SourceIndex) + If indexedCourse.HasSameData(reference) Then Return indexedCourse + End If + + Return schedule.Courses.FirstOrDefault(Function(course) course.HasSameData(reference)) End Function 'web에서 문자열 가져오는 함수 @@ -218,15 +218,10 @@ Module DataModule Return Color.FromArgb(Red, Green, Blue) End Function - Public Sub writeTable(data As String) - Dim normalizedData As String = SerializeTableFragment(ParseTableFragment(data)) - File.WriteAllText(TableSaveLocation(False), normalizedData, Utf8WithoutBom) - End Sub - - Public Function readTable() As String - If My.Computer.FileSystem.FileExists(TableSaveLocation(False)) Then - 'My.Settings.defalutTable = OptionSave() - Dim data As String = ReadTableFile(TableSaveLocation(False)) + Public Function ReadScheduleData() As String + Dim filePath As String = TableSaveLocation(False) + If File.Exists(filePath) Then + Dim data As String = ReadTableFile(filePath) Return SerializeTableFragment(ParseTableFragment(data)) Else Return "" @@ -284,11 +279,4 @@ Module DataModule Return True End Function - Public Function xmlEncode(value As String) As String - Return SecurityElement.Escape(value) - End Function - - Public Function xmlDecode(value As String) As String - Return HttpUtility.HtmlDecode(value) - End Function End Module diff --git a/utable/Modules/WinModule.vb b/utable/Modules/WinModule.vb index 3fd4498..5b37fef 100644 --- a/utable/Modules/WinModule.vb +++ b/utable/Modules/WinModule.vb @@ -203,7 +203,7 @@ Module WinModule + "'아니오'를 누르시면 해당 정보는 제외된 채 정보가 복사됩니다)", vbQuestion + vbYesNo) = vbYes Then cominfo += vbCr + vbCr + "[uTable Default Data]" + vbCr - cominfo += readTable() + cominfo += ReadScheduleData() cominfo += vbCr + "[End of Data]" End If diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index 4a2d9ae..ac9d7ab 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -380,8 +380,8 @@ Public Class CellControl If appearPoint.X + ViewCourse.Width > TableForm.Location.X + TableForm.Width Then appearPoint.X = TableForm.Location.X + TableForm.Width - ViewCourse.Width If appearPoint.Y + ViewCourse.Height > TableForm.Location.Y + TableForm.Height Then appearPoint.Y = TableForm.Location.Y + TableForm.Height - ViewCourse.Height - ViewCourse.olddata = TryCast(Tag, String) - If String.IsNullOrEmpty(ViewCourse.olddata) Then Return + ViewCourse.currentCourse = TryCast(Tag, TableCourse) + If ViewCourse.currentCourse Is Nothing Then Return ViewCourse.blacktext = blackText ViewCourse.SetDesktopLocation(appearPoint.X, appearPoint.Y) @@ -476,20 +476,20 @@ Public Class CellControl End Sub Public Sub ModifyCheck(value As Boolean) - Dim data As String = readTable() - Dim olddata As String = TryCast(Tag, String) - If String.IsNullOrEmpty(olddata) Then Return - - Dim newdata As String = olddata - If newdata.Contains("") Then - Dim tmp As String = "" + getTableData(newdata, "checked") + "" - newdata = newdata.Replace(tmp, "" + value.ToString + "") - Else - newdata += vbTab + "" + value.ToString + "" + vbCrLf - End If + Dim reference As TableCourse = TryCast(Tag, TableCourse) + If reference Is Nothing Then Return + + Dim schedule As TableSchedule = LoadSchedule() + Dim course As TableCourse = FindCourse(schedule, reference) + If course Is Nothing Then Return + + course.IsChecked = value + course.CheckedSpecified = True + SaveSchedule(schedule) - writeTable(data.Replace(olddata, newdata)) - Tag = newdata + '화면이 보유한 참조도 디스크에 저장한 상태와 맞춘다. + reference.IsChecked = value + reference.CheckedSpecified = True End Sub Private Sub BeginFade() diff --git a/utable/uTable.vbproj b/utable/uTable.vbproj index e27f3e9..ea8b141 100644 --- a/utable/uTable.vbproj +++ b/utable/uTable.vbproj @@ -301,6 +301,7 @@ + True From 9ce2222f792778e6d72bdfe1e82ab5a25164bd4a Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 11:20:58 +0900 Subject: [PATCH 22/24] feat: add hover state reconciliation and manage course details form lifecycle in CellControl --- utable/UserControls/CellControl.vb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/utable/UserControls/CellControl.vb b/utable/UserControls/CellControl.vb index ac9d7ab..7a0d61d 100644 --- a/utable/UserControls/CellControl.vb +++ b/utable/UserControls/CellControl.vb @@ -74,6 +74,7 @@ Public Class CellControl Private ReadOnly hoverAnimationClock As New Stopwatch() Private hoverAnimationStartBounds As Rectangle Private hoverAnimationTargetBounds As Rectangle + Private courseDetailsForm As ViewCourse Private timeFont As Font Private titleFont As Font @@ -193,6 +194,12 @@ Public Class CellControl Protected Overrides Sub OnMouseLeave(e As EventArgs) MyBase.OnMouseLeave(e) + ReconcileHoverWithCursor() + End Sub + + Private Sub ReconcileHoverWithCursor() + If IsDisposed OrElse Disposing OrElse Not IsHandleCreated Then Return + '위쪽으로 확장되는 셀을 이동하면 새로 계산된 영역 안에 포인터가 있어도 'WinForms가 MouseLeave를 발생시킬 수 있음 If ClientRectangle.Contains(PointToClient(Cursor.Position)) Then Return @@ -362,6 +369,10 @@ Public Class CellControl End Sub Protected Overrides Sub OnHandleDestroyed(e As EventArgs) + If courseDetailsForm IsNot Nothing Then + RemoveHandler courseDetailsForm.FormClosed, AddressOf CourseDetailsFormClosed + courseDetailsForm = Nothing + End If hoverAnimationTimer.Stop() MyBase.OnHandleDestroyed(e) End Sub @@ -383,11 +394,25 @@ Public Class CellControl ViewCourse.currentCourse = TryCast(Tag, TableCourse) If ViewCourse.currentCourse Is Nothing Then Return + courseDetailsForm = ViewCourse + AddHandler courseDetailsForm.FormClosed, AddressOf CourseDetailsFormClosed ViewCourse.blacktext = blackText ViewCourse.SetDesktopLocation(appearPoint.X, appearPoint.Y) ViewCourse.Show() End Sub + Private Sub CourseDetailsFormClosed(sender As Object, e As FormClosedEventArgs) + Dim closedForm As ViewCourse = TryCast(sender, ViewCourse) + If closedForm IsNot Nothing Then + RemoveHandler closedForm.FormClosed, AddressOf CourseDetailsFormClosed + End If + If ReferenceEquals(courseDetailsForm, closedForm) Then courseDetailsForm = Nothing + + '별도 폼이 마우스 입력을 가져간 동안 CellControl의 MouseLeave가 누락될 수 + '있으므로, 폼이 닫히는 시점의 실제 포인터 위치로 호버 상태를 보정한다. + ReconcileHoverWithCursor() + End Sub + Private Function GetRequiredHeight() As Integer EnsureRenderFonts() Dim contentWidth As Integer = Math.Max(1, ClientSize.Width - HorizontalPadding * 2) From 1262d94b6a1512b7a90844986e8d485b6895ec31 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 11:41:08 +0900 Subject: [PATCH 23/24] feat: allow empty professor value in coursedata --- utable/Forms/SetCourse.vb | 8 ++++---- utable/Forms/TableForm.vb | 7 ++++++- utable/Forms/ViewCourse.vb | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/utable/Forms/SetCourse.vb b/utable/Forms/SetCourse.vb index 6022a60..fa1dc94 100644 --- a/utable/Forms/SetCourse.vb +++ b/utable/Forms/SetCourse.vb @@ -178,10 +178,10 @@ Public Class SetCourse Exit Sub End If - If ProfTB.Text = Nothing Then - MsgBox("교수명을 입력하세요.", vbExclamation) - Exit Sub - End If + 'If ProfTB.Text = Nothing Then + ' MsgBox("교수명을 입력하세요.", vbExclamation) + ' Exit Sub + 'End If If DayCombo.Text = Nothing Then MsgBox("요일을 선택하세요.", vbExclamation) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index cbe08d6..57a1593 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -1537,7 +1537,12 @@ Public Class TableForm Dim time As New List(Of Integer) For Each course As TableCourse In dayData - courses.Add(course.Name + " (" + course.Professor + ")") + If course.Professor = "" Then + courses.Add(course.Name) + Else + courses.Add(course.Name + " (" + course.Professor + ")") + End If + time.Add(course.Start) Next diff --git a/utable/Forms/ViewCourse.vb b/utable/Forms/ViewCourse.vb index 9f3690a..61ff733 100644 --- a/utable/Forms/ViewCourse.vb +++ b/utable/Forms/ViewCourse.vb @@ -268,7 +268,8 @@ Public Class ViewCourse courseTitle = NormalizeCourseTitle(course.Name) Text = courseTitle UpperTitleLabel.Text = courseTitle - SubTitleLabel.Text = course.Professor + ", " + SubTitleLabel.Text = course.Professor + If Not course.Professor = "" Then SubTitleLabel.Text += ", " Dim startt As Integer = course.Start Dim endt As Integer = course.End From ddaa9a3a2358a74b1fe03e8a703c0d39a249c404 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Thu, 20 Aug 2026 11:44:32 +0900 Subject: [PATCH 24/24] version: update version to 1.10.0.0 --- uTablePackage/Package.appxmanifest | 4 ++-- utable/My Project/AssemblyInfo.vb | 9 +++++---- utable/uTable.vbproj | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/uTablePackage/Package.appxmanifest b/uTablePackage/Package.appxmanifest index 6d42556..cb7b47c 100644 --- a/uTablePackage/Package.appxmanifest +++ b/uTablePackage/Package.appxmanifest @@ -1,11 +1,11 @@ - + - + uTable PBJSoftware diff --git a/utable/My Project/AssemblyInfo.vb b/utable/My Project/AssemblyInfo.vb index 70bef02..c6b11cb 100644 --- a/utable/My Project/AssemblyInfo.vb +++ b/utable/My Project/AssemblyInfo.vb @@ -1,4 +1,5 @@ -Imports System.Reflection +Imports System.Resources +Imports System.Reflection Imports System.Runtime.InteropServices ' 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해 @@ -30,6 +31,6 @@ Imports System.Runtime.InteropServices ' 기본값으로 할 수 있습니다. ' - - - + + + diff --git a/utable/uTable.vbproj b/utable/uTable.vbproj index ea8b141..d25430f 100644 --- a/utable/uTable.vbproj +++ b/utable/uTable.vbproj @@ -17,6 +17,7 @@ + false 게시\ true Disk @@ -28,8 +29,7 @@ false true 0 - 1.0.0.%2a - false + 1.10.0.0 false true @@ -456,4 +456,4 @@ Static - + \ No newline at end of file