using System.IO; using System.Timers; using DBObj; using DualScreenDemo; namespace OverlayFormObj { public partial class OverlayForm : Form { private string marqueeText = "這是跑馬燈文字示例 - 歡迎使用MediaPlayerForm!"; private Color marqueeTextColor = Color.White; private string marqueeTextSecondLine = ""; private string marqueeTextThirdLine = ""; private int marqueeXPos; private int marqueeXPosSecondLine; private int marqueeXPosThirdLine; private System.Windows.Forms.Timer marqueeTimer; private Image backgroundImage; private Image firstStickerImage; private Image secondStickerImage; private float firstStickerXPos; private float secondStickerXPos; private float imageYPos; private int screenHeight; private int topMargin; private int bottomMargin; public static System.Windows.Forms.Timer displayTimer = new System.Windows.Forms.Timer(); public static System.Timers.Timer songDisplayTimer = new System.Timers.Timer(); public static System.Timers.Timer unifiedTimer; private System.Windows.Forms.Timer stickerTimer1 = new System.Windows.Forms.Timer(); private System.Windows.Forms.Timer stickerTimer2 = new System.Windows.Forms.Timer(); private System.Windows.Forms.Timer keyUpTimer = new System.Windows.Forms.Timer(); private System.Windows.Forms.Timer keyDownTimer = new System.Windows.Forms.Timer(); public Label standardKeyLabel; private System.Windows.Forms.Timer standardKeyTimer = new System.Windows.Forms.Timer(); private System.Windows.Forms.Timer messageTimer = new System.Windows.Forms.Timer(); public Label maleKeyLabel; public Label messageLabel; private System.Windows.Forms.Timer maleKeyTimer; public Label femaleKeyLabel; private System.Windows.Forms.Timer femaleKeyTimer; private System.Windows.Forms.Timer secondLineTimer; private DateTime secondLineStartTime; private const int secondLineDuration = 20000; private Image qrCodeImage; private bool showQRCode; private System.Windows.Forms.Timer segmentSwitchTimer = new System.Windows.Forms.Timer(); private int currentSegmentIndex = 0; private List textSegments = new List(); public enum MarqueeStartPosition { Middle, Right } private static OverlayForm _mainForm; public static OverlayForm MainForm { get { return _mainForm; } private set { _mainForm = value; } } public static OverlayForm Instance { get; private set; } public OverlayForm() { SetStyle(ControlStyles.SupportsTransparentBackColor, true); MainForm = this; Instance = this; InitializeFormSettings(); ConfigureTimers(); InitializeLabels(); ConfigureSegmentTimer(); imageYPos = (screenHeight / 3) - 1024 / 6; } private void ConfigureSegmentTimer() { segmentSwitchTimer.Interval = 3000; segmentSwitchTimer.Tick += (sender, e) => { if (textSegments.Count > 1) { currentSegmentIndex = (currentSegmentIndex + 1) % textSegments.Count; blackBackgroundPanel.Invalidate(); } }; } private void SplitSecondLineText(string text) { textSegments.Clear(); if (string.IsNullOrEmpty(text)) { return; } if (text.Length <= 16) { textSegments.Add(text); } else { for (int i = 0; i < text.Length; i += 16) { int length = Math.Min(16, text.Length - i); textSegments.Add(text.Substring(i, length)); } } currentSegmentIndex = 0; } private void InitializeMaleKeyTimer() { maleKeyTimer = new System.Windows.Forms.Timer(); maleKeyTimer.Interval = 3000; maleKeyTimer.Tick += (s, e) => { HideAllLabels(); }; } private void InitializeFemaleKeyTimer() { femaleKeyTimer = new System.Windows.Forms.Timer(); femaleKeyTimer.Interval = 3000; femaleKeyTimer.Tick += (s, e) => { HideAllLabels(); }; } private void AddCenteredPicture(Bitmap bitmap, int y) { int x = (this.Width - bitmap.Width) / 2; AddPicture(bitmap, x, y); } private void AddPicture(Bitmap bitmap, int x, int y) { PictureBox pictureBox = new PictureBox { Image = bitmap, Size = bitmap.Size, BackColor = Color.Transparent, Location = new Point(x, y) }; this.Controls.Add(pictureBox); pictureBox.BringToFront(); } private Bitmap GenerateTextImage(string text, Font font, Color textColor, Color backgroundColor) { SizeF textSize; using (Bitmap tempBitmap = new Bitmap(1, 1)) using (Graphics tempGraphics = Graphics.FromImage(tempBitmap)) { textSize = tempGraphics.MeasureString(text, font); } // 創建一個稍大的位圖尺寸,以容納描邊 int padding = 4; // 描邊寬度 Bitmap bitmap = new Bitmap((int)textSize.Width + padding * 2, (int)textSize.Height + padding * 2); using (Graphics graphics = Graphics.FromImage(bitmap)) { graphics.Clear(backgroundColor); graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 先繪製黑色描邊 using (Brush outlineBrush = new SolidBrush(Color.Black)) { for (int x = -2; x <= 2; x++) { for (int y = -2; y <= 2; y++) { if (x != 0 || y != 0) { graphics.DrawString(text, font, outlineBrush, new PointF(padding + x, padding + y)); } } } } // 繪製中心文字 using (Brush textBrush = new SolidBrush(textColor)) { graphics.DrawString(text, font, textBrush, new PointF(padding, padding)); } } // 修剪圖片邊緣空白 return TrimBitmap(bitmap); } private Bitmap TrimBitmap(Bitmap source) { Rectangle rect = FindContentBounds(source); if (rect.Width == 0 || rect.Height == 0) return source; // 防止圖片完全空白時崩潰 Bitmap trimmedBitmap = new Bitmap(rect.Width, rect.Height); using (Graphics g = Graphics.FromImage(trimmedBitmap)) { g.DrawImage(source, 0, 0, rect, GraphicsUnit.Pixel); } return trimmedBitmap; } private Rectangle FindContentBounds(Bitmap bmp) { int width = bmp.Width; int height = bmp.Height; int top = 0, left = 0, bottom = height - 1, right = width - 1; bool found = false; // 找到頂邊界 for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (bmp.GetPixel(x, y).A > 0) { top = y; found = true; break; } } if (found) break; } found = false; // 找到底邊界 for (int y = height - 1; y >= 0; y--) { for (int x = 0; x < width; x++) { if (bmp.GetPixel(x, y).A > 0) { bottom = y; found = true; break; } } if (found) break; } found = false; // 找到左邊界 for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (bmp.GetPixel(x, y).A > 0) { left = x; found = true; break; } } if (found) break; } found = false; // 找到右邊界 for (int x = width - 1; x >= 0; x--) { for (int y = 0; y < height; y++) { if (bmp.GetPixel(x, y).A > 0) { right = x; found = true; break; } } if (found) break; } return Rectangle.FromLTRB(left, top, right + 1, bottom + 1); } public static void DisplayNumberAtTopLeft(string newText) { if (MainForm != null) { MainForm.Invoke(new System.Action(() => { string currentText = MainForm.displayLabel.Text; MainForm.nextSongLabel.Visible = false; string combinedText = currentText + newText; if (combinedText.Length > 6) { combinedText = combinedText.Substring(0, 6); } MainForm.displayLabel.Text = combinedText; MainForm.nextSongLabel.Visible = false; displayTimer.Stop(); displayTimer.Start(); })); } } private void InitializeFormSettings() { this.FormBorderStyle = FormBorderStyle.None; this.TopMost = true; this.BackColor = Color.Magenta; this.TransparencyKey = this.BackColor; this.Height = 50; if (Screen.AllScreens.Length > 1) { var secondaryScreen = Screen.AllScreens[1]; this.Width = secondaryScreen.Bounds.Width; this.Location = new Point(secondaryScreen.Bounds.Location.X, this.Location.Y); screenHeight = secondaryScreen.Bounds.Height; topMargin = screenHeight / 3; bottomMargin = screenHeight * 2 / 3; } else { this.Width = Screen.PrimaryScreen.Bounds.Width; this.screenHeight = Screen.PrimaryScreen.Bounds.Height; } marqueeXPos = this.Width; marqueeXPosSecondLine = 7 * this.Width / 8; marqueeXPosThirdLine = this.Width; marqueeTimer = new System.Windows.Forms.Timer(); marqueeTimer.Interval = 20; marqueeTimer.Tick += MarqueeTimer_Tick; marqueeTimer.Start(); secondLineTimer = new System.Windows.Forms.Timer(); secondLineTimer.Interval = 100; secondLineTimer.Tick += SecondLineTimer_Tick; try { string filePath = Path.Combine(Application.StartupPath,"txt","WelcomeMessage.txt"); marqueeText = File.ReadAllText(filePath); } catch (Exception ex) { Console.WriteLine("Error reading marquee text from file: " + ex.Message); marqueeText = "這是跑馬燈文字示例 - 歡迎使用MediaPlayerForm!"; } this.DoubleBuffered = true; } private void ConfigureTimers() { displayTimer.Interval = 15000; displayTimer.Tick += DisplayTimer_Tick; songDisplayTimer = new System.Timers.Timer(30000); songDisplayTimer.Elapsed += new ElapsedEventHandler(SongDisplayTimer_Elapsed); songDisplayTimer.AutoReset = true; songDisplayTimer.Enabled = true; unifiedTimer = new System.Timers.Timer(15000); unifiedTimer.Elapsed += new ElapsedEventHandler(UnifiedTimer_Elapsed); unifiedTimer.AutoReset = true; unifiedTimer.Enabled = true; stickerTimer1.Interval = 15000; stickerTimer1.Tick += (sender, e) => { lock (imageLock) { firstStickerImage = null; this.Invalidate(); } stickerTimer1.Stop(); HideImages(); }; stickerTimer2.Interval = 15000; stickerTimer2.Tick += (sender, e) => { lock (imageLock) { secondStickerImage = null; this.Invalidate(); } stickerTimer2.Stop(); HideImages(); }; } private static void DisplayTimer_Tick(object sender, EventArgs e) { if (MainForm.InvokeRequired) { MainForm.Invoke(new System.Action(() => { MainForm.displayLabel.Text = ""; MainForm.nextSongLabel.Visible = true; // 显示 nextSongLabel })); } else { MainForm.displayLabel.Text = ""; MainForm.nextSongLabel.Visible = true; // 显示 nextSongLabel } displayTimer.Stop(); // 停止计时器 } // 當 songDisplayTimer 計時完成時會呼叫此函式 private static void SongDisplayTimer_Elapsed(object sender, EventArgs e) { // 檢查是否需要跨執行緒操作 UI 控制項 if (MainForm.InvokeRequired) { // 如果目前不在 UI 執行緒上,必須透過 Invoke 安全執行 UI 更新邏輯 Console.WriteLine("SongDisplayTimer_Tick invoked on UI thread."); MainForm.BeginInvoke(new Action(() => { if (MainForm.songDisplayLabel != null) MainForm.songDisplayLabel.Text = ""; if (MainForm.nextSongLabel != null) MainForm.nextSongLabel.Visible = true; })); } else { // 如果已經在 UI 執行緒,就直接更新 UI 控制項 Console.WriteLine("SongDisplayTimer_Tick invoked on background thread."); MainForm.songDisplayLabel.Text = ""; MainForm.nextSongLabel.Visible = true; } // 停止計時器,避免重複觸發 songDisplayTimer.Stop(); } private readonly object _lockObject = new object(); private bool _handlingTimeout = false; private async void UnifiedTimer_Elapsed(object sender, EventArgs e) { if (_handlingTimeout) return; _handlingTimeout = true; try { if (MainForm.InvokeRequired) { MainForm.BeginInvoke((Action)(() => UnifiedTimer_Elapsed(sender, e))); return; } displayLabel.Text = ""; switch (CurrentUIState) { case UIState.SelectingLanguage: case UIState.SelectingArtistCategory: case UIState.SelectingAction: case UIState.SelectingSong: case UIState.SelectingArtist: case UIState.PlayHistory: SetUIState(UIState.Initial); await HandleTimeout(""); break; } } finally { _handlingTimeout = false; } } private async Task HandleTimeout(string message) { Console.WriteLine("HandleTimeout called with message: " + message); unifiedTimer.Stop(); SetUIState(UIState.Initial); //DisplayMessage(message, 2000); CommandHandler._indataHistory.Clear(); // 清空歷史紀錄 await Task.Delay(2000); } private void DisplayMessage(string message, int duration) { MainForm.nextSongLabel.Visible = false; displayLabel.Text = message; unifiedTimer.Interval = duration; unifiedTimer.Start(); } private void HideImages() { bool anyStickersActive = false; lock (imageLock) { if (firstStickerImage != null) { firstStickerImage = null; stickerTimer1.Stop(); anyStickersActive = true; } if (secondStickerImage != null) { secondStickerImage = null; stickerTimer2.Stop(); anyStickersActive = true; } if (!anyStickersActive) { backgroundImage = null; } this.Invalidate(); } } public void UpdateSongDisplayLabel(string newText) { songDisplayLabel.Text = newText; songDisplayLabel.Font = new Font("Arial", 125); songDisplayLabel.ForeColor = Color.White; songDisplayTimer.Stop(); songDisplayTimer.Start(); this.Invalidate(); } private void SecondLineTimer_Tick(object sender, EventArgs e) { if ((DateTime.Now - secondLineStartTime).TotalMilliseconds >= secondLineDuration) { marqueeTextSecondLine = ""; secondLineTimer.Stop(); } } public void ResetMarqueeTextToWelcomeMessage() { try { string filePath = Path.Combine(Application.StartupPath,"txt","WelcomeMessage.txt"); string welcomeMessage = File.ReadAllText(filePath); this.UpdateMarqueeText(welcomeMessage, MarqueeStartPosition.Right, Color.White); } catch (Exception ex) { Console.WriteLine("Error reading marquee text from file: " + ex.Message); } } protected override void OnPaint(PaintEventArgs e) { blackBackgroundPanel.SendToBack(); base.OnPaint(e); using (Font largeFont = new Font("微軟正黑體", 34, FontStyle.Bold)) using (Brush whiteBrush = new SolidBrush(Color.White)) using (Brush LimeGreen = new SolidBrush(Color.LimeGreen)) using (Brush marqueeBrush = new SolidBrush(marqueeTextColor)) using (Brush backgroundBrush = new SolidBrush(Color.FromArgb(255, 0, 0, 0))) { SizeF textSize = e.Graphics.MeasureString(marqueeText, largeFont); float yPosition1 = 10; float yPosition2 = 55; float yPosition3 = 100; e.Graphics.FillRectangle(backgroundBrush, marqueeXPos, yPosition1, textSize.Width, textSize.Height); e.Graphics.DrawString(marqueeText, largeFont, marqueeBrush, new PointF(marqueeXPos, yPosition1)); Rectangle clipRect = new Rectangle((int)(this.Width / 8), (int)yPosition2, (int)(3 * this.Width / 4), (int)textSize.Height); Region originalClip = e.Graphics.Clip; e.Graphics.SetClip(clipRect); SizeF textSizeSecondLine = e.Graphics.MeasureString(marqueeTextSecondLine, largeFont); float centeredXPos = (this.Width - textSizeSecondLine.Width) / 2; e.Graphics.FillRectangle(backgroundBrush, centeredXPos, yPosition2, textSizeSecondLine.Width, textSizeSecondLine.Height); e.Graphics.DrawString(marqueeTextSecondLine, largeFont, LimeGreen, new PointF(centeredXPos, yPosition2)); e.Graphics.Clip = originalClip; if (string.IsNullOrEmpty(marqueeTextSecondLine)) { SizeF textSizeThirdLine = e.Graphics.MeasureString(marqueeTextThirdLine, largeFont); e.Graphics.FillRectangle(backgroundBrush, marqueeXPosThirdLine, yPosition2, textSizeThirdLine.Width, textSizeThirdLine.Height); e.Graphics.DrawString(marqueeTextThirdLine, largeFont, whiteBrush, new PointF(marqueeXPosThirdLine, yPosition2)); } else { SizeF textSizeThirdLine = e.Graphics.MeasureString(marqueeTextThirdLine, largeFont); e.Graphics.FillRectangle(backgroundBrush, marqueeXPosThirdLine, yPosition3, textSizeThirdLine.Width, textSizeThirdLine.Height); e.Graphics.DrawString(marqueeTextThirdLine, largeFont, whiteBrush, new PointF(marqueeXPosThirdLine, yPosition3)); } } lock (imageLock) { if (backgroundImage != null) { e.Graphics.DrawImage(backgroundImage, new Rectangle(25, 100, this.Width - 50, (int)(this.Height * 2 / 3) - 100)); } if (firstStickerImage != null) { e.Graphics.DrawImage(firstStickerImage, firstStickerXPos, imageYPos); } if (secondStickerImage != null) { e.Graphics.DrawImage(secondStickerImage, secondStickerXPos, imageYPos); } if (showQRCode && qrCodeImage != null) { int qrCodeSize = screenHeight / 6; int qrCodeX = 10; int qrCodeY = this.Height - qrCodeSize - 20; Rectangle qrCodeRect = new Rectangle(qrCodeX, qrCodeY, qrCodeSize, qrCodeSize); e.Graphics.DrawImage(qrCodeImage, qrCodeRect); } } // 確保跑馬燈第二行在最前 // blackBackgroundPanel.SendToBack(); } public void DisplayBarrage(string text) { if (this.InvokeRequired) { this.Invoke(new System.Action(() => DisplayBarrage(text))); return; } Random rnd = new Random(); for (int i = 0; i < 30; i++) { Label lblBarrage = new Label { Text = text, AutoSize = true, ForeColor = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256)), Font = new Font("Arial", rnd.Next(10, 50)), Location = new Point(rnd.Next(0, this.Width), rnd.Next(0, this.Height)) }; this.Controls.Add(lblBarrage); lblBarrage.BringToFront(); System.Windows.Forms.Timer moveTimer = new System.Windows.Forms.Timer { Interval = 50 }; moveTimer.Tick += (sender, e) => { lblBarrage.Left -= 5; if (lblBarrage.Right < 0) { lblBarrage.Dispose(); moveTimer.Dispose(); } }; moveTimer.Start(); int duration = rnd.Next(3000, 7000); System.Windows.Forms.Timer durationTimer = new System.Windows.Forms.Timer { Interval = duration }; durationTimer.Tick += (sender, e) => { if (moveTimer.Enabled) { moveTimer.Stop(); moveTimer.Dispose(); } this.Controls.Remove(lblBarrage); lblBarrage.Dispose(); durationTimer.Stop(); durationTimer.Dispose(); }; durationTimer.Start(); } } public void DisplaySticker(string stickerId) { //Console.WriteLine("Attempting to display sticker."); this.Invoke((MethodInvoker)delegate { Console.WriteLine("Form Width: " + this.Width); Console.WriteLine("Form Height: " + this.Height); string imagePath = String.Format("{0}\\themes\\superstar\\superstar-pic\\{1}.png", Application.StartupPath, stickerId); Console.WriteLine("Image path: " + imagePath); try { Image newSticker = Image.FromFile(imagePath); lock (imageLock) { if (firstStickerImage == null) { firstStickerImage = newSticker; firstStickerXPos = this.Width / 2 - firstStickerImage.Width / 2; stickerTimer1.Start(); } else if (secondStickerImage == null) { firstStickerXPos = this.Width * 3 / 10f - firstStickerImage.Width / 8; secondStickerImage = newSticker; secondStickerXPos = this.Width * 7 / 10f - secondStickerImage.Width / 8; backgroundImage = null; stickerTimer2.Start(); } else { firstStickerImage = secondStickerImage; firstStickerXPos = this.Width * 3 / 10f - firstStickerImage.Width / 8; secondStickerImage = newSticker; secondStickerXPos = this.Width * 7 / 10f - secondStickerImage.Width / 8; stickerTimer2.Start(); } } this.Invalidate(); } catch (Exception ex) { Console.WriteLine("Error loading sticker image: " + ex.Message); } }); } public static string ReadSongNumber() { string songNumber = MainForm.displayLabel.Text; MainForm.nextSongLabel.Visible = false; return songNumber; } private List LanguageSongList; public enum UIState { Initial, SelectingLanguage, SelectingSong, SelectingAction, PlayingSong, SelectingArtistCategory, SelectingStrokeCount, SelectingArtist, PlayHistory } public enum Category { NewSongs, HotSongs, Artists } public static UIState CurrentUIState = UIState.Initial; private string currentLanguage = ""; public static Category CurrentCategory { get; set; } private SongData selectedSong; public static void SetUIState(UIState newState) { CurrentUIState = newState; displayTimer.Stop(); switch (newState) { case UIState.Initial: foreach (var control in MainForm.Controls.OfType().ToArray()) { if (control != MainForm.displayLabel && control != MainForm.pauseLabel && control != MainForm.muteLabel && control != MainForm.volumeUpLabel && control != MainForm.volumeDownLabel && control != MainForm.micUpLabel && control != MainForm.micDownLabel && control != MainForm.standardKeyLabel && control != MainForm.keyUpLabel && control != MainForm.keyDownLabel && control != MainForm.maleKeyLabel && control != MainForm.femaleKeyLabel && control != MainForm.squareLabel && control != MainForm.professionalLabel && control != MainForm.standardLabel && control != MainForm.singDownLabel && control != MainForm.brightLabel && control != MainForm.softLabel && control != MainForm.autoLabel && control != MainForm.romanticLabel && control != MainForm.dynamicLabel && control != MainForm.tintLabel && control != MainForm.blackBackgroundPanel && control != MainForm.nextSongLabel) { MainForm.Controls.Remove(control); // control.Dispose(); control.Visible = false; } } MainForm.displayLabel.Text = ""; CommandHandler.readyForSongListInput = false; unifiedTimer.Stop(); break; case UIState.SelectingLanguage: unifiedTimer.Interval = 10000; unifiedTimer.Enabled = true; unifiedTimer.Start(); break; case UIState.SelectingSong: unifiedTimer.Interval = 10000; unifiedTimer.Enabled = true; unifiedTimer.Start(); break; case UIState.SelectingArtistCategory: unifiedTimer.Interval = 10000; unifiedTimer.Enabled = true; unifiedTimer.Start(); break; case UIState.SelectingStrokeCount: unifiedTimer.Interval = 10000; unifiedTimer.Enabled = true; unifiedTimer.Start(); break; case UIState.SelectingArtist: unifiedTimer.Interval = 10000; unifiedTimer.Enabled = true; unifiedTimer.Start(); break; case UIState.PlayHistory: unifiedTimer.Interval = 10000; unifiedTimer.Enabled = true; unifiedTimer.Start(); break; default: break; } } int number; int inputNumber; public void OnUserInput(string input) { bool isNumber = int.TryParse(input, out number); if (isNumber) { if (CurrentCategory == Category.NewSongs) { switch (CurrentUIState) { case UIState.SelectingLanguage: HandleLanguageSelection(number); break; case UIState.SelectingSong: HandleSongSelection(number); break; default: displayLabel.Text = "無效的狀態"; displayLabel.BringToFront(); break; } } else if (CurrentCategory == Category.HotSongs) { switch (CurrentUIState) { case UIState.SelectingLanguage: HandleLanguageSelection(number); break; case UIState.SelectingSong: HandleSongSelection(number); break; default: displayLabel.Text = "無效的狀態"; displayLabel.BringToFront(); break; } } else if (CurrentCategory == Category.Artists) { switch (CurrentUIState) { case UIState.SelectingArtistCategory: ProcessArtistCategorySelection(number); break; case UIState.SelectingStrokeCount: ProcessStrokeCountSelection(number); break; case UIState.SelectingArtist: HandleArtistSelection(number); break; case UIState.SelectingSong: HandleSongSelection(number); break; default: displayLabel.Text = "無效的狀態"; displayLabel.BringToFront(); break; } } } else if (input == "a") { try { if (CurrentUIState == UIState.SelectingSong) { Console.WriteLine("Current State is SelectingSong, ready to process song selection."); Console.WriteLine("Number: " + inputNumber); int songIndex = (currentPage - 1) * songsPerPage + (inputNumber - 1); Console.WriteLine("Calculated Song Index: " + songIndex + ", Total Songs: " + totalSongs); if (songIndex >= 0 && songIndex < totalSongs) { selectedSong = LanguageSongList[songIndex]; Console.WriteLine("Adding song to playlist: " + LanguageSongList[songIndex].Song + " " + selectedSong.FileName); // DisplayActionWithSong(currentPage, songIndex, "點播"); AddSongToPlaylist(selectedSong); } else { Console.WriteLine("Song index out of range."); } } } catch (Exception ex) { Console.WriteLine("An error occurred while processing input 'a': " + ex.Message); Console.WriteLine(ex.StackTrace); } } else if (input == "b") { if (CurrentUIState == UIState.SelectingSong) { Console.WriteLine("Current State is SelectingSong, ready to process song selection."); int songIndex = (currentPage - 1) * songsPerPage + (inputNumber - 1); Console.WriteLine("Calculated Song Index: " + songIndex + ", Total Songs: " + totalSongs); if (songIndex < totalSongs) { selectedSong = LanguageSongList[songIndex]; Console.WriteLine("Adding song to playlist: " + LanguageSongList[songIndex].Song + " " + selectedSong.FileName ); // DisplayActionWithSong(currentPage, songIndex, "插播"); InsertSongToPlaylist(selectedSong); } else { Console.WriteLine("Song index out of range."); } } } } private void HandleLanguageSelection(int number) { switch (number) { case 1: currentLanguage = "國語"; break; case 2: currentLanguage = "台語"; break; case 3: currentLanguage = "粵語"; break; case 4: currentLanguage = "英語"; break; case 5: currentLanguage = "日語"; break; case 6: currentLanguage = "韓語"; break; default: displayLabel.Text = "輸入錯誤!!!"; displayLabel.BringToFront(); OverlayForm.displayTimer.Start(); return; } Console.WriteLine("Language selected: " + currentLanguage); DisplaySongsInLanguage(currentLanguage, CurrentCategory); CurrentUIState = UIState.SelectingSong; Console.WriteLine("State changed to SelectingSong"); } private Artist selectedArtist; private void HandleArtistSelection(int number) { int artistIndex = (currentPage - 1) * artistsPerPage + (number - 1); inputNumber = number; if (artistIndex < totalArtists) { selectedArtist = currentArtistList[artistIndex]; currentLanguage = selectedArtist.Name; SetUIState(UIState.SelectingSong); string query = $"SELECT * FROM song_library_cache WHERE artistA ='{selectedArtist.Name}' OR artistB='{selectedArtist.Name}' ORDER BY song_counts DESC;"; LanguageSongList = PrimaryForm.Instance.SearchSongs_Mysql(query); //LanguageSongList = SongListManager.Instance.GetSongsByArtist(selectedArtist.Name); currentPage = 1; totalSongs = LanguageSongList.Count; DisplaySongs(currentPage); } else { Console.WriteLine("Song index out of range."); } } private void HandleSongSelection(int number) { Console.WriteLine("Current State is SelectingSong, ready to process song selection."); int songIndex = (currentPage - 1) * songsPerPage + (number - 1); inputNumber = number; Console.WriteLine("Calculated Song Index: " + songIndex + ", Total Songs: " + totalSongs); if (songIndex < totalSongs) { selectedSong = LanguageSongList[songIndex]; Console.WriteLine("Adding song to playlist C: " + LanguageSongList[songIndex].Song + " " + selectedSong.FileName); DisplaySongsWithArrows(currentPage, songIndex); //AddSongToPlaylist(selectedSong); } else { Console.WriteLine("Song index out of range."); } } private string currentArtistCategory; private void ProcessArtistCategorySelection(int number) { switch (number) { case 1: currentArtistCategory = "男"; break; case 2: currentArtistCategory = "女"; break; case 3: currentArtistCategory = "團"; break; case 4: currentArtistCategory = "外"; break; case 5: currentArtistCategory = "全部"; break; default: Console.WriteLine("Invalid selection"); return; } ClearDisplay(); DisplayStrokeCountOptions(); } private void ClearDisplay() { foreach (var control in this.Controls.OfType().ToArray()) { if (control != displayLabel && control != pauseLabel && control != muteLabel && control != volumeUpLabel && control != volumeDownLabel && control != micUpLabel && control != micDownLabel && control != standardKeyLabel && control != keyUpLabel && control != keyDownLabel && control != maleKeyLabel && control != femaleKeyLabel && control != squareLabel && control != professionalLabel && control != standardLabel && control != singDownLabel && control != brightLabel && control != softLabel && control != autoLabel && control != romanticLabel && control != dynamicLabel && control != tintLabel && control != blackBackgroundPanel && control != nextSongLabel) { this.Controls.Remove(control); control.Dispose(); } } } public void UpdateHistoryLabel(List historySongs, List playStates, int currentPage, int totalPages) { // 移除畫面上所有現有的 PictureBox(用於刷新內容) this.Controls.OfType().ToList().ForEach(p => this.Controls.Remove(p)); // 建立標題文字,例如 "已點歌曲 (1 / 3)" string headerText = $"已點歌曲 ({currentPage} / {totalPages})"; Font headerFont = new Font("Microsoft JhengHei", 50, FontStyle.Bold); // 將標題文字轉為圖片 Bitmap headerBitmap = GenerateTextImage(headerText, headerFont, Color.White, Color.Transparent); // 將標題圖片置中顯示在 Y=150 的位置 AddCenteredPicture(headerBitmap, 150); // 設定歌名顯示的起始 Y 座標 int startY = 230; // 歌名之間的垂直間距 int verticalSpacing = 6; // 左右邊界,用來對齊歌名與狀態文字的位置 int leftMargin = 100; int rightMargin = this.Width - 100; // 逐一繪製歷史歌曲列表 for (int i = 0; i < historySongs.Count; i++) { var song = historySongs[i]; // 歌曲資料 var state = playStates[i]; // 對應的播放狀態 string status; // 播放狀態文字(如「播放中」) Color textColor; // 對應的顏色 // 根據播放狀態決定狀態文字與顏色 switch (state) { case PlayState.Played: status = "(播畢)"; textColor = Color.FromArgb(200, 75, 125); // 播畢顏色:紫紅色 break; case PlayState.Playing: status = "(播放中)"; textColor = Color.LimeGreen; // 播放中顏色:亮綠 break; case PlayState.NotPlayed: status = ""; textColor = Color.White; // 尚未播放:白色 break; default: status = ""; textColor = Color.White; break; } // 建立顯示的歌曲文字,例如 "1. 我的歌" string songText = $"{i + 1}. {song.Song}"; Font songFont = new Font("Microsoft JhengHei", 50, FontStyle.Bold); // 將歌名轉成圖片 Bitmap songBitmap = GenerateTextImage(songText, songFont, textColor, Color.Transparent); Bitmap statusBitmap = null; // 如果有狀態文字,則也轉成圖片 if (!string.IsNullOrEmpty(status)) { Font statusFont = new Font("Microsoft JhengHei", 50, FontStyle.Bold); statusBitmap = GenerateTextImage(status, statusFont, textColor, Color.Transparent); } // 計算目前這首歌的 Y 座標位置 int fixedRowHeight = 80; int y = startY + i * (fixedRowHeight + verticalSpacing); //int y = startY + i * (songBitmap.Height + verticalSpacing); // 加入歌名圖片到畫面左側 AddPicture(songBitmap, leftMargin, y); // 若有狀態圖片,則加入到畫面右側 if (statusBitmap != null) { int statusX = rightMargin - statusBitmap.Width; AddPicture(statusBitmap, statusX, y); } } } public void UpdateDisplayLabels(string[] messages)//新歌歌星排行首頁 { // 移除畫面上現有的所有 PictureBox(舊的文字圖片) this.Controls.OfType().ToList().ForEach(p => this.Controls.Remove(p)); // 如果沒有訊息內容,直接跳出,不進行後續繪製 if (messages.Length == 0) return; // 設定主標題字體大小 int mainTitleFontSize = 60; // 設定選項字體大小 int optionFontSize = 50; // 每行之間的垂直間距(像素) int lineSpacing = 15; // 取得主標題文字(第一行) string mainTitle = messages[0]; // 建立主標題用的字型:微軟正黑體、粗體 Font mainTitleFont = new Font("Microsoft JhengHei", mainTitleFontSize, FontStyle.Bold); // 使用自定義方法產生主標題的文字圖像(白色字+透明底) Bitmap mainTitleBitmap = GenerateTextImage(mainTitle, mainTitleFont, Color.White, Color.Transparent); // 主標題起始的 Y 座標 int startY = 130; // 將主標題圖片水平置中並加入畫面 AddCenteredPicture(mainTitleBitmap, startY); // 更新 Y 座標位置,準備繪製下一區塊(選項),加上主標題高度與間距 startY += mainTitleBitmap.Height + lineSpacing; // 取得剩下的訊息文字(第二行起):當作選項 string[] options = messages.Skip(1).ToArray(); // 選項的起始 Y 座標 int optionsStartY = startY; // 每欄最多可放幾個項目(以兩欄平均分配) int maxItemsPerColumn = (int)Math.Ceiling(options.Length / 2.0); // 設定左右欄的 X 座標(用於放置圖片) int leftColumnX = 200; int rightColumnX = this.Width / 2 + 150; // 開始逐一處理每個選項 for (int i = 0; i < options.Length; i++) { // 設定每個選項的字型 Font optionFont = new Font("Microsoft JhengHei", optionFontSize, FontStyle.Bold); // 建立文字對應的圖像(白色字+透明底) Bitmap optionBitmap = GenerateTextImage(options[i], optionFont, Color.White, Color.Transparent); // 根據目前索引,決定放在左欄還是右欄 int x = (i < maxItemsPerColumn) ? leftColumnX : rightColumnX; // 決定目前這張圖的 Y 座標位置(依序向下排列) int currentY = optionsStartY + ((i % maxItemsPerColumn) * (optionBitmap.Height + lineSpacing)); // 加入圖片到畫面上 AddPicture(optionBitmap, x, currentY); } } private string strokeRange; private int totalArtists = 0; private const int artistsPerPage = 8; private List currentArtistList = new List(); private void ProcessStrokeCountSelection(int number) { List selectedArtists = null; var manager =new ArtistManager(); switch (number) { case 1: selectedArtists = manager.GetArtistsByCategoryAndStrokeCountRange(currentArtistCategory ,0, 3); strokeRange = "00~03"; break; case 2: selectedArtists = manager.GetArtistsByCategoryAndStrokeCountRange(currentArtistCategory, 4, 7); strokeRange = "04~07"; break; case 3: selectedArtists = manager.GetArtistsByCategoryAndStrokeCountRange(currentArtistCategory, 8, 11); strokeRange = "08~11"; break; case 4: selectedArtists = manager.GetArtistsByCategoryAndStrokeCountRange(currentArtistCategory, 12, 15); strokeRange = "12~15"; break; case 5: selectedArtists = manager.GetArtistsByCategoryAndStrokeCountRange(currentArtistCategory, 16, int.MaxValue); strokeRange = "16以上"; break; default: Console.WriteLine("Invalid selection"); return; } if (selectedArtists != null && selectedArtists.Count > 0) { SetUIState(OverlayForm.UIState.SelectingArtist); DisplayArtists(selectedArtists, currentPage); } else { Console.WriteLine("No artists found for the selected stroke count range."); } } private void DisplayArtists(List artists, int page)//歌星點進去後的畫面 { currentArtistList = artists; totalArtists = artists.Count; if (artists == null || artists.Count == 0) { Console.WriteLine("Artist list is null or empty."); return; } this.Controls.OfType().ToList().ForEach(p => this.Controls.Remove(p)); int artistsPerColumn = 4; int startIndex = (page - 1) * artistsPerPage; int endIndex = Math.Min(startIndex + artistsPerPage, artists.Count); int totalPages = (int)Math.Ceiling((double)artists.Count / artistsPerPage); string categoryDisplayText = currentArtistCategory switch { "男" => "男歌星", "女" => "女歌星", "團" => "團體", "外" => "外語", "全部" => "全部", _ => currentArtistCategory }; string headerText = String.Format("{0} -- {1} ({2} / {3})", categoryDisplayText, strokeRange, page, totalPages); Font headerFont = new Font("Microsoft JhengHei", 50, FontStyle.Bold); Bitmap headerBitmap = GenerateTextImage(headerText, headerFont, Color.White, Color.Transparent); AddCenteredPicture(headerBitmap, 150); int startY = 250; int verticalSpacing = 15; int leftColumnX = 200; // 左邊列的起始 X 偏移量 int rightColumnX = this.Width / 2 + 150; // 右邊列的起始 X 偏移量 for (int i = startIndex; i < endIndex; i++) { int artistNumber = i - startIndex + 1; string artistNumberText = $"{artistNumber}."; string artistNameText = artists[i].Name; Font numberFont = new Font("Microsoft JhengHei", 45 , FontStyle.Bold); Bitmap numberBitmap = GenerateTextImage(artistNumberText, numberFont, Color.White, Color.Transparent); Font nameFont = new Font("Microsoft JhengHei", 45, FontStyle.Bold); Bitmap nameBitmap = GenerateTextImage(artistNameText, nameFont, Color.White, Color.Transparent); int x = (i - startIndex) < artistsPerColumn ? leftColumnX : rightColumnX; int y = startY + ((i - startIndex) % artistsPerColumn) * (numberBitmap.Height + verticalSpacing); AddPicture(numberBitmap, x, y); AddPicture(nameBitmap, x + numberBitmap.Width, y); } } private void DisplayStrokeCountOptions() { string categoryDisplayText; switch (currentArtistCategory) { case "男": categoryDisplayText = "男歌星"; break; case "女": categoryDisplayText = "女歌星"; break; case "團": categoryDisplayText = "團體"; break; case "外": categoryDisplayText = "外語"; break; case "全部": categoryDisplayText = "全部"; break; default: categoryDisplayText = currentArtistCategory; break; } string[] messages = new string[] { categoryDisplayText, "1. 0~3", "2. 4~7", "3. 8~11", "4. 12~15", "5. 16以上" }; SetUIState(OverlayForm.UIState.SelectingStrokeCount); UpdateDisplayLabels(messages); } private void DisplaySongsInLanguage(string language, Category category) { string sqlQuery; if (category == Category.NewSongs) { sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};"; } else if (category == Category.HotSongs) { sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY song_counts DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; } else { ClearDisplay(); displayLabel.Text = "無效的類別"; return; } var songsInLanguage = PrimaryForm.Instance.SearchSongs_Mysql(sqlQuery); if (songsInLanguage == null || songsInLanguage.Count == 0) { ClearDisplay(); displayLabel.Text = $"{language} - 熱門中未找到歌曲"; LanguageSongList = null; totalSongs = 0; currentPage = 0; return; } LanguageSongList = songsInLanguage; totalSongs = songsInLanguage.Count; currentPage = 1; ClearDisplay(); DisplaySongs(currentPage); } // 歌庫路徑調整 public void AddSongToPlaylist(SongData songData) { try { //var filePath1 = songData.SongFilePathHost1; //var filePath2 = songData.SongFilePathHost2; // 之後還要設計成本地的資料夾位置 string pathToPlay = songData.FileName; if (File.Exists(pathToPlay)) { bool wasEmpty = PrimaryForm.userRequestedSongs.Count == 0; PrimaryForm.userRequestedSongs.Add(songData); PrimaryForm.playedSongsHistory.Add(songData); PrimaryForm.playStates.Add(wasEmpty ? PlayState.Playing : PlayState.NotPlayed); // 刷新頁面 if (PrimaryForm.Instance.multiPagePanel.get_currentSongList() == PrimaryForm.playedSongsHistory) PrimaryForm.Instance.multiPagePanel.LoadSongs(PrimaryForm.Instance.currentSongList); if (wasEmpty) { VideoPlayerForm.Instance.SetPlayingSongList(PrimaryForm.userRequestedSongs); PrimaryForm.currentSongIndexInHistory += 1; } VideoPlayerForm.Instance.UpdateNextSongFromPlaylist(); PrimaryForm.PrintPlayingSongList(); // 點播次數+1 PrimaryForm.Instance.AddSongCount(songData.SongNumber); } } catch (Exception ex) { Console.WriteLine("Error occurred: " + ex.Message); } } public void InsertSongToPlaylist(SongData songData) { try { // 從 songData 中取得兩個可能的檔案路徑(主機1與主機2) var pathToPlay = songData.FileName; // 檢查兩個主機上的檔案是否皆不存在 if (File.Exists(pathToPlay)) { // 若其中一個存在,就用第一個存在的那個檔案 Console.WriteLine("path to play" + pathToPlay); // 檢查目前使用者歌單是否為空 bool wasEmpty = PrimaryForm.userRequestedSongs.Count == 0; if (wasEmpty) { // 若是空的,代表是第一首歌,直接加入並設為正在播放 PrimaryForm.userRequestedSongs.Add(songData); // 加入播放清單 VideoPlayerForm.Instance.SetPlayingSongList(PrimaryForm.userRequestedSongs); // 傳給播放器 PrimaryForm.playedSongsHistory.Add(songData); // 加入播放歷史 PrimaryForm.playStates.Add(PlayState.Playing); // 設定狀態為正在播放 PrimaryForm.currentSongIndexInHistory += 1; // 歷史索引 +1 } else if (PrimaryForm.userRequestedSongs.Count == 1) { // 若清單中已有一首,插入新歌在 index 1 (即目前播放之後的位置) PrimaryForm.userRequestedSongs.Insert(1, songData); PrimaryForm.playedSongsHistory.Insert(PrimaryForm.currentSongIndexInHistory + 1, songData); PrimaryForm.playStates.Insert(PrimaryForm.currentSongIndexInHistory + 1, PlayState.NotPlayed); // 尚未播放 } else { // 若清單中超過一首,同樣插入在 index 1,也是在當前歌曲之後的位置 PrimaryForm.userRequestedSongs.Insert(1, songData); PrimaryForm.playedSongsHistory.Insert(PrimaryForm.currentSongIndexInHistory + 1, songData); PrimaryForm.playStates.Insert(PrimaryForm.currentSongIndexInHistory + 1, PlayState.NotPlayed); } // 更新下一首即將播放的資訊(畫面或播放器邏輯用) VideoPlayerForm.Instance.UpdateNextSongFromPlaylist(); // 印出目前播放清單資訊到畫面或 console PrimaryForm.PrintPlayingSongList(); } } catch (Exception ex) { // 捕捉所有例外並印出錯誤訊息(避免整個流程當掉) Console.WriteLine("Error occurred: " + ex.Message); } } // 測試 public int currentPage = 1; public int songsPerPage = 5; public int totalSongs = 0; public void DisplaySongs(int page) { // 檢查 LanguageSongList 是否為空,避免發生錯誤 if (LanguageSongList == null || LanguageSongList.Count == 0) { Console.WriteLine("LanguageSongList is null or empty."); return; } // 清除介面上所有 PictureBox 控件,避免重複顯示舊的內容 this.Controls.OfType().ToList().ForEach(p => this.Controls.Remove(p)); // 每列顯示 5 首歌 int songsPerColumn = 5; // 計算當前頁面的起始與結束索引 int startIndex = (page - 1) * songsPerPage; int endIndex = Math.Min(startIndex + songsPerPage, LanguageSongList.Count); // 計算總頁數 int totalPages = (int)Math.Ceiling((double)LanguageSongList.Count / songsPerPage); // 根據當前分類選擇標題文字 string categoryText = OverlayForm.CurrentCategory switch { OverlayForm.Category.NewSongs => "新歌", OverlayForm.Category.HotSongs => "熱門", _ => "" }; // 設定標題格式,包含語言、分類與當前頁碼 string headerText = $"{currentLanguage} - {categoryText} ({page} / {totalPages})"; // 設定標題的字體樣式 Font headerFont = new Font("Microsoft JhengHei", 60, FontStyle.Bold); // 生成標題圖片 Bitmap headerBitmap = GenerateTextImage(headerText, headerFont, Color.White, Color.Transparent); // 顯示標題圖片,垂直置於 150px 處 AddCenteredPicture(headerBitmap, 150); // 設定歌名顯示區域的起始 Y 位置 int startY = 250; // 左列與右列的 X 位置 int leftColumnX = 100; int rightColumnX = this.Width / 2 + 100; // 計算當前頁面最大歌名和歌手文字長度,決定適合的字體大小 int maxSongLength = 0; int maxArtistLength = 0; for (int i = startIndex; i < endIndex; i++) { string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Song}"; string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB) ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}" : LanguageSongList[i].ArtistA; maxSongLength = Math.Max(maxSongLength, songText.Length); maxArtistLength = Math.Max(maxArtistLength, artistText.Length); } // 根據最大字數決定適當的字體大小 int songFontSize = maxSongLength > 20 ? 35 : 45; int artistFontSize = maxArtistLength > 20 ? 30 : 35; // 設定歌曲行間距 int verticalSpacing = songFontSize == 30 ? 25 : 10; // 設定統一的行高 int rowHeight = 0; // 計算行高 for (int i = startIndex; i < endIndex; i++) { string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Song}"; string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB) ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}" : LanguageSongList[i].ArtistA; Font songFont = new Font("Microsoft JhengHei", songFontSize, FontStyle.Bold); Font artistFont = new Font("Microsoft JhengHei", artistFontSize, FontStyle.Bold); Bitmap songBitmap = GenerateTextImage(songText, songFont, Color.White, Color.Transparent); Bitmap artistBitmap = GenerateTextImage(artistText, artistFont, Color.White, Color.Transparent); rowHeight = Math.Max(rowHeight, Math.Max(songBitmap.Height, artistBitmap.Height)); } // 依據計算出的行高,逐行顯示歌曲與歌手名稱 for (int i = startIndex; i < endIndex; i++) { int songNumber = i - startIndex + 1; string songText = $"{songNumber}. {LanguageSongList[i].Song}"; string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB) ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}" : LanguageSongList[i].ArtistA; Font songFont = new Font("Microsoft JhengHei", songFontSize, FontStyle.Bold); Font artistFont = new Font("Microsoft JhengHei", artistFontSize, FontStyle.Bold); Bitmap songBitmap = GenerateTextImage(songText, songFont, Color.White, Color.Transparent); Bitmap artistBitmap = GenerateTextImage(artistText, artistFont, Color.White, Color.Transparent); // 根據索引決定左側或右側顯示 int x = (i - startIndex) < songsPerColumn ? leftColumnX : rightColumnX; // 計算 Y 位置 int y = startY + ((i - startIndex) % songsPerColumn) * (rowHeight + verticalSpacing); // 顯示歌曲名稱圖片 AddPicture(songBitmap, x, y); // 顯示歌手名稱圖片(稍微右移) AddPicture(artistBitmap, x + songBitmap.Width + 20, y); } } public void DisplaySongsWithArrows(int page, int highlightIndex) { // 檢查 LanguageSongList 是否為空,避免發生錯誤 if (LanguageSongList == null || LanguageSongList.Count == 0) { Console.WriteLine("Error: LanguageSongList is null or empty."); return; } // 清除介面上所有 PictureBox 控件,避免重複顯示舊的內容 this.Controls.OfType().ToList().ForEach(p => this.Controls.Remove(p)); // 每列顯示 5 首歌 int songsPerColumn = 5; // 計算當前頁面的起始與結束索引 int startIndex = (page - 1) * songsPerPage; int endIndex = Math.Min(startIndex + songsPerPage, LanguageSongList.Count); // 計算總頁數 int totalPages = (int)Math.Ceiling((double)LanguageSongList.Count / songsPerPage); // 根據當前分類選擇標題文字 string categoryText = OverlayForm.CurrentCategory switch { OverlayForm.Category.NewSongs => "新歌", OverlayForm.Category.HotSongs => "熱門", _ => "" }; // 設定標題格式,包含語言、分類與當前頁碼 string headerText = $"{currentLanguage} - {categoryText} ({page} / {totalPages})"; // 設定標題的字體樣式 Font headerFont = new Font("Microsoft JhengHei", 60, FontStyle.Bold); // 生成標題圖片 Bitmap headerBitmap = GenerateTextImage(headerText, headerFont, Color.White, Color.Transparent); // 顯示標題圖片,垂直置於 150px 處 AddCenteredPicture(headerBitmap, 150); // 設定歌名顯示區域的起始 Y 位置 int startY = 250; // 左列與右列的 X 位置 int leftColumnX = 100; int rightColumnX = this.Width / 2 + 100; // 找到當前頁面中最長的 songText 和 artistText 長度 int maxSongLength = 0; int maxArtistLength = 0; for (int i = startIndex; i < endIndex; i++) { string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Song}"; string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB) ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}" : LanguageSongList[i].ArtistA; maxSongLength = Math.Max(maxSongLength, songText.Length); maxArtistLength = Math.Max(maxArtistLength, artistText.Length); } // 動態調整字體大小 int songFontSize = maxSongLength > 20 ? 35 : 45; int artistFontSize = maxArtistLength > 20 ? 30 : 35; int verticalSpacing = songFontSize == 30 ? 25 : 10; // 統一行高計算 int rowHeight = 0; for (int i = startIndex; i < endIndex; i++) { string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Song}"; string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB) ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}" : LanguageSongList[i].ArtistA; Font tempSongFont = new Font("Microsoft JhengHei", songFontSize, FontStyle.Bold); Font tempArtistFont = new Font("Microsoft JhengHei", artistFontSize, FontStyle.Bold); Bitmap tempSongBitmap = GenerateTextImage(songText, tempSongFont, Color.White, Color.Transparent); Bitmap tempArtistBitmap = GenerateTextImage(artistText, tempArtistFont, Color.White, Color.Transparent); rowHeight = Math.Max(rowHeight, Math.Max(tempSongBitmap.Height, tempArtistBitmap.Height)); } // 依據計算出的行高,逐行顯示歌曲與歌手名稱 for (int i = startIndex; i < endIndex; i++) { int songNumber = i - startIndex + 1; string songText = $"{songNumber}. {LanguageSongList[i].Song}"; string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB) ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}" : LanguageSongList[i].ArtistA; // 設定顏色,選中的索引顯示為亮綠色 Color songColor = (i == highlightIndex) ? Color.LimeGreen : Color.White; Color artistColor = (i == highlightIndex) ? Color.LimeGreen : Color.White; Font songFont = new Font("Microsoft JhengHei", songFontSize, FontStyle.Bold); Bitmap songBitmap = GenerateTextImage(songText, songFont, songColor, Color.Transparent); Font artistFont = new Font("Microsoft JhengHei", artistFontSize, FontStyle.Bold); Bitmap artistBitmap = GenerateTextImage(artistText, artistFont, artistColor, Color.Transparent); int x = (i - startIndex) < songsPerColumn ? leftColumnX : rightColumnX; int y = startY + ((i - startIndex) % songsPerColumn) * (rowHeight + verticalSpacing); // 顯示歌曲名稱圖片 AddPicture(songBitmap, x, y); // 顯示歌手名稱圖片(稍微右移) AddPicture(artistBitmap, x + songBitmap.Width + 20, y); } } public void DisplayActionWithSong(int page, int songIndex, string actionType) { // try // { // if (LanguageSongList == null || LanguageSongList.Count == 0) // { // Console.WriteLine("Error: LanguageSongList is null or empty."); // return; // } // SongData song = LanguageSongList[songIndex]; // this.Controls.OfType().ToList().ForEach(p => this.Controls.Remove(p)); // int songsPerColumn = 5; // int startIndex = (page - 1) * songsPerPage; // int endIndex = Math.Min(startIndex + songsPerPage, LanguageSongList.Count); // int totalPages = (int)Math.Ceiling((double)LanguageSongList.Count / songsPerPage); // string headerText = $"{actionType}: {song.ArtistA} - {song.Song} ({page} / {totalPages})"; // Font headerFont = new Font("Microsoft JhengHei", 40, FontStyle.Bold); // Color headerColor = actionType == "點播" ? Color.LimeGreen : Color.Yellow; // Bitmap headerBitmap = GenerateTextImage(headerText, headerFont, headerColor, Color.Transparent); // AddCenteredPicture(headerBitmap, 150); // int startY = 250; // int verticalSpacing = 10; // int leftColumnX = 200; // int rightColumnX = this.Width / 2 + 150; // for (int i = startIndex; i < endIndex; i++) // { // int songNumber = i - startIndex + 1; // string songText = $"{songNumber}. {LanguageSongList[i].Song}"; // string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB) // ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}" // : LanguageSongList[i].ArtistA; // Font songFont = new Font("Microsoft JhengHei", 40, FontStyle.Bold); // Bitmap songBitmap = GenerateTextImage(songText, songFont, Color.White, Color.Transparent); // Font artistFont = new Font("Microsoft JhengHei", 30, FontStyle.Bold); // Bitmap artistBitmap = GenerateTextImage(artistText, artistFont, Color.White, Color.Transparent); // int x = (i - startIndex) < songsPerColumn ? leftColumnX : rightColumnX; // int y = startY + ((i - startIndex) % songsPerColumn) * (songBitmap.Height + verticalSpacing); // AddPicture(songBitmap, x, y); // AddPicture(artistBitmap, x + songBitmap.Width + 20, y); // } // } // catch (Exception ex) // { // Console.WriteLine($"Error in DisplayActionWithSong: {ex.Message}"); // Console.WriteLine(ex.StackTrace); // } } public void NextPage() { unifiedTimer.Stop(); if (CurrentUIState == UIState.SelectingArtist) { if (currentPage * artistsPerPage < totalArtists) { currentPage++; DisplayArtists(currentArtistList, currentPage); } } else { if (currentPage * songsPerPage < totalSongs) { currentPage++; DisplaySongs(currentPage); } } unifiedTimer.Start(); } public void PreviousPage() { unifiedTimer.Stop(); if (CurrentUIState == UIState.SelectingArtist) { if (currentPage > 1) { currentPage--; DisplayArtists(currentArtistList, currentPage); } } else { if (currentPage > 1) { currentPage--; DisplaySongs(currentPage); } } unifiedTimer.Start(); } } }