diff --git a/.gitignore b/.gitignore index 9451532..b737457 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ Superstar.mdf Superstar_log.ldf .vs build.bat -DualScreenKTVPlayStation.exe \ No newline at end of file +DualScreenKTVPlayStation.exe +themes/superstar/_www/ \ No newline at end of file diff --git a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.BopomofoSearch.cs b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.BopomofoSearch.cs index 7c6eaa8..12d459c 100644 --- a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.BopomofoSearch.cs +++ b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.BopomofoSearch.cs @@ -8,21 +8,27 @@ using System.Collections.Generic; using IniParser; using IniParser.Model; using System.Text; - +/* Song Search with ZhuYin */ namespace DualScreenDemo { public partial class PrimaryForm { + // 注音歌曲的 PictureBox private PictureBox pictureBoxZhuYinSongs; - + //存放注音按鈕的陣列 private Button[] phoneticButtonsForSongs; + //特殊功能按鈕(修改、清除、關閉) private Button modifyButtonZhuYinSongs; private Button clearButtonZhuYinSongs; private Button closeButtonZhuYinSongs; + //用於顯示輸入文字的輸入框 private RichTextBox inputBoxZhuYinSongs; - + /// + /// 注音歌曲搜尋按鈕點擊事件 + /// private void ZhuyinSearchSongsButton_Click(object sender, EventArgs e) { + //更新搜尋模式按鈕的背景圖 zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongActiveBackground; englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground; pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground; @@ -30,196 +36,318 @@ namespace DualScreenDemo handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground; numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground; - + // 讀取 config.ini 並獲取注音圖片的路徑 var configData = LoadConfigData(); string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["ZhuYinSongs"]); - + //顯示注音歌曲圖片 ShowImageOnPictureBoxZhuYinSongs(Path.Combine(Application.StartupPath, imagePath)); - - SetZhuYinSingersAndButtonsVisibility(false); + //設定不同模式的UI顯示 + //SetZhuYinSingersAndButtonsVisibility(false); SetEnglishSingersAndButtonsVisibility(false); SetPinYinSingersAndButtonsVisibility(false); SetHandWritingForSingersAndButtonsVisibility(false); SetZhuYinSongsAndButtonsVisibility(true); pictureBoxZhuYinSongs.Visible = true; } - + /// + /// 初始化注音按鈕 (Phonetic Buttons) 並載入其對應的圖片與座標 + /// 1. 讀取 config.ini 設定檔,獲取按鈕的相關數據 (符號、座標、圖片) + /// 2. 解析注音符號並儲存至 phoneticSymbols 陣列 + /// 3. 解析按鈕的座標資訊,存入 phoneticButtonCoords + /// 4. 解析按鈕的圖片 (正常狀態、按下狀態、懸停狀態),存入 phoneticButtonImages。 + /// 5. 依序建立 35 個注音按鈕,並套用對應的圖片與事件處理函數。 + /// private void InitializePhoneticButtonsForSongs() { + // 1. 從設定檔 (config.ini) 讀取配置數據,包含按鈕座標、圖片等 var data = LoadConfigData(); + // 2. 讀取注音符號列表,這些符號將用於顯示在按鈕上 phoneticSymbols = LoadPhoneticSymbols(data); + + // 3. 從設定檔載入 **注音按鈕的座標**,每個按鈕都有對應的 (X, Y, Width, Height) phoneticButtonCoords = LoadButtonCoordinates(data, "PhoneticButtonCoordinates", 35); + + // 4. 從設定檔載入 **注音按鈕的圖片**,每個按鈕都有正常、按下、懸停三種狀態 phoneticButtonImages = LoadButtonImages(data, "PhoneticButtonImages", 35); + // 5. 建立 35 個注音按鈕的陣列 (每個按鈕對應一個注音符號) phoneticButtonsForSongs = new Button[35]; + + // 6. 迴圈建立所有的注音按鈕 for (int i = 0; i < 35; i++) { + // 取得當前按鈕的圖片 (從已載入的 phoneticButtonImages 字典中獲取) var buttonImages = phoneticButtonImages[$"button{i}"]; - CreatePhoneticButtonForSongs(i, buttonImages.normal, buttonImages.mouseDown, buttonImages.mouseOver); + + // 建立單個注音按鈕,並設定其圖片與點擊事件 + CreatePhoneticButtonForSongs( + i, // 按鈕索引 (對應於 phoneticSymbols) + buttonImages.normal, // 按鈕的普通狀態圖片 + buttonImages.mouseDown, // 按下時的圖片 + buttonImages.mouseOver // 滑鼠懸停時的圖片 + ); } } + + /// + /// 建立單個注音按鈕,並設定其圖片、大小、位置,以及滑鼠事件。 + /// + /// 按鈕索引 (對應於 phoneticSymbols) + /// 按鈕的普通狀態圖片路徑 + /// 按鈕被按下時的圖片路徑 + /// 滑鼠懸停時的圖片路徑 private void CreatePhoneticButtonForSongs(int index, string normalImagePath, string mouseDownImagePath, string mouseOverImagePath) { try { - - + // 1. 創建按鈕並初始化屬性 phoneticButtonsForSongs[index] = new Button { - Name = $"phoneticButton_{phoneticSymbols[index]}", - BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)), - BackgroundImageLayout = ImageLayout.Stretch, - FlatStyle = FlatStyle.Flat, - FlatAppearance = { BorderSize = 0 } + Name = $"phoneticButton_{phoneticSymbols[index]}", // 設定按鈕名稱,方便識別 + BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)), // 設定預設背景圖 + BackgroundImageLayout = ImageLayout.Stretch, // 背景圖自動填滿按鈕 + FlatStyle = FlatStyle.Flat, // 設定按鈕為扁平樣式 + FlatAppearance = { BorderSize = 0 } // 移除按鈕的邊框 }; - ResizeAndPositionButton(phoneticButtonsForSongs[index], phoneticButtonCoords[index].X, phoneticButtonCoords[index].Y, - phoneticButtonCoords[index].Width, phoneticButtonCoords[index].Height); + // 2. 設定按鈕的大小與位置 + ResizeAndPositionButton( + phoneticButtonsForSongs[index], + phoneticButtonCoords[index].X, + phoneticButtonCoords[index].Y, + phoneticButtonCoords[index].Width, + phoneticButtonCoords[index].Height + ); + // 3. 載入三種狀態的圖片 Image normalImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)); Image mouseDownImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseDownImagePath)); Image mouseOverImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseOverImagePath)); + // 4. 設定滑鼠事件,改變背景圖 phoneticButtonsForSongs[index].MouseDown += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = mouseDownImage; phoneticButtonsForSongs[index].MouseUp += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = normalImage; phoneticButtonsForSongs[index].MouseEnter += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = mouseOverImage; phoneticButtonsForSongs[index].MouseLeave += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = normalImage; + + // 5. 設定按鈕的點擊事件 (當按下按鈕時,觸發 PhoneticButton_Click) phoneticButtonsForSongs[index].Click += PhoneticButton_Click; + + // 6. 存入對應的注音符號,方便之後的處理 phoneticButtonsForSongs[index].Tag = phoneticSymbols[index]; + // 7. 將按鈕加入視窗 this.Controls.Add(phoneticButtonsForSongs[index]); - - } catch (Exception ex) { + // 例外處理,確保按鈕建立失敗時不影響其他部分 Console.WriteLine($"Error creating button at index {index}: {ex.Message}"); } } + + /// + /// 初始化注音輸入界面的所有按鈕和輸入框。 + /// private void InitializeButtonsForZhuYinSongs() { + // 1. 從設定檔 (config.ini) 載入注音符號 LoadPhoneticSymbolsFromConfig(); + + // 2. 初始化 35 個注音按鈕 InitializePhoneticButtonsForSongs(); + + // 3. 初始化特殊按鈕 (例如:刪除、確定、返回按鈕) InitializeSpecialButtonsForZhuYinSongs(); + + // 4. 初始化輸入框 (用於顯示使用者輸入的注音符號) InitializeInputBoxZhuYinSongs(); } + + /// + /// 初始化注音輸入界面的特殊按鈕,包括「修改」、「清除」和「關閉」。 + /// private void InitializeSpecialButtonsForZhuYinSongs() { - + // 1. 初始化「修改」按鈕 (刪除上一個輸入的注音符號) InitializeModifyButtonZhuYinSongs(); - + // 2. 初始化「清除」按鈕 (清空所有輸入內容) InitializeClearButtonZhuYinSongs(); - + // 3. 初始化「關閉」按鈕 (關閉注音輸入介面) InitializeCloseButtonZhuYinSongs(); } + + /// + /// 初始化「修改」按鈕,讓使用者可以刪除上一個輸入的注音符號。 + /// private void InitializeModifyButtonZhuYinSongs() { + // 1. 讀取設定檔 (config.ini) 來獲取特殊按鈕的座標與圖像資訊 var data = LoadConfigData(); + + // 2. 從設定檔讀取「修改」按鈕的座標數據 modifyButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonZhuYinSongs"); + + // 3. 從設定檔讀取「修改」按鈕的圖像 (Normal、MouseOver、MouseDown) var buttonImages = LoadButtonImages(data, "ModifyButtonImagesZhuYin"); + // 4. 使用座標與圖像來建立「修改」按鈕,並綁定點擊事件 modifyButtonZhuYinSongs = CreateSpecialButton( - "btnModifyZhuYinSongs", - modifyButtonZhuYinCoords, - buttonImages.normal, - buttonImages.mouseOver, - buttonImages.mouseDown, - ModifyButtonZhuYinSongs_Click + "btnModifyZhuYinSongs", // 按鈕名稱 + modifyButtonZhuYinCoords, // 按鈕座標 + buttonImages.normal, // 預設 (normal) 圖像 + buttonImages.mouseOver, // 滑鼠移過 (hover) 圖像 + buttonImages.mouseDown, // 按下 (pressed) 圖像 + ModifyButtonZhuYinSongs_Click // 綁定按鈕點擊事件 ); } + + /// + /// 「修改」按鈕的點擊事件,刪除輸入框內的最後一個注音符號。 + /// private void ModifyButtonZhuYinSongs_Click(object sender, EventArgs e) { - + // 1. 確保輸入框 (inputBoxZhuYinSongs) 存在於目前的視窗控制項中 if (this.Controls.Contains(inputBoxZhuYinSongs) && inputBoxZhuYinSongs.Text.Length > 0) { + // 2. 刪除輸入框中的最後一個字元 (即移除最後一個注音符號) inputBoxZhuYinSongs.Text = inputBoxZhuYinSongs.Text.Substring(0, inputBoxZhuYinSongs.Text.Length - 1); } } + /// + /// 初始化「清除」按鈕,並從設定檔載入其位置與圖片資源。 + /// private void InitializeClearButtonZhuYinSongs() { + // 1. 從設定檔 (config.ini) 讀取配置數據 var data = LoadConfigData(); + + // 2. 讀取「清除」按鈕的座標設定 (從 "SpecialButtonCoordinates" 內的 "clearButtonZhuYinSongs" 取得) clearButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonZhuYinSongs"); + + // 3. 讀取「清除」按鈕的圖片 (正常、滑鼠懸停、按下狀態) var buttonImages = LoadButtonImages(data, "ClearButtonImagesZhuYin"); + // 4. 建立「清除」按鈕,並設定對應的事件處理函式 (ClearButtonZhuYinSongs_Click) clearButtonZhuYinSongs = CreateSpecialButton( - "btnClearZhuYinSongs", - clearButtonZhuYinCoords, - buttonImages.normal, - buttonImages.mouseOver, - buttonImages.mouseDown, - ClearButtonZhuYinSongs_Click + "btnClearZhuYinSongs", // 按鈕名稱 + clearButtonZhuYinCoords, // 按鈕座標與大小 + buttonImages.normal, // 正常狀態圖片 + buttonImages.mouseOver, // 滑鼠懸停圖片 + buttonImages.mouseDown, // 按下時圖片 + ClearButtonZhuYinSongs_Click // 點擊事件處理函式 ); } + /// + /// 當使用者點擊「清除」按鈕時,將輸入框 (inputBoxZhuYinSongs) 的內容清空。 + /// + /// 觸發事件的按鈕。 + /// 事件參數。 private void ClearButtonZhuYinSongs_Click(object sender, EventArgs e) - { + { + // 1. 確保視窗內包含「注音輸入框」(inputBoxZhuYinSongs),且輸入框內有文字 if (this.Controls.Contains(inputBoxZhuYinSongs) && inputBoxZhuYinSongs.Text.Length > 0) { + // 2. 清空輸入框內容 inputBoxZhuYinSongs.Text = ""; } } + + /// + /// 初始化注音輸入的關閉按鈕,從設定檔讀取按鈕座標與圖片,並設置點擊事件 + /// private void InitializeCloseButtonZhuYinSongs() { + // 讀取設定檔數據 var data = LoadConfigData(); + + // 從設定檔讀取關閉按鈕的座標 closeButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonZhuYinSongs"); + + // 從設定檔讀取關閉按鈕的圖片 var buttonImages = LoadButtonImages(data, "CloseButtonImagesZhuYin"); + // 創建關閉按鈕並綁定點擊事件 closeButtonZhuYinSongs = CreateSpecialButton( - "btnCloseZhuYinSongs", - closeButtonZhuYinCoords, - buttonImages.normal, - buttonImages.mouseOver, - buttonImages.mouseDown, - CloseButtonZhuYinSongs_Click + "btnCloseZhuYinSongs", // 按鈕名稱 + closeButtonZhuYinCoords, // 按鈕座標 + buttonImages.normal, // 正常狀態圖片 + buttonImages.mouseOver, // 滑鼠懸停圖片 + buttonImages.mouseDown, // 按下圖片 + CloseButtonZhuYinSongs_Click // 綁定點擊事件 ); } + + /// + /// 關閉注音輸入介面,隱藏相關 UI 元件 + /// + /// 觸發事件的按鈕 (關閉按鈕) + /// 事件參數 private void CloseButtonZhuYinSongs_Click(object sender, EventArgs e) { + // 隱藏注音輸入的圖片 pictureBoxZhuYinSongs.Visible = false; + + // 隱藏注音輸入的所有按鈕與介面元素 SetZhuYinSongsAndButtonsVisibility(false); } + /// + /// 初始化注音輸入框 (RichTextBox),設定外觀、事件處理及位置大小 + /// private void InitializeInputBoxZhuYinSongs() { try { + // 讀取輸入框的配置,例如字體、顏色、大小等 LoadInputBoxConfig(); + // 建立注音輸入框並套用讀取到的設定 inputBoxZhuYinSongs = new RichTextBox { Name = "inputBoxZhuYinSongs", - ForeColor = inputBoxForeColor, - Font = new Font(inputBoxFontName, inputBoxFontSize, inputBoxFontStyle), - ScrollBars = RichTextBoxScrollBars.None + ForeColor = inputBoxForeColor, // 設定文字顏色 + Font = new Font(inputBoxFontName, inputBoxFontSize, inputBoxFontStyle), // 設定字體 + ScrollBars = RichTextBoxScrollBars.None // 禁用滾動條 }; - ResizeAndPositionControl(inputBoxZhuYinSongs, inputBoxZhuYinCoords.X, inputBoxZhuYinCoords.Y, inputBoxZhuYinCoords.Width, inputBoxZhuYinCoords.Height); + // 調整輸入框大小與位置 + ResizeAndPositionControl(inputBoxZhuYinSongs, inputBoxZhuYinCoords.X, inputBoxZhuYinCoords.Y, + inputBoxZhuYinCoords.Width, inputBoxZhuYinCoords.Height); + // 設定文字變更事件,用來即時篩選歌曲 inputBoxZhuYinSongs.TextChanged += (sender, e) => { - string searchText = inputBoxZhuYinSongs.Text; + string searchText = inputBoxZhuYinSongs.Text; // 取得輸入內容 + + // 根據輸入的注音篩選歌曲清單 var searchResults = allSongs.Where(song => song.PhoneticNotation.StartsWith(searchText)).ToList(); + + // 重置分頁 currentPage = 0; currentSongList = searchResults; totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); + // 更新多頁面面板的內容 multiPagePanel.currentPageIndex = 0; multiPagePanel.LoadSongs(currentSongList); }; + // 將輸入框加入到 UI 控制項 this.Controls.Add(inputBoxZhuYinSongs); } catch (Exception ex) @@ -227,51 +355,95 @@ namespace DualScreenDemo Console.WriteLine("Error initializing inputBoxZhuYinSongs: " + ex.Message); } } - + + /// + /// 存儲 PictureBoxZhuYinSongs 的座標與尺寸信息。 + /// + /// + /// 此元組包含以下四個值: + /// X:X 座標 + /// , Y:Y 座標 + /// , Width:寬度 + /// , Height:高度 + /// private (int X, int Y, int Width, int Height) pictureBoxZhuYinSongCoords; + /// + /// 從設定檔 (config.ini) 讀取 PictureBoxZhuYinSongs 的座標與尺寸 + /// private void LoadPictureBoxZhuYinSongCoordsFromConfig() { + // 建立 INI 檔案解析器 var parser = new FileIniDataParser(); + + // 讀取 config.ini 設定檔的內容 IniData data = parser.ReadFile("config.ini"); + // 取得 "PictureBoxZhuYinSongs" 段落的設定數據 var coords = data["PictureBoxZhuYinSongs"]; + + // 解析座標與尺寸,並存入 pictureBoxZhuYinSongCoords pictureBoxZhuYinSongCoords = ( - int.Parse(coords["X"]), - int.Parse(coords["Y"]), - int.Parse(coords["Width"]), - int.Parse(coords["Height"]) + int.Parse(coords["X"]), // 讀取 X 座標 + int.Parse(coords["Y"]), // 讀取 Y 座標 + int.Parse(coords["Width"]), // 讀取寬度 + int.Parse(coords["Height"]) // 讀取高度 ); } + + /// + /// 在 pictureBoxZhuYinSongs 上顯示指定路徑的圖片,並根據設定調整其大小與位置。 + /// + /// 要顯示的圖片檔案路徑 private void ShowImageOnPictureBoxZhuYinSongs(string imagePath) { - + // 從 config.ini 讀取 PictureBox 的座標與尺寸 LoadPictureBoxZhuYinSongCoordsFromConfig(); - + // 讀取圖片檔案並載入 Bitmap 物件 Bitmap originalImage = new Bitmap(imagePath); - - Rectangle displayArea = new Rectangle(pictureBoxZhuYinSongCoords.X, pictureBoxZhuYinSongCoords.Y, pictureBoxZhuYinSongCoords.Width, pictureBoxZhuYinSongCoords.Height); + // 設定圖片顯示區域,使用從設定檔讀取的座標與尺寸 + Rectangle displayArea = new Rectangle( + pictureBoxZhuYinSongCoords.X, + pictureBoxZhuYinSongCoords.Y, + pictureBoxZhuYinSongCoords.Width, + pictureBoxZhuYinSongCoords.Height + ); - + // 設定 PictureBox 的圖片 pictureBoxZhuYinSongs.Image = originalImage; - - ResizeAndPositionPictureBox(pictureBoxZhuYinSongs, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height); + // 調整 PictureBox 的大小與位置,使其符合設定 + ResizeAndPositionPictureBox( + pictureBoxZhuYinSongs, + displayArea.X, + displayArea.Y, + displayArea.Width, + displayArea.Height + ); + // 顯示 PictureBox pictureBoxZhuYinSongs.Visible = true; } + + /// + /// 設定注音歌曲相關的 PictureBox、按鈕和輸入框的可見性。 + /// + /// 若為 true,則顯示這些控件;否則隱藏。 private void SetZhuYinSongsAndButtonsVisibility(bool isVisible) { + // 定義要執行的操作,設定各控件的可見性 System.Action action = () => { try { + // 暫停佈局邏輯,防止在調整控件可見性時觸發不必要的佈局計算,提升性能 SuspendLayout(); + // 檢查並設定 pictureBoxZhuYinSongs 的可見性 if (pictureBoxZhuYinSongs == null) { Console.WriteLine("pictureBoxZhuYinSongs is null"); @@ -282,6 +454,7 @@ namespace DualScreenDemo if (isVisible) pictureBoxZhuYinSongs.BringToFront(); } + // 檢查並設定 phoneticButtonsForSongs 陣列中每個按鈕的可見性 if (phoneticButtonsForSongs == null) { Console.WriteLine("phoneticButtonsForSongs is null"); @@ -302,6 +475,7 @@ namespace DualScreenDemo } } + // 檢查並設定 modifyButtonZhuYinSongs 的可見性 if (modifyButtonZhuYinSongs == null) { Console.WriteLine("modifyButtonZhuYinSongs is null"); @@ -312,6 +486,7 @@ namespace DualScreenDemo if (isVisible) modifyButtonZhuYinSongs.BringToFront(); } + // 檢查並設定 clearButtonZhuYinSongs 的可見性 if (clearButtonZhuYinSongs == null) { Console.WriteLine("clearButtonZhuYinSongs is null"); @@ -322,6 +497,7 @@ namespace DualScreenDemo if (isVisible) clearButtonZhuYinSongs.BringToFront(); } + // 檢查並設定 closeButtonZhuYinSongs 的可見性 if (closeButtonZhuYinSongs == null) { Console.WriteLine("closeButtonZhuYinSongs is null"); @@ -332,6 +508,7 @@ namespace DualScreenDemo if (isVisible) closeButtonZhuYinSongs.BringToFront(); } + // 檢查並設定 inputBoxZhuYinSongs 的可見性 if (inputBoxZhuYinSongs == null) { Console.WriteLine("inputBoxZhuYinSongs is null"); @@ -342,10 +519,12 @@ namespace DualScreenDemo if (isVisible) inputBoxZhuYinSongs.BringToFront(); } + // 恢復佈局邏輯,允許佈局計算 ResumeLayout(); - PerformLayout(); + // 強制控件立即執行佈局邏輯,確保佈局更新立即生效 + PerformLayout(); - + // 刷新各控件,確保其狀態立即更新 pictureBoxZhuYinSongs?.Refresh(); if (phoneticButtonsForSongs != null) { @@ -361,10 +540,12 @@ namespace DualScreenDemo } catch (Exception ex) { + // 捕捉並輸出異常資訊,方便除錯 Console.WriteLine("Error in SetZhuYinSongsAndButtonsVisibility: " + ex.Message); } }; + // 如果當前執行緒需要呼叫 Invoke 方法才能修改控件,則使用 Invoke if (this.InvokeRequired) { this.Invoke(action); @@ -374,5 +555,6 @@ namespace DualScreenDemo action(); } } + } } \ No newline at end of file diff --git a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.EnglishSearch.cs b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.EnglishSearch.cs index ac459a9..19de466 100644 --- a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.EnglishSearch.cs +++ b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.EnglishSearch.cs @@ -7,11 +7,12 @@ using System.Drawing; using IniParser; using IniParser.Model; using System.Text; - +/* 歌曲查詢 英文輸入 */ namespace DualScreenDemo { public partial class PrimaryForm { + /* 主頁面設計 */ private PictureBox pictureBoxEnglishSongs; private Button[] numberButtonsForSongs; @@ -53,7 +54,7 @@ namespace DualScreenDemo var data = LoadConfigData(); numberButtonCoords = LoadButtonCoordinates(data, "NumberButtonCoordinates", 10); var buttonImages = LoadButtonImages(data, "NumberButtonImages", 10); - + /* 按鈕圖片路徑設置 */ numberButtonsForSongs = new Button[10]; for (int i = 0; i < 10; i++) { @@ -81,7 +82,7 @@ namespace DualScreenDemo this.Controls.Add(numberButtonsForSongs[i]); } } - + /* 按鈕按下事件 */ private void NumberButtonForSongs_Click(object sender, EventArgs e) { @@ -95,6 +96,7 @@ namespace DualScreenDemo } } + /* 鍵盤對應按鈕位置 */ private void InitializeLetterButtonsForEnglishSongs() { var data = LoadConfigData(); @@ -227,7 +229,7 @@ namespace DualScreenDemo } private void InitializeInputBoxEnglishSongs() - { + { /* 英文輸入介面設定,參考 config.ini */ try { var parser = new FileIniDataParser(); @@ -260,7 +262,7 @@ namespace DualScreenDemo }; ResizeAndPositionControl(inputBoxEnglishSongs, x, y, width, height); - + /*搜尋結果 傳回點歌介面*/ inputBoxEnglishSongs.TextChanged += (sender, e) => { string searchText = inputBoxEnglishSongs.Text; @@ -280,7 +282,7 @@ namespace DualScreenDemo Console.WriteLine($"An error occurred: {ex.Message}"); } } - + /* 圖片位置設置 */ private void ShowImageOnPictureBoxEnglishSongs(string imagePath) { try diff --git a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.NumberSearch.cs b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.NumberSearch.cs index f7edf9e..64012c3 100644 --- a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.NumberSearch.cs +++ b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.NumberSearch.cs @@ -8,95 +8,138 @@ namespace DualScreenDemo { public partial class PrimaryForm { + /// + /// 「數字搜尋」按鈕點擊事件 + /// 1. 更新所有搜尋按鈕的背景圖片,使「數字搜尋」顯示為啟動狀態 + /// 2. 在 `pictureBoxSongIDSearch` 顯示對應的圖片 + /// 3. 顯示「數字搜尋」相關 UI 元素 + /// private void NumberSearchButton2_Click(object sender, EventArgs e) { + // 更新搜尋按鈕的背景圖片 (將所有按鈕設為未選中狀態,僅數字搜尋為選中狀態) zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground; englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground; pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground; wordCountSearchSongButton.BackgroundImage = wordCountSearchSongNormalBackground; handWritingSearchSongButton.BackgroundImage = handWritingSearchNormalBackground; - numberSearchSongButton.BackgroundImage = numberSearchSongActiveBackground; + numberSearchSongButton.BackgroundImage = numberSearchSongActiveBackground; // 設定數字搜尋為選中狀態 + // 在 `pictureBoxSongIDSearch` 顯示數字搜尋的 UI 圖片 ShowImageOnPictureBoxSongIDSearch(Path.Combine(Application.StartupPath, @"themes\superstar\6-1.png")); + // 顯示與數字搜尋相關的 UI 元素 SetPictureBoxSongIDSearchAndButtonsVisibility(true); pictureBoxSongIDSearch.Visible = true; } + /// + /// 在 pictureBoxSongIDSearch 中顯示指定路徑的圖片,並對其進行裁切與調整大小 + /// 1. 加載原始圖片 + /// 2. 裁切圖片以符合 UI 需求 + /// 3. 設定 pictureBoxSongIDSearch 的圖片 + /// 4. 調整 pictureBoxSongIDSearch 的位置與大小 + /// 5. 顯示 pictureBoxSongIDSearch + /// + /// 要顯示的圖片路徑 private void ShowImageOnPictureBoxSongIDSearch(string imagePath) { - + // 1. 加載原始圖片 Bitmap originalImage = new Bitmap(imagePath); - + // 2. 定義裁切區域 (左上角 x=593, y=135, 寬=507, 高=508) Rectangle cropArea = new Rectangle(593, 135, 507, 508); - + // 3. 進行圖片裁切 Bitmap croppedImage = CropImage(originalImage, cropArea); - + // 4. 設定 `pictureBoxSongIDSearch` 的圖片 pictureBoxSongIDSearch.Image = croppedImage; - - + + // 5. 調整 `pictureBoxSongIDSearch` 的大小與位置 (基於 cropArea 並加上偏移量) ResizeAndPositionPictureBox(pictureBoxSongIDSearch, cropArea.X + offsetXSongID, cropArea.Y + offsetYSongID, cropArea.Width, cropArea.Height); - + + // 6. 顯示 `pictureBoxSongIDSearch` pictureBoxSongIDSearch.Visible = true; } + + /// + /// 當使用者點擊「修改按鈕」時,刪除 `inputBoxSongIDSearch` 輸入框內的最後一個字元。 + /// + /// 觸發事件的對象(按鈕) + /// 事件參數 private void ModifyButtonSongIDSearch_Click(object sender, EventArgs e) { - + // 確保 `inputBoxSongIDSearch` 內有文字,避免 `Substring` 發生錯誤 if (inputBoxSongIDSearch.Text.Length > 0) { - + // 刪除輸入框內的最後一個字元 inputBoxSongIDSearch.Text = inputBoxSongIDSearch.Text.Substring(0, inputBoxSongIDSearch.Text.Length - 1); } } + + /// + /// 當使用者點擊「關閉按鈕」時,隱藏歌曲編號搜尋的相關 UI 元件。 + /// + /// 觸發事件的對象(按鈕) + /// 事件參數 private void CloseButtonSongIDSearch_Click(object sender, EventArgs e) { - + // 隱藏歌曲編號搜尋的相關 UI 元件(包含輸入框、按鈕、圖片等) SetPictureBoxSongIDSearchAndButtonsVisibility(false); } + /// + /// 控制歌曲編號搜尋的 UI 顯示與隱藏。 + /// true 時,顯示 UI 元件。 + /// false 時,隱藏 UI 元件。 + /// + /// 是否顯示 UI private void SetPictureBoxSongIDSearchAndButtonsVisibility(bool isVisible) { + // 定義 UI 更新的 Action System.Action action = () => { + // 暫停佈局更新,提高效能 SuspendLayout(); - + // 控制歌曲編號搜尋圖片的顯示/隱藏 pictureBoxSongIDSearch.Visible = isVisible; - + // 如果顯示圖片,則將其置頂 if (isVisible) pictureBoxSongIDSearch.BringToFront(); - + // 控制「修改」和「關閉」按鈕的可見性 modifyButtonSongIDSearch.Visible = isVisible; closeButtonSongIDSearch.Visible = isVisible; - + // 如果按鈕顯示,則置頂 if (isVisible) { modifyButtonSongIDSearch.BringToFront(); closeButtonSongIDSearch.BringToFront(); } - + // 控制數字按鈕的可見性 foreach (Button button in numberButtonsSongIDSearch) { button.Visible = isVisible; + // 使按鈕顯示時位於最上層 if (isVisible) button.BringToFront(); } + // 控制輸入框的可見性 inputBoxSongIDSearch.Visible = isVisible; if (isVisible) inputBoxSongIDSearch.BringToFront(); + // 恢復佈局更新 ResumeLayout(); }; + // 如果當前執行緒不是 UI 執行緒,則使用 Invoke 呼叫 if (this.InvokeRequired) { this.Invoke(action); @@ -106,113 +149,140 @@ namespace DualScreenDemo action(); } } + + /// + /// 初始化數字輸入 (Song ID Search) 相關按鈕與輸入框。 + /// 這些按鈕用於數字輸入、刪除 (Modify) 及關閉 (Close) 功能。 + /// private void InitializeButtonsForPictureBoxSongIDSearch() { - + // 數字按鈕的座標陣列 (左上角 X, 左上角 Y, 右下角 X, 右下角 Y) int[,] coords = new int[,] { - {651, 292, 752, 400}, - {760, 292, 861, 400}, - {869, 292, 972, 399}, - {652, 401, 752, 502}, - {760, 401, 861, 504}, - {869, 398, 972, 502}, - {651, 502, 753, 607}, - {759, 504, 863, 607}, - {869, 503, 973, 608}, - {981, 501, 1083, 609} + {651, 292, 752, 400}, // 1 + {760, 292, 861, 400}, // 2 + {869, 292, 972, 399}, // 3 + {652, 401, 752, 502}, // 4 + {760, 401, 861, 504}, // 5 + {869, 398, 972, 502}, // 6 + {651, 502, 753, 607}, // 7 + {759, 504, 863, 607}, // 8 + {869, 503, 973, 608}, // 9 + {981, 501, 1083, 609} // 0 }; + // 取得螢幕的解析度 int screenW = Screen.PrimaryScreen.Bounds.Width; int screenH = Screen.PrimaryScreen.Bounds.Height; - + // 計算畫面縮放比例 float widthRatio = screenW / (float)1920; float heightRatio = screenH / (float)900; + // 初始化數字按鈕陣列 numberButtonsSongIDSearch = new Button[10]; + // 建立數字按鈕 for (int i = 0; i < numberButtonsSongIDSearch.Length; i++) { numberButtonsSongIDSearch[i] = new Button(); - + // 設定按鈕大小與位置 ConfigureButton( numberButtonsSongIDSearch[i], - coords[i, 0], - coords[i, 1], - coords[i, 2] - coords[i, 0], - coords[i, 3] - coords[i, 1], - - resizedNormalStateImageFor6_1, - resizedMouseOverImageFor6_1, - resizedMouseDownImageFor6_1, + coords[i, 0], // X 座標 + coords[i, 1], // Y 座標 + coords[i, 2] - coords[i, 0], // 按鈕寬度 + coords[i, 3] - coords[i, 1], // 按鈕高度 + resizedNormalStateImageFor6_1, // 一般狀態圖片 + resizedMouseOverImageFor6_1, // 滑鼠懸停圖片 + resizedMouseDownImageFor6_1, // 按下時圖片 null ); + // 重新計算按鈕位置,確保適應不同解析度 int newXForSongID = (int)(((numberButtonsSongIDSearch[i].Location.X / widthRatio) + offsetXSongID) * widthRatio); int newYForSongID = (int)(((numberButtonsSongIDSearch[i].Location.Y / heightRatio) + offsetYSongID) * heightRatio); numberButtonsSongIDSearch[i].Location = new Point(newXForSongID, newYForSongID); - + // 設定按鈕名稱與 Tag (數字值) numberButtonsSongIDSearch[i].Name = "NumberButtonSongIDSearch" + i; - numberButtonsSongIDSearch[i].Tag = (i + 1).ToString(); - if (i == 9) + if (i == 9) // 最後一個按鈕 (實際上應為數字 "0") { numberButtonsSongIDSearch[i].Name = "NumberButtonSongIDSearch0"; numberButtonsSongIDSearch[i].Tag = "0"; } - + // 設定按鈕的點擊事件 numberButtonsSongIDSearch[i].Click += SongIDSearchButton_Click; - + // 加入畫面 this.Controls.Add(numberButtonsSongIDSearch[i]); } - - modifyButtonSongIDSearch = new Button { + // 初始化「刪除」按鈕 (Modify) + modifyButtonSongIDSearch = new Button + { Name = "ModifyButtonSongIDSearch", Tag = "Modify", Visible = false }; - ConfigureButton(modifyButtonSongIDSearch, 978, 292, 1081 - 978, 397 - 292, resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, ModifyButtonSongIDSearch_Click); + ConfigureButton(modifyButtonSongIDSearch, 978, 292, 1081 - 978, 397 - 292, + resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, ModifyButtonSongIDSearch_Click); + + // 調整刪除按鈕位置以適應不同解析度 int newX = (int)(((modifyButtonSongIDSearch.Location.X / widthRatio) + offsetXSongID) * widthRatio); - int newY = (int)(((modifyButtonSongIDSearch.Location.Y / widthRatio) + offsetYSongID) * heightRatio); + int newY = (int)(((modifyButtonSongIDSearch.Location.Y / heightRatio) + offsetYSongID) * heightRatio); modifyButtonSongIDSearch.Location = new Point(newX, newY); + this.Controls.Add(modifyButtonSongIDSearch); - - - closeButtonSongIDSearch = new Button { + + // 初始化「關閉」按鈕 (Close) + closeButtonSongIDSearch = new Button + { Name = "CloseButtonSongIDSearch", Tag = "Close", Visible = false }; - - ConfigureButton(closeButtonSongIDSearch, 982, 147, 1082 - 982, 250 - 147, resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, CloseButtonSongIDSearch_Click); + + ConfigureButton(closeButtonSongIDSearch, 982, 147, 1082 - 982, 250 - 147, + resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, CloseButtonSongIDSearch_Click); + + // 調整關閉按鈕位置以適應不同解析度 newX = (int)(((closeButtonSongIDSearch.Location.X / widthRatio) + offsetXSongID) * widthRatio); - newY = (int)(((closeButtonSongIDSearch.Location.Y / widthRatio) + offsetYSongID) * heightRatio); + newY = (int)(((closeButtonSongIDSearch.Location.Y / heightRatio) + offsetYSongID) * heightRatio); closeButtonSongIDSearch.Location = new Point(newX, newY); + this.Controls.Add(closeButtonSongIDSearch); - inputBoxSongIDSearch = new RichTextBox(); - inputBoxSongIDSearch.Name = "inputBoxSongIDSearch"; - ResizeAndPositionControl(inputBoxSongIDSearch, 645 + offsetXSongID, 197 + offsetXSongID, 986 - 645, 281 - 197); - inputBoxSongIDSearch.ForeColor = Color.Black; - inputBoxSongIDSearch.Font = new Font("細明體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular); + // 初始化輸入框 (RichTextBox) + inputBoxSongIDSearch = new RichTextBox + { + Name = "inputBoxSongIDSearch", + ForeColor = Color.Black, + Font = new Font("細明體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular) + }; + // 調整輸入框大小與位置 + ResizeAndPositionControl(inputBoxSongIDSearch, 645 + offsetXSongID, 197 + offsetXSongID, 986 - 645, 281 - 197); + + // 當輸入框內容改變時,自動搜尋歌曲 inputBoxSongIDSearch.TextChanged += (sender, e) => { string searchText = inputBoxSongIDSearch.Text; - + + // 使用 LINQ 搜尋符合條件的歌曲 (以輸入的數字開頭) var searchResults = allSongs.Where(song => song.SongNumber.StartsWith(searchText)).ToList(); + + // 重設分頁與歌曲列表 currentPage = 0; currentSongList = searchResults; totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); + // 更新歌曲顯示 multiPagePanel.currentPageIndex = 0; multiPagePanel.LoadSongs(currentSongList); }; @@ -220,16 +290,25 @@ namespace DualScreenDemo this.Controls.Add(inputBoxSongIDSearch); } + + /// + /// 數字按鈕點擊事件,將按鈕上的數字 (Tag 值) 加入到 inputBoxSongIDSearch 中。 + /// + /// 觸發事件的按鈕 + /// 事件參數 private void SongIDSearchButton_Click(object sender, EventArgs e) { - - + // 嘗試將 sender 轉型為 Button var button = sender as Button; + + // 確保按鈕不為 null 且具有 Tag 屬性 (Tag 屬性存放數字) if (button != null && button.Tag != null) { + // 將按鈕的 Tag 值 (數字) 加入到輸入框 inputBoxSongIDSearch.Text += button.Tag.ToString(); } } + } } \ No newline at end of file diff --git a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.PinyinSearch.cs b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.PinyinSearch.cs index 818b3af..d406e69 100644 --- a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.PinyinSearch.cs +++ b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.PinyinSearch.cs @@ -10,15 +10,22 @@ namespace DualScreenDemo { public partial class PrimaryForm { + // 拼音歌曲的 PictureBox private PictureBox pictureBoxPinYinSongs; + // 存放拼音按鈕的陣列 private Button[] letterButtonsForPinYinSongs; + // 特殊功能按鈕(修改、清除、關閉) private Button modifyButtonPinYinSongs; private Button clearButtonPinYinSongs; private Button closeButtonPinYinSongs; + // 用於顯示輸入文字的輸入框 private RichTextBox inputBoxPinYinSongs; - + /// + /// 拼音歌曲搜尋按鈕點擊事件 + /// private void PinyinSearchSongsButton_Click(object sender, EventArgs e) { + // 更新搜尋模式按鈕的背景圖 zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground; englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground; pinyinSearchSongButton.BackgroundImage = pinyinSearchSongActiveBackground; @@ -26,288 +33,445 @@ namespace DualScreenDemo handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground; numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground; - + // 讀取 config.ini 並獲取拼音圖片的路徑 var configData = LoadConfigData(); string pinyinImagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["PinYinSongs"]); - + // 顯示拼音歌曲圖片 ShowImageOnPictureBoxPinYinSongs(Path.Combine(Application.StartupPath, pinyinImagePath)); - + // 設定不同模式的 UI 顯示 SetZhuYinSingersAndButtonsVisibility(false); SetEnglishSingersAndButtonsVisibility(false); SetPinYinSingersAndButtonsVisibility(false); SetPinYinSongsAndButtonsVisibility(true); pictureBoxPinYinSongs.Visible = true; } - + /// + /// 初始化拼音按鈕 + /// private void InitializeLetterButtonsForPinYinSongs() { + // 從設定檔 (config.ini) 讀取配置數據 var data = LoadConfigData(); + + // 從配置數據中載入拼音字母按鈕的影像 (包含正常、點擊、滑鼠懸停三種狀態) var buttonImages = LoadButtonImages(data, "PinYinLetterButtonImages", 26); + + // 定義 QWERTY 鍵盤排列的字母順序 string qwertyLayout = "QWERTYUIOPASDFGHJKLZXCVBNM"; + + // 初始化拼音按鈕陣列,長度為 26(對應英文字母) letterButtonsForPinYinSongs = new Button[26]; + // 迴圈遍歷 26 個字母,依序建立按鈕 for (int i = 0; i < 26; i++) { + // 從配置檔案讀取當前按鈕的座標資訊 (X, Y, Width, Height) var coords = data["PinYinLetterButtonCoordinates"][$"button{i}"].Split(','); + + // 建立拼音按鈕,並設定名稱、座標、影像與事件處理函式 letterButtonsForPinYinSongs[i] = CreateButton( - $"letterButton_{qwertyLayout[i]}", - (int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3])), - buttonImages[$"button{i}"].normal, - buttonImages[$"button{i}"].mouseDown, - buttonImages[$"button{i}"].mouseOver, - LetterButtonPinYinSongs_Click + $"letterButton_{qwertyLayout[i]}", // 按鈕名稱,例如 "letterButton_Q" + (int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3])), // 解析座標數據 + buttonImages[$"button{i}"].normal, // 正常狀態影像 + buttonImages[$"button{i}"].mouseDown, // 按下狀態影像 + buttonImages[$"button{i}"].mouseOver, // 滑鼠懸停狀態影像 + LetterButtonPinYinSongs_Click // 點擊事件處理函式 ); + + // 設定按鈕的標籤 (Tag) 為對應的字母,例如 Q、W、E... letterButtonsForPinYinSongs[i].Tag = qwertyLayout[i]; + + // 將按鈕新增到表單的控制項集合中,讓其顯示在介面上 this.Controls.Add(letterButtonsForPinYinSongs[i]); } } - + /// + /// 處理拼音按鈕點擊事件 + /// + /// 觸發事件按鈕 + /// 事件參數 private void LetterButtonPinYinSongs_Click(object sender, EventArgs e) { - + // 嘗試將觸發事件的物件轉換為 Button 類型 var button = sender as Button; + + // 檢查按鈕是否為 null,並確保該按鈕的 Tag 屬性不為 null if (button != null && button.Tag != null) { + // 確保輸入框 (inputBoxPinYinSongs) 是可見狀態 if (inputBoxPinYinSongs.Visible) { + // 將按鈕的標籤 (Tag) 轉換為字串,並附加到輸入框的文字內容中 inputBoxPinYinSongs.Text += button.Tag.ToString(); } } } + /// + /// 初始化拼音輸入相關的 UI 控件,包括字母按鈕、特殊功能按鈕(修改、清除、關閉),以及拼音輸入框。 + /// private void InitializeButtonsForPinYinSongs() { + // 初始化拼音字母按鈕,根據 QWERTY 鍵盤佈局建立對應的按鈕 InitializeLetterButtonsForPinYinSongs(); + + // 初始化特殊功能按鈕(修改、清除、關閉) InitializeSpecialButtonsForPinYinSongs(); + + // 初始化拼音輸入框,使用者可透過輸入拼音來搜尋歌曲 InitializeInputBoxPinYinSongs(); } + /// + /// 初始化拼音輸入的特殊功能按鈕,包括: + /// 1. 修改按鈕 - 刪除輸入框中的最後一個字母 + /// 2. 清除按鈕 - 清空輸入框的內容 + /// 3. 關閉按鈕 - 隱藏拼音輸入的 UI 元件 + /// private void InitializeSpecialButtonsForPinYinSongs() { - + // 初始化「修改」按鈕(刪除輸入框最後一個字母) InitializeModifyButtonPinYinSongs(); - + // 初始化「清除」按鈕(清空輸入框內容) InitializeClearButtonPinYinSongs(); - + // 初始化「關閉」按鈕(關閉拼音輸入 UI) InitializeCloseButtonPinYinSongs(); } + + /// + /// 初始化「修改」按鈕,提供刪除拼音輸入框最後一個字母的功能。 + /// private void InitializeModifyButtonPinYinSongs() { + // 讀取設定檔,載入特殊按鈕的配置資料 var data = LoadConfigData(); + + // 從設定檔取得「修改按鈕」的座標與大小 modifyButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonPinYinSongs"); + + // 讀取「修改按鈕」的圖片資源(一般狀態、滑鼠懸停、按下) var buttonImages = LoadButtonImages(data, "ModifyButtonImagesPinYin"); + // 創建「修改」按鈕,並綁定點擊事件 modifyButtonPinYinSongs = CreateSpecialButton( - "btnModifyPinYinSongs", - modifyButtonPinYinCoords, - buttonImages.normal, - buttonImages.mouseOver, - buttonImages.mouseDown, - ModifyButtonPinYinSongs_Click + "btnModifyPinYinSongs", // 按鈕名稱 + modifyButtonPinYinCoords, // 設定按鈕的座標與大小 + buttonImages.normal, // 設定按鈕的正常狀態圖片 + buttonImages.mouseOver, // 設定按鈕的滑鼠懸停圖片 + buttonImages.mouseDown, // 設定按鈕的按下狀態圖片 + ModifyButtonPinYinSongs_Click // 綁定按鈕的點擊事件處理函式 ); } + /// + /// 「修改」按鈕點擊事件:刪除拼音輸入框 (inputBoxPinYinSongs) 中的最後一個字母。 + /// private void ModifyButtonPinYinSongs_Click(object sender, EventArgs e) { - + // 確保 inputBoxPinYinSongs 存在於視窗控制項集合內,且輸入框內有文字 if (this.Controls.Contains(inputBoxPinYinSongs) && inputBoxPinYinSongs.Text.Length > 0) { + // 刪除輸入框內的最後一個字母 inputBoxPinYinSongs.Text = inputBoxPinYinSongs.Text.Substring(0, inputBoxPinYinSongs.Text.Length - 1); } } + + /// + /// 初始化「清除」按鈕 (clearButtonPinYinSongs),用於清空拼音輸入框 (inputBoxPinYinSongs)。 + /// private void InitializeClearButtonPinYinSongs() { + // 從設定檔載入資料 var data = LoadConfigData(); + + // 讀取「清除」按鈕的座標配置 (X, Y, Width, Height) clearButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonPinYinSongs"); + + // 載入「清除」按鈕的圖片 (一般狀態、滑鼠懸停、按下時的圖片) var buttonImages = LoadButtonImages(data, "ClearButtonImagesPinYin"); + // 建立「清除」按鈕,設定對應的座標與圖片,並綁定點擊事件 clearButtonPinYinSongs = CreateSpecialButton( - "btnClearPinYinSongs", - clearButtonPinYinCoords, - buttonImages.normal, - buttonImages.mouseOver, - buttonImages.mouseDown, - ClearButtonPinYinSongs_Click + "btnClearPinYinSongs", // 按鈕名稱 + clearButtonPinYinCoords, // 設定按鈕的座標與大小 + buttonImages.normal, // 設定按鈕的正常狀態圖片 + buttonImages.mouseOver, // 設定按鈕的滑鼠懸停圖片 + buttonImages.mouseDown, // 設定按鈕的按下狀態圖片 + ClearButtonPinYinSongs_Click // 綁定按鈕的點擊事件處理函式 ); } + /// + /// 清空拼音輸入框的內容。 + /// 當使用者點擊清除按鈕時,若輸入框存在且有內容,則將其清空。 + /// private void ClearButtonPinYinSongs_Click(object sender, EventArgs e) { + // 檢查視窗內是否包含 inputBoxPinYinSongs 控制項,且輸入框內是否有文字 if (this.Controls.Contains(inputBoxPinYinSongs) && inputBoxPinYinSongs.Text.Length > 0) { + // 清空拼音輸入框的內容 inputBoxPinYinSongs.Text = ""; } } + /// + /// 初始化「關閉」按鈕 (closeButtonPinYinSongs),用於隱藏拼音輸入介面。 + /// private void InitializeCloseButtonPinYinSongs() { + // 讀取設定檔中的按鈕配置數據 var data = LoadConfigData(); + + // 從設定檔中取得「關閉」按鈕的座標 (X, Y, Width, Height) closeButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonPinYinSongs"); + + // 從設定檔中讀取「關閉」按鈕的圖片 (一般狀態、滑鼠懸停、按下時的圖片) var buttonImages = LoadButtonImages(data, "CloseButtonImagesPinYin"); + // 建立「關閉」按鈕,設定名稱、座標、圖片及點擊事件 closeButtonPinYinSongs = CreateSpecialButton( - "btnClosePinYinSongs", - closeButtonPinYinCoords, - buttonImages.normal, - buttonImages.mouseOver, - buttonImages.mouseDown, - CloseButtonPinYinSongs_Click + "btnClosePinYinSongs", // 按鈕名稱 + closeButtonPinYinCoords, // 設定按鈕的座標與大小 + buttonImages.normal, // 設定按鈕的正常狀態圖片 + buttonImages.mouseOver, // 設定按鈕的滑鼠懸停圖片 + buttonImages.mouseDown, // 設定按鈕的按下狀態圖片 + CloseButtonPinYinSongs_Click // 綁定按鈕的點擊事件處理函式 ); } + /// + /// 關閉拼音輸入模式,隱藏相關 UI 元件。 + /// + /// 觸發事件的按鈕。 + /// 事件參數。 private void CloseButtonPinYinSongs_Click(object sender, EventArgs e) { + // 隱藏拼音輸入的背景圖片 (可能是 UI 中的輸入框背景) pictureBoxPinYinSongs.Visible = false; + // 設定拼音輸入框與所有相關按鈕的可見性為 false SetPinYinSongsAndButtonsVisibility(false); - } + } + /// + /// 初始化拼音輸入框 (RichTextBox),並從 config.ini 讀取相關設定。 + /// private void InitializeInputBoxPinYinSongs() { try { + // 創建一個 INI 檔案解析器 var parser = new FileIniDataParser(); - parser.Parser.Configuration.AssigmentSpacer = ""; - parser.Parser.Configuration.CommentString = "#"; - parser.Parser.Configuration.CaseInsensitive = true; - - IniData data; + // 配置解析器的參數 + parser.Parser.Configuration.AssigmentSpacer = ""; // 設定 = 兩側沒有空格 + parser.Parser.Configuration.CommentString = "#"; // 使用 # 作為註解符號 + parser.Parser.Configuration.CaseInsensitive = true; // 參數名稱不區分大小寫 + + IniData data; // 儲存解析後的 INI 數據 + + // 讀取 config.ini 文件,使用 UTF-8 編碼 using (var reader = new StreamReader("config.ini", System.Text.Encoding.UTF8)) { data = parser.ReadData(reader); } - int x = int.Parse(data["InputBoxPinYinSongs"]["X"]); - int y = int.Parse(data["InputBoxPinYinSongs"]["Y"]); - int width = int.Parse(data["InputBoxPinYinSongs"]["Width"]); - int height = int.Parse(data["InputBoxPinYinSongs"]["Height"]); - string fontName = data["InputBoxPinYinSongs"]["FontName"]; - float fontSize = float.Parse(data["InputBoxPinYinSongs"]["FontSize"]); - FontStyle fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxPinYinSongs"]["FontStyle"]); - Color foreColor = Color.FromName(data["InputBoxPinYinSongs"]["ForeColor"]); + // **從 INI 檔案讀取拼音輸入框的位置與大小** + int x = int.Parse(data["InputBoxPinYinSongs"]["X"]); // X 座標 + int y = int.Parse(data["InputBoxPinYinSongs"]["Y"]); // Y 座標 + int width = int.Parse(data["InputBoxPinYinSongs"]["Width"]); // 寬度 + int height = int.Parse(data["InputBoxPinYinSongs"]["Height"]); // 高度 + // **讀取字型設定** + string fontName = data["InputBoxPinYinSongs"]["FontName"]; // 字型名稱 + float fontSize = float.Parse(data["InputBoxPinYinSongs"]["FontSize"]); // 字體大小 + FontStyle fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxPinYinSongs"]["FontStyle"]); // 字體樣式 + Color foreColor = Color.FromName(data["InputBoxPinYinSongs"]["ForeColor"]); // 文字顏色 + + // 創建拼音輸入框 (RichTextBox) inputBoxPinYinSongs = new RichTextBox { - Visible = false, - Name = "inputBoxPinYinSongs", - ForeColor = foreColor, - Font = new Font(fontName, fontSize / 900 * Screen.PrimaryScreen.Bounds.Height, fontStyle) + Visible = false, // 預設為隱藏 + Name = "inputBoxPinYinSongs", // 設定控制項名稱 + ForeColor = foreColor, // 設定文字顏色 + Font = new Font( + fontName, + fontSize / 900 * Screen.PrimaryScreen.Bounds.Height, // 根據螢幕大小調整字體 + fontStyle + ) }; + // 設定輸入框的位置與大小 ResizeAndPositionControl(inputBoxPinYinSongs, x, y, width, height); + // **綁定 TextChanged 事件 (當輸入內容改變時觸發搜尋)** inputBoxPinYinSongs.TextChanged += (sender, e) => { string searchText = inputBoxPinYinSongs.Text; - var searchResults = allSongs.Where(song => song.PinyinNotation.StartsWith(searchText)).ToList(); - currentPage = 0; - currentSongList = searchResults; - totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); + // 根據拼音前綴篩選歌曲 + var searchResults = allSongs.Where(song => song.PinyinNotation.StartsWith(searchText)).ToList(); + + currentPage = 0; // 重置當前頁面索引 + currentSongList = searchResults; // 更新搜尋結果 + totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); // 計算總頁數 + + // 更新 UI,顯示搜尋結果 multiPagePanel.currentPageIndex = 0; multiPagePanel.LoadSongs(currentSongList); }; + // 將拼音輸入框加入視窗中 this.Controls.Add(inputBoxPinYinSongs); } catch (Exception ex) { + // 發生錯誤時輸出錯誤訊息 (避免程式崩潰) Console.WriteLine($"An error occurred: {ex.Message}"); } } - + /// + /// 存儲 PictureBoxPinYinSongs 的座標與尺寸信息。 + /// + /// + /// 此元組包含以下四個值: + /// X:X 座標 + /// , Y:Y 座標 + /// , Width:寬度 + /// , Height:高度 + /// private (int X, int Y, int Width, int Height) pictureBoxPinYinSongCoords; + /// + /// 從 config.ini 配置檔案中載入 PictureBoxPinYinSongs 的座標與尺寸設定。 + /// private void LoadPictureBoxPinYinSongCoordsFromConfig() { + // 創建一個 INI 檔案解析器 var parser = new FileIniDataParser(); + + // 讀取 config.ini 文件並解析成 IniData 對象 IniData data = parser.ReadFile("config.ini"); + // 取得 PictureBoxPinYinSongs 區段的設定值 var coords = data["PictureBoxPinYinSongs"]; + + // 解析 X, Y, Width, Height,並存入 pictureBoxPinYinSongCoords pictureBoxPinYinSongCoords = ( - int.Parse(coords["X"]), - int.Parse(coords["Y"]), - int.Parse(coords["Width"]), - int.Parse(coords["Height"]) + int.Parse(coords["X"]), // 解析 X 座標 + int.Parse(coords["Y"]), // 解析 Y 座標 + int.Parse(coords["Width"]), // 解析 寬度 + int.Parse(coords["Height"]) // 解析 高度 ); } - + /// + /// 顯示拼音歌曲圖片 + /// + /// 圖片路徑 private void ShowImageOnPictureBoxPinYinSongs(string imagePath) { - - LoadPictureBoxPinYinSongCoordsFromConfig(); + // 從設定檔載入 PictureBox 的座標與大小 + LoadPictureBoxPinYinSongCoordsFromConfig(); - - Bitmap originalImage = new Bitmap(imagePath); + // 使用指定的圖片路徑建立 Bitmap 影像 + Bitmap originalImage = new Bitmap(imagePath); - - Rectangle displayArea = new Rectangle(pictureBoxPinYinSongCoords.X, pictureBoxPinYinSongCoords.Y, pictureBoxPinYinSongCoords.Width, pictureBoxPinYinSongCoords.Height); + // 建立一個矩形,表示 PictureBox 應該顯示的範圍 + Rectangle displayArea = new Rectangle( + pictureBoxPinYinSongCoords.X, // 設定 X 座標 + pictureBoxPinYinSongCoords.Y, // 設定 Y 座標 + pictureBoxPinYinSongCoords.Width, // 設定 寬度 + pictureBoxPinYinSongCoords.Height // 設定 高度 + ); - - pictureBoxPinYinSongs.Image = originalImage; + // 將載入的圖片設定為 pictureBoxPinYinSongs 的影像 + pictureBoxPinYinSongs.Image = originalImage; + + // 調整 PictureBox 的大小與位置,使其符合 displayArea 的設定 + ResizeAndPositionPictureBox( + pictureBoxPinYinSongs, + displayArea.X, + displayArea.Y, + displayArea.Width, + displayArea.Height + ); + + // 顯示 PictureBox + pictureBoxPinYinSongs.Visible = true; - - ResizeAndPositionPictureBox(pictureBoxPinYinSongs, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height); - - pictureBoxPinYinSongs.Visible = true; } - + /// + /// 設定拼音模式的 UI 是否可見 + /// + /// 是否可見 private void SetPinYinSongsAndButtonsVisibility(bool isVisible) { + // 定義一個委派 (Action),用於更新 UI 控件的可見性 System.Action action = () => { + // 暫停佈局更新,以防止 UI 閃爍或重繪時出現異常 SuspendLayout(); + // 設定 pictureBoxPinYinSongs 的可見性 pictureBoxPinYinSongs.Visible = isVisible; - if (isVisible) pictureBoxPinYinSongs.BringToFront(); + if (isVisible) pictureBoxPinYinSongs.BringToFront(); // 確保顯示時位於最前方 + // 設定所有拼音字母按鈕的可見性 foreach (var button in letterButtonsForPinYinSongs) { button.Visible = isVisible; if (isVisible) button.BringToFront(); } + // 設定 modifyButtonPinYinSongs (修改按鈕) 的可見性 if (modifyButtonPinYinSongs != null) { modifyButtonPinYinSongs.Visible = isVisible; if (isVisible) modifyButtonPinYinSongs.BringToFront(); } + // 設定 clearButtonPinYinSongs (清除按鈕) 的可見性 if (clearButtonPinYinSongs != null) { clearButtonPinYinSongs.Visible = isVisible; if (isVisible) clearButtonPinYinSongs.BringToFront(); } + // 設定 closeButtonPinYinSongs (關閉按鈕) 的可見性 closeButtonPinYinSongs.Visible = isVisible; if (isVisible) closeButtonPinYinSongs.BringToFront(); + // 設定 inputBoxPinYinSongs (輸入框) 的可見性 inputBoxPinYinSongs.Visible = isVisible; if (isVisible) inputBoxPinYinSongs.BringToFront(); + // 恢復佈局,允許 UI 更新 ResumeLayout(); PerformLayout(); - + // 刷新 pictureBoxPinYinSongs,確保畫面更新 pictureBoxPinYinSongs.Refresh(); + + // 刷新拼音字母按鈕 foreach (var button in letterButtonsForPinYinSongs) { button.Refresh(); } - + // 刷新其他按鈕與輸入框 modifyButtonPinYinSongs.Refresh(); clearButtonPinYinSongs.Refresh(); closeButtonPinYinSongs.Refresh(); inputBoxPinYinSongs.Refresh(); }; + // 如果當前執行緒不是 UI 執行緒,則使用 Invoke 確保執行於 UI 執行緒 if (this.InvokeRequired) { this.Invoke(action); @@ -316,6 +480,7 @@ namespace DualScreenDemo { action(); } + } } } \ No newline at end of file diff --git a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.WordCountSearch.cs b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.WordCountSearch.cs index 2f7c6e9..e8d7b13 100644 --- a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.WordCountSearch.cs +++ b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.WordCountSearch.cs @@ -8,182 +8,237 @@ namespace DualScreenDemo { public partial class PrimaryForm { - + /// + /// 當使用者點擊「詞數查歌」按鈕時,切換到詞數查歌模式,更新 UI 並顯示對應的查歌界面。 + /// + /// 觸發事件的按鈕。 + /// 事件參數。 private void WordCountSearchSong_Click(object sender, EventArgs e) - { - zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground; - englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground; - pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground; - wordCountSearchSongButton.BackgroundImage = wordCountSearchSongActiveBackground; - handWritingSearchSongButton.BackgroundImage = handWritingSearchNormalBackground; - numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground; + { + // 設定查歌模式按鈕的背景圖片 + zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground; // 注音查歌(普通狀態) + englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground; // 英文查歌(普通狀態) + pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground; // 拼音查歌(普通狀態) + wordCountSearchSongButton.BackgroundImage = wordCountSearchSongActiveBackground; // 詞數查歌(選中狀態) + handWritingSearchSongButton.BackgroundImage = handWritingSearchNormalBackground; // 手寫查歌(普通狀態) + numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground; // 數字查歌(普通狀態) + // 顯示詞數查歌對應的圖片 ShowImageOnPictureBoxWordCount(Path.Combine(Application.StartupPath, @"themes\superstar\6-1.png")); + // 設定詞數查歌的 PictureBox 和按鈕的可見性 SetPictureBoxWordCountAndButtonsVisibility(true); - pictureBoxWordCount.Visible = true; - } + pictureBoxWordCount.Visible = true; // 顯示 PictureBoxWordCount + } + /// + /// 顯示詞數查歌對應的圖片 + /// + /// 圖片路徑 private void ShowImageOnPictureBoxWordCount(string imagePath) { + // 載入原始圖片 Bitmap originalImage = new Bitmap(imagePath); - + // 定義裁切區域 (起始座標: 593, 135,寬度: 507,高度: 508) Rectangle cropArea = new Rectangle(593, 135, 507, 508); - + // 裁切圖片,獲得指定範圍的影像 Bitmap croppedImage = CropImage(originalImage, cropArea); - + // 將裁切後的圖片顯示在 pictureBoxWordCount 上 pictureBoxWordCount.Image = croppedImage; - - - ResizeAndPositionPictureBox(pictureBoxWordCount, cropArea.X + offsetXWordCount, cropArea.Y + offsetXWordCount, cropArea.Width, cropArea.Height); - + + // 調整 pictureBoxWordCount 的大小與位置 + ResizeAndPositionPictureBox(pictureBoxWordCount, + cropArea.X + offsetXWordCount, // X 座標 (包含位移量) + cropArea.Y + offsetYWordCount, // Y 座標 (包含位移量) + cropArea.Width, // 寬度 + cropArea.Height // 高度 + ); + + // 顯示 pictureBoxWordCount pictureBoxWordCount.Visible = true; + } - private void InitializeButtonsForPictureBoxWordCount() - { - - int[,] coords = new int[,] + /// + /// 初始化「詞數查歌」模式的按鈕與輸入框,並根據螢幕解析度進行調整。 + /// + private void InitializeButtonsForPictureBoxWordCount() { - {651, 292, 752, 400}, - {760, 292, 861, 400}, - {869, 292, 972, 399}, - {652, 401, 752, 502}, - {760, 401, 861, 504}, - {869, 398, 972, 502}, - {651, 502, 753, 607}, - {759, 504, 863, 607}, - {869, 503, 973, 608}, - {981, 501, 1083, 609} - }; - - int screenW = Screen.PrimaryScreen.Bounds.Width; - int screenH = Screen.PrimaryScreen.Bounds.Height; - - - float widthRatio = screenW / (float)1440; - float heightRatio = screenH / (float)900; - - numberButtonsWordCount = new Button[10]; - - for (int i = 0; i < numberButtonsWordCount.Length; i++) - { - numberButtonsWordCount[i] = new Button(); - - - ConfigureButton( - numberButtonsWordCount[i], - coords[i, 0], - coords[i, 1], - coords[i, 2] - coords[i, 0], - coords[i, 3] - coords[i, 1], - - resizedNormalStateImageFor6_1, - resizedMouseOverImageFor6_1, - resizedMouseDownImageFor6_1, - null - ); - - int newXForWordCount = (int)(((numberButtonsWordCount[i].Location.X / widthRatio) + offsetXWordCount) * widthRatio); - int newYForWordCount = (int)(((numberButtonsWordCount[i].Location.Y / heightRatio) + offsetYWordCount) * heightRatio); - numberButtonsWordCount[i].Location = new Point(newXForWordCount, newYForWordCount); - - - numberButtonsWordCount[i].Name = "NumberButtonWordCount" + i; - - numberButtonsWordCount[i].Tag = (i + 1).ToString(); - if (i == 9) + // 定義數字按鈕的座標範圍 (左上角 X, Y 以及右下角 X, Y) + int[,] coords = new int[,] { - numberButtonsWordCount[i].Name = "NumberButtonWordCount0"; - numberButtonsWordCount[i].Tag = "0"; + {651, 292, 752, 400}, // 1 + {760, 292, 861, 400}, // 2 + {869, 292, 972, 399}, // 3 + {652, 401, 752, 502}, // 4 + {760, 401, 861, 504}, // 5 + {869, 398, 972, 502}, // 6 + {651, 502, 753, 607}, // 7 + {759, 504, 863, 607}, // 8 + {869, 503, 973, 608}, // 9 + {981, 501, 1083, 609} // 0 + }; + + // 取得螢幕解析度 + int screenW = Screen.PrimaryScreen.Bounds.Width; + int screenH = Screen.PrimaryScreen.Bounds.Height; + + // 計算寬高比例,供座標縮放使用 + float widthRatio = screenW / (float)1440; + float heightRatio = screenH / (float)900; + + // 建立數字按鈕陣列 + numberButtonsWordCount = new Button[10]; + + // 依序建立 0-9 按鈕 + for (int i = 0; i < numberButtonsWordCount.Length; i++) + { + numberButtonsWordCount[i] = new Button(); + + // 設定按鈕屬性 (位置、大小、圖片) + ConfigureButton( + numberButtonsWordCount[i], + coords[i, 0], + coords[i, 1], + coords[i, 2] - coords[i, 0], // 計算按鈕寬度 + coords[i, 3] - coords[i, 1], // 計算按鈕高度 + resizedNormalStateImageFor6_1, + resizedMouseOverImageFor6_1, + resizedMouseDownImageFor6_1, + null + ); + + // 調整按鈕位置,使其適應不同解析度 + int newXForWordCount = (int)(((numberButtonsWordCount[i].Location.X / widthRatio) + offsetXWordCount) * widthRatio); + int newYForWordCount = (int)(((numberButtonsWordCount[i].Location.Y / heightRatio) + offsetYWordCount) * heightRatio); + numberButtonsWordCount[i].Location = new Point(newXForWordCount, newYForWordCount); + + // 設定按鈕名稱 + numberButtonsWordCount[i].Name = "NumberButtonWordCount" + i; + numberButtonsWordCount[i].Tag = (i + 1).ToString(); + + // 處理按鈕 0 的特殊名稱與標籤 + if (i == 9) + { + numberButtonsWordCount[i].Name = "NumberButtonWordCount0"; + numberButtonsWordCount[i].Tag = "0"; + } + + // 設定點擊事件 + numberButtonsWordCount[i].Click += WordCountButton_Click; + + // 將按鈕加入視窗控制項 + this.Controls.Add(numberButtonsWordCount[i]); } - - numberButtonsWordCount[i].Click += WordCountButton_Click; + // 建立「修改」按鈕 (Modify) + modifyButtonWordCount = new Button { + Name = "ModifyButtonWordCount", + Tag = "Modify", + Visible = false + }; + ConfigureButton(modifyButtonWordCount, 978, 292, 1081 - 978, 397 - 292, + resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, + ModifyButtonWordCount_Click); - - this.Controls.Add(numberButtonsWordCount[i]); + // 調整「修改」按鈕的位置 + int newX = (int)(((modifyButtonWordCount.Location.X / widthRatio) + offsetXWordCount) * widthRatio); + int newY = (int)(((modifyButtonWordCount.Location.Y / heightRatio) + offsetYWordCount) * heightRatio); + modifyButtonWordCount.Location = new Point(newX, newY); + + // 加入「修改」按鈕至視窗 + this.Controls.Add(modifyButtonWordCount); + + // 建立「關閉」按鈕 (Close) + closeButtonWordCount = new Button { + Name = "CloseButtonWordCount", + Tag = "Close", + Visible = false + }; + ConfigureButton(closeButtonWordCount, 982, 147, 1082 - 982, 250 - 147, + resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, + CloseButtonWordCount_Click); + + // 調整「關閉」按鈕的位置 + newX = (int)(((closeButtonWordCount.Location.X / widthRatio) + offsetXWordCount) * widthRatio); + newY = (int)(((closeButtonWordCount.Location.Y / heightRatio) + offsetYWordCount) * heightRatio); + closeButtonWordCount.Location = new Point(newX, newY); + + // 加入「關閉」按鈕至視窗 + this.Controls.Add(closeButtonWordCount); + + // 建立輸入框 (RichTextBox) 供使用者輸入詞數 + inputBoxWordCount = new RichTextBox(); + inputBoxWordCount.Name = "inputBoxWordCount"; + + // 設定輸入框位置與大小 + ResizeAndPositionControl(inputBoxWordCount, 645 + offsetXWordCount, 197 + offsetYWordCount, 986 - 645, 281 - 197); + inputBoxWordCount.ForeColor = Color.Black; + inputBoxWordCount.Font = new Font("細明體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular); + + // 設定輸入框內容變更時的處理邏輯 + inputBoxWordCount.TextChanged += (sender, e) => + { + string searchText = inputBoxWordCount.Text; + int targetLength = 0; + + // 檢查輸入的是否為數字 + if (int.TryParse(searchText, out targetLength)) + { + // 根據詞數長度篩選歌曲 + var searchResults = allSongs.Where(song => song.Song.Replace(" ", "").Length == targetLength).ToList(); + currentPage = 0; + currentSongList = searchResults; + totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); + + // 更新多頁面面板 + multiPagePanel.currentPageIndex = 0; + multiPagePanel.LoadSongs(currentSongList); + } + else + { + // 若輸入非數字,則清空當前歌曲列表 + currentSongList.Clear(); + } + }; + + // 加入輸入框至視窗 + this.Controls.Add(inputBoxWordCount); } - - modifyButtonWordCount = new Button { - Name = "ModifyButtonWordCount", - Tag = "Modify", - Visible = false - }; - - ConfigureButton(modifyButtonWordCount, 978, 292, 1081 - 978, 397 - 292, resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, ModifyButtonWordCount_Click); - int newX = (int)(((modifyButtonWordCount.Location.X / widthRatio) + offsetXWordCount) * widthRatio); - int newY = (int)(((modifyButtonWordCount.Location.Y / widthRatio) + offsetYWordCount) * heightRatio); - modifyButtonWordCount.Location = new Point(newX, newY); - this.Controls.Add(modifyButtonWordCount); - - - closeButtonWordCount = new Button { - Name = "CloseButtonWordCount", - Tag = "Close", - Visible = false - }; - - ConfigureButton(closeButtonWordCount, 982, 147, 1082 - 982, 250 - 147, resizedNormalStateImageFor6_1, resizedMouseOverImageFor6_1, resizedMouseDownImageFor6_1, CloseButtonWordCount_Click); - newX = (int)(((closeButtonWordCount.Location.X / widthRatio) + offsetXWordCount) * widthRatio); - newY = (int)(((closeButtonWordCount.Location.Y / widthRatio) + offsetYWordCount) * heightRatio); - closeButtonWordCount.Location = new Point(newX, newY); - this.Controls.Add(closeButtonWordCount); - - inputBoxWordCount = new RichTextBox(); - inputBoxWordCount.Name = "inputBoxWordCount"; - ResizeAndPositionControl(inputBoxWordCount, 645 + offsetXWordCount, 197 + offsetYWordCount, 986 - 645, 281 - 197); - inputBoxWordCount.ForeColor = Color.Black; - inputBoxWordCount.Font = new Font("細明體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular); - - inputBoxWordCount.TextChanged += (sender, e) => - { - string searchText = inputBoxWordCount.Text; - int targetLength = 0; - - - if (int.TryParse(searchText, out targetLength)) - { - - var searchResults = allSongs.Where(song => song.Song.Replace(" ", "").Length == targetLength).ToList(); - currentPage = 0; - currentSongList = searchResults; - totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); - - multiPagePanel.currentPageIndex = 0; - multiPagePanel.LoadSongs(currentSongList); - } - else - { - - currentSongList.Clear(); - } - }; - - this.Controls.Add(inputBoxWordCount); - } - private void WordCountButton_Click(object sender, EventArgs e) - { - - - var button = sender as Button; - if (button != null && button.Tag != null) + /// + /// 處理數字按鈕的點擊事件,將按鈕對應的數字 (Tag) 加入輸入框 inputBoxWordCount 中。 + /// + /// 觸發事件的按鈕 + /// 事件參數 + private void WordCountButton_Click(object sender, EventArgs e) { - inputBoxWordCount.Text += button.Tag.ToString(); - } - } + // 嘗試將 sender 轉換為 Button 類型 + var button = sender as Button; - private void CloseButtonWordCount_Click(object sender, EventArgs e) - { - - SetPictureBoxWordCountAndButtonsVisibility(false); - } + // 確保按鈕不為空,且擁有有效的 Tag 值 + if (button != null && button.Tag != null) + { + // 將按鈕的 Tag 值 (數字) 追加到輸入框中 + inputBoxWordCount.Text += button.Tag.ToString(); + } + } + + /// + /// 關閉詞數輸入界面,隱藏相關的 PictureBox 和按鈕。 + /// + /// 觸發事件的按鈕。 + /// 事件參數。 + private void CloseButtonWordCount_Click(object sender, EventArgs e) + { + // 設定詞數輸入界面及其按鈕的可見性為 false,使其隱藏 + SetPictureBoxWordCountAndButtonsVisibility(false); + } } } \ No newline at end of file diff --git a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.cs b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.cs index 4ae7d07..0f3daaa 100644 --- a/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.cs +++ b/PrimaryFormParts/SongSearch/PrimaryForm.SongSearch.cs @@ -4,8 +4,13 @@ using System.Windows.Forms; namespace DualScreenDemo { + /* + * 主要視窗 (PrimaryForm) 的部分定義 + * 負責初始化搜尋按鈕並處理歌曲搜尋 UI 相關邏輯 + */ public partial class PrimaryForm { + // 各種歌曲搜尋按鈕及其對應的背景圖片 (一般狀態 / 啟動狀態) private Button zhuyinSearchSongButton; private Bitmap zhuyinSearchSongNormalBackground; private Bitmap zhuyinSearchSongActiveBackground; @@ -25,8 +30,15 @@ namespace DualScreenDemo private Bitmap numberSearchSongNormalBackground; private Bitmap numberSearchSongActiveBackground; + /// + /// 點擊「歌曲搜尋」按鈕時的事件處理函式 + /// 1. 更新導航按鈕的背景圖片,使「歌曲搜尋」按鈕顯示為啟動狀態 + /// 2. 隱藏其他搜尋/分類 UI,僅顯示歌曲搜尋選單 + /// 3. 若有 QR Code 顯示,則將其隱藏 + /// private void SongSearchButton_Click(object sender, EventArgs e) { + // 更新導航按鈕的背景圖片 newSongAlertButton.BackgroundImage = newSongAlertNormalBackground; hotPlayButton.BackgroundImage = hotPlayNormalBackground; singerSearchButton.BackgroundImage = singerSearchNormalBackground; @@ -40,7 +52,7 @@ namespace DualScreenDemo deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground; isOnOrderedSongsPage = false; - + // 隱藏其他 UI SetHotSongButtonsVisibility(false); SetNewSongButtonsVisibility(false); SetSingerSearchButtonsVisibility(false); @@ -50,14 +62,15 @@ namespace DualScreenDemo SetZhuYinSingersAndButtonsVisibility(false); SetZhuYinSongsAndButtonsVisibility(false); SetEnglishSingersAndButtonsVisibility(false); - SetPinYinSingersAndButtonsVisibility(false); SetPinYinSongsAndButtonsVisibility(false); SetPictureBoxToggleLightAndButtonsVisibility(false); SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false); + + // 顯示歌曲搜尋選單按鈕 SetSongSearchButtonsVisibility(true); - + // 隱藏 QR Code (若有) if (pictureBoxQRCode != null) { pictureBoxQRCode.Visible = false; @@ -65,12 +78,25 @@ namespace DualScreenDemo } } + /// + /// 設定「歌曲搜尋」相關按鈕的可見性 + /// + /// 是否顯示 private void SetSongSearchButtonsVisibility(bool isVisible) { - pictureBox4.Visible = isVisible; + pictureBox4.Visible = isVisible; // 控制搜尋 UI 背景的可見性 - Button[] songSearchButtons = { zhuyinSearchSongButton, englishSearchSongButton, wordCountSearchSongButton, pinyinSearchSongButton, handWritingSearchSongButton, numberSearchSongButton }; + // 歌曲搜尋的按鈕陣列 + Button[] songSearchButtons = { + zhuyinSearchSongButton, + englishSearchSongButton, + wordCountSearchSongButton, + pinyinSearchSongButton, + handWritingSearchSongButton, + numberSearchSongButton + }; + // 設定按鈕可見性,若顯示則移至最前 foreach (var button in songSearchButtons) { button.Visible = isVisible; @@ -81,25 +107,41 @@ namespace DualScreenDemo } } + /// + /// 初始化所有「歌曲搜尋」按鈕 + /// 依據不同的搜尋方式 (注音、英文、拼音、筆劃、手寫、數字) 建立對應按鈕 + /// private void InitializeButtonsForSongSearch() { - - InitializeSearchButton(ref zhuyinSearchSongButton, "zhuyinSearchSongButton", 1214, 230, 209, 59, ref zhuyinSearchSongNormalBackground, ref zhuyinSearchSongActiveBackground, normalStateImageSongQuery, mouseDownImageSongQuery, ZhuyinSearchSongsButton_Click); + // 初始化「注音搜尋」按鈕 + InitializeSearchButton(ref zhuyinSearchSongButton, "zhuyinSearchSongButton", + 1214, 230, 209, 59, ref zhuyinSearchSongNormalBackground, ref zhuyinSearchSongActiveBackground, + normalStateImageSongQuery, mouseDownImageSongQuery, ZhuyinSearchSongsButton_Click); - - InitializeSearchButton(ref englishSearchSongButton, "englishSearchSongButton", 1214, 293, 209, 58, ref englishSearchSongNormalBackground, ref englishSearchSongActiveBackground, normalStateImageSongQuery, mouseDownImageSongQuery, EnglishSearchSongsButton_Click); + // 初始化「英文搜尋」按鈕 + InitializeSearchButton(ref englishSearchSongButton, "englishSearchSongButton", + 1214, 293, 209, 58, ref englishSearchSongNormalBackground, ref englishSearchSongActiveBackground, + normalStateImageSongQuery, mouseDownImageSongQuery, EnglishSearchSongsButton_Click); - - InitializeSearchButton(ref pinyinSearchSongButton, "pinyinSearchSongButton", 1214, 356, 209, 58, ref pinyinSearchSongNormalBackground, ref pinyinSearchSongActiveBackground, normalStateImageSongQuery, mouseDownImageSongQuery, PinyinSearchSongsButton_Click); + // 初始化「拼音搜尋」按鈕 + InitializeSearchButton(ref pinyinSearchSongButton, "pinyinSearchSongButton", + 1214, 356, 209, 58, ref pinyinSearchSongNormalBackground, ref pinyinSearchSongActiveBackground, + normalStateImageSongQuery, mouseDownImageSongQuery, PinyinSearchSongsButton_Click); - - InitializeSearchButton(ref wordCountSearchSongButton, "wordCountSearchSongButton", 1214, 418, 209, 59, ref wordCountSearchSongNormalBackground, ref wordCountSearchSongActiveBackground, normalStateImageSongQuery, mouseDownImageSongQuery, WordCountSearchSong_Click); + // 初始化「筆劃搜尋」按鈕 + InitializeSearchButton(ref wordCountSearchSongButton, "wordCountSearchSongButton", + 1214, 418, 209, 59, ref wordCountSearchSongNormalBackground, ref wordCountSearchSongActiveBackground, + normalStateImageSongQuery, mouseDownImageSongQuery, WordCountSearchSong_Click); - - InitializeSearchButton(ref handWritingSearchSongButton, "handWritingSearchSongButton", 1214, 481, 209, 59, ref handWritingSearchSongNormalBackground, ref handWritingSearchSongActiveBackground, normalStateImageSongQuery, mouseDownImageSongQuery, HandWritingSearchButtonForSongs_Click); + // 初始化「手寫搜尋」按鈕 + InitializeSearchButton(ref handWritingSearchSongButton, "handWritingSearchSongButton", + 1214, 481, 209, 59, ref handWritingSearchSongNormalBackground, ref handWritingSearchSongActiveBackground, + normalStateImageSongQuery, mouseDownImageSongQuery, HandWritingSearchButtonForSongs_Click); - - InitializeSearchButton(ref numberSearchSongButton, "numberSearchSongButton", 1214, 544, 209, 58, ref numberSearchSongNormalBackground, ref numberSearchSongActiveBackground, normalStateImageSongQuery, mouseDownImageSongQuery, NumberSearchButton2_Click); + // 初始化「數字搜尋」按鈕 + InitializeSearchButton(ref numberSearchSongButton, "numberSearchSongButton", + 1214, 544, 209, 58, ref numberSearchSongNormalBackground, ref numberSearchSongActiveBackground, + normalStateImageSongQuery, mouseDownImageSongQuery, NumberSearchButton2_Click); } } -} \ No newline at end of file +} diff --git a/config.ini b/config.ini index 7951d4a..bd3421c 100644 --- a/config.ini +++ b/config.ini @@ -7,6 +7,7 @@ ZhuYinSongs = themes\superstar\歌名\注音\VOD_歌名查詢_注音查詢(按 EnglishSongs = themes\superstar\歌名\英文\VOD_歌名查詢_英文查詢(按鍵)_歌星查詢-注音查詢_未按.png PinYinSongs = themes\superstar\歌星\拼音\VOD_歌星查詢_拼音查詢(按鍵)_歌星查詢-注音查詢_未按.png HandWritingSongs = themes\superstar\歌星\手寫\3.歌星查詢(手寫按鍵)_未按.png +WordCountSongs = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)_未按.png [PictureBoxZhuYinSingers] X = 130 @@ -44,6 +45,12 @@ Y = 350 Width = 1079 Height = 394 +[PictureBoxWordCountSongs] +X = 130 +Y = 350 +Width = 1079 +Height = 394 + [PhoneticSymbols] Symbols=ㄅ,ㄉ,ㄍ,ㄐ,ㄓ,ㄗ,ㄛ,ㄡ,ㄤ,ㄧ,ㄆ,ㄊ,ㄎ,ㄑ,ㄔ,ㄘ,ㄜ,ㄢ,ㄦ,ㄨ,ㄇ,ㄋ,ㄏ,ㄒ,ㄕ,ㄙ,ㄞ,ㄣ,ㄩ,ㄈ,ㄌ, ,ㄖ,ㄚ,ㄠ @@ -595,4 +602,75 @@ Height = 63 FontName = Times New Roman FontSize = 26 FontStyle = Regular -ForeColor = Black \ No newline at end of file +ForeColor = Black + +[InputBoxWordCountSongs] +X=150 +Y=264 +Width=596 +Height=63 +FontName=微軟正黑體 +FontSize=26 +FontStyle=Bold +ForeColor=Black + + +[NumberButtonCoordinatesWithWordCount] +button1 = 157,201,27,32 +button2 = 187,201,27,32 +button3 = 217,201,27,32 +button4 = 247,201,27,32 +button5 = 277,200,27,32 +button6 = 307,201,27,32 +button7 = 337,201,27,32 +button8 = 367,201,27,32 +button9 = 397,201,27,32 +button10 = 427,201,27,32 + + +[NumberButtonImagesWithWordCount] +button0_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-02.png +button0_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-17.png +button0_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-02.png +button1_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-03.png +button1_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-18.png +button1_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-03.png +button2_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-04.png +button2_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-19.png +button2_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-04.png +button3_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-05.png +button3_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-20.png +button3_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-05.png +button4_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-06.png +button4_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-21.png +button4_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-06.png +button5_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-07.png +button5_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-22.png +button5_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-07.png +button6_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-08.png +button6_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-23.png +button6_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-08.png +button7_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-09.png +button7_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-24.png +button7_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-09.png +button8_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-10.png +button8_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-25.png +button8_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-10.png +button9_normal = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-11.png +button9_mouseDown = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-26.png +button9_mouseOver = themes\superstar\歌名\編號\VOD_歌名查詢_編號查詢(按鍵)-11.png + +[ModifyButtonImagesWordCount] +normal = themes\superstar\歌星\拼音\VOD_歌名查詢_編號查詢(按鍵)-13.png +mouseOver = themes\superstar\歌星\拼音\VOD_歌名查詢_編號查詢(按鍵)-13.png +mouseDown = themes\superstar\歌星\拼音\VOD_歌名查詢_編號查詢(按鍵)-28.png + +[ClearButtonImagesWordCount] +normal = themes\superstar\歌名\拼音\VOD_歌名查詢_編號查詢(按鍵)-14.png +mouseOver = themes\superstar\歌名\拼音\VOD_歌名查詢_編號查詢(按鍵)-14.png +mouseDown = themes\superstar\歌名\拼音\VOD_歌名查詢_編號查詢(按鍵)-29.png + +[CloseButtonImagesWordCount] +normal = themes\superstar\歌名\拼音\VOD_歌名查詢_編號查詢(按鍵)-15.png +mouseOver = themes\superstar\歌名\拼音\VOD_歌名查詢_編號查詢(按鍵)-15.png +mouseDown = themes\superstar\歌名\拼音\VOD_歌名查詢_編號查詢(按鍵)-30.png \ No newline at end of file