2025-04-07 16:54:10 +08:00
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 < string > textSegments = new List < string > ( ) ;
public enum MarqueeStartPosition
{
Middle ,
Right
}
private static OverlayForm _mainForm ;
public static OverlayForm MainForm
{
get { return _mainForm ; }
private set { _mainForm = value ; }
}
2025-05-19 13:23:09 +08:00
public static OverlayForm Instance { get ; private set ; }
2025-04-07 16:54:10 +08:00
public OverlayForm ( )
{
SetStyle ( ControlStyles . SupportsTransparentBackColor , true ) ;
MainForm = this ;
2025-05-19 13:23:09 +08:00
Instance = this ;
2025-04-07 16:54:10 +08:00
InitializeFormSettings ( ) ;
ConfigureTimers ( ) ;
InitializeLabels ( ) ;
ConfigureSegmentTimer ( ) ;
2025-04-10 14:16:34 +08:00
imageYPos = ( screenHeight / 3 ) - 1024 / 6 ;
2025-04-07 16:54:10 +08:00
}
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
{
2025-04-09 11:46:18 +08:00
string filePath = Path . Combine ( Application . StartupPath , "txt" , "WelcomeMessage.txt" ) ;
2025-04-07 16:54:10 +08:00
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 ( ) ;
}
2025-04-09 11:23:22 +08:00
2025-04-07 16:54:10 +08:00
stickerTimer1 . Stop ( ) ;
HideImages ( ) ;
} ;
stickerTimer2 . Interval = 15000 ;
stickerTimer2 . Tick + = ( sender , e ) = > {
lock ( imageLock )
{
secondStickerImage = null ;
this . Invalidate ( ) ;
}
2025-04-09 11:23:22 +08:00
2025-04-07 16:54:10 +08:00
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 ( ) ; // 停止计时器
}
2025-05-08 10:09:14 +08:00
// 當 songDisplayTimer 計時完成時會呼叫此函式
2025-04-07 16:54:10 +08:00
private static void SongDisplayTimer_Elapsed ( object sender , EventArgs e )
{
2025-05-08 10:09:14 +08:00
// 檢查是否需要跨執行緒操作 UI 控制項
2025-04-07 16:54:10 +08:00
if ( MainForm . InvokeRequired )
{
2025-05-08 10:09:14 +08:00
// 如果目前不在 UI 執行緒上,必須透過 Invoke 安全執行 UI 更新邏輯
2025-04-07 16:54:10 +08:00
Console . WriteLine ( "SongDisplayTimer_Tick invoked on UI thread." ) ;
2025-06-24 10:35:01 +08:00
MainForm . BeginInvoke ( new Action ( ( ) = >
2025-04-07 16:54:10 +08:00
{
2025-06-24 10:35:01 +08:00
if ( MainForm . songDisplayLabel ! = null )
MainForm . songDisplayLabel . Text = "" ;
2025-05-08 10:09:14 +08:00
2025-06-24 10:35:01 +08:00
if ( MainForm . nextSongLabel ! = null )
MainForm . nextSongLabel . Visible = true ;
2025-04-07 16:54:10 +08:00
} ) ) ;
}
else
{
2025-05-08 10:09:14 +08:00
// 如果已經在 UI 執行緒,就直接更新 UI 控制項
2025-04-07 16:54:10 +08:00
Console . WriteLine ( "SongDisplayTimer_Tick invoked on background thread." ) ;
MainForm . songDisplayLabel . Text = "" ;
2025-05-08 10:09:14 +08:00
MainForm . nextSongLabel . Visible = true ;
2025-04-07 16:54:10 +08:00
}
2025-05-08 10:09:14 +08:00
// 停止計時器,避免重複觸發
2025-04-07 16:54:10 +08:00
songDisplayTimer . Stop ( ) ;
}
2025-05-08 10:09:14 +08:00
2025-04-07 16:54:10 +08:00
private readonly object _lockObject = new object ( ) ;
2025-05-26 15:21:07 +08:00
private bool _handlingTimeout = false ;
2025-04-07 16:54:10 +08:00
private async void UnifiedTimer_Elapsed ( object sender , EventArgs e )
{
2025-05-26 15:21:07 +08:00
if ( _handlingTimeout ) return ;
_handlingTimeout = true ;
2025-04-07 16:54:10 +08:00
2025-05-26 15:21:07 +08:00
try
{
if ( MainForm . InvokeRequired )
{
MainForm . BeginInvoke ( ( Action ) ( ( ) = > UnifiedTimer_Elapsed ( sender , e ) ) ) ;
return ;
}
displayLabel . Text = "" ;
2025-04-07 16:54:10 +08:00
2025-05-26 15:21:07 +08:00
switch ( CurrentUIState )
{
case UIState . SelectingLanguage :
case UIState . SelectingArtistCategory :
case UIState . SelectingAction :
case UIState . SelectingSong :
case UIState . SelectingArtist :
case UIState . PlayHistory :
SetUIState ( UIState . Initial ) ;
2025-05-28 11:04:03 +08:00
await HandleTimeout ( "" ) ;
2025-05-26 15:21:07 +08:00
break ;
}
}
finally
2025-04-07 16:54:10 +08:00
{
2025-05-26 15:21:07 +08:00
_handlingTimeout = false ;
2025-04-07 16:54:10 +08:00
}
}
private async Task HandleTimeout ( string message )
{
Console . WriteLine ( "HandleTimeout called with message: " + message ) ;
2025-05-26 15:21:07 +08:00
unifiedTimer . Stop ( ) ;
2025-04-07 16:54:10 +08:00
SetUIState ( UIState . Initial ) ;
2025-05-26 15:21:07 +08:00
//DisplayMessage(message, 2000);
CommandHandler . _indataHistory . Clear ( ) ; // 清空歷史紀錄
2025-04-07 16:54:10 +08:00
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
{
2025-04-09 11:46:18 +08:00
string filePath = Path . Combine ( Application . StartupPath , "txt" , "WelcomeMessage.txt" ) ;
2025-04-07 16:54:10 +08:00
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 ) )
2025-07-03 18:15:21 +08:00
using ( Brush LimeGreen = new SolidBrush ( Color . LimeGreen ) )
2025-04-07 16:54:10 +08:00
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 ;
2025-07-03 18:15:21 +08:00
2025-04-07 16:54:10 +08:00
e . Graphics . FillRectangle ( backgroundBrush , centeredXPos , yPosition2 , textSizeSecondLine . Width , textSizeSecondLine . Height ) ;
2025-07-03 18:15:21 +08:00
e . Graphics . DrawString ( marqueeTextSecondLine , largeFont , LimeGreen , new PointF ( centeredXPos , yPosition2 ) ) ;
2025-04-07 16:54:10 +08:00
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 )
{
2025-04-14 14:17:06 +08:00
//Console.WriteLine("Attempting to display sticker.");
2025-04-07 16:54:10 +08:00
this . Invoke ( ( MethodInvoker ) delegate {
Console . WriteLine ( "Form Width: " + this . Width ) ;
Console . WriteLine ( "Form Height: " + this . Height ) ;
2025-04-10 14:16:34 +08:00
string imagePath = String . Format ( "{0}\\themes\\superstar\\superstar-pic\\{1}.png" , Application . StartupPath , stickerId ) ;
2025-04-07 16:54:10 +08:00
Console . WriteLine ( "Image path: " + imagePath ) ;
try
{
Image newSticker = Image . FromFile ( imagePath ) ;
lock ( imageLock )
{
if ( firstStickerImage = = null )
{
firstStickerImage = newSticker ;
2025-04-09 11:23:22 +08:00
firstStickerXPos = this . Width / 2 - firstStickerImage . Width / 2 ;
2025-04-07 16:54:10 +08:00
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 < SongData > 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 < Control > ( ) . 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 & &
2025-05-26 15:21:07 +08:00
control ! = MainForm . nextSongLabel )
2025-04-07 16:54:10 +08:00
{
MainForm . Controls . Remove ( control ) ;
2025-05-26 15:21:07 +08:00
// control.Dispose();
control . Visible = false ;
2025-04-07 16:54:10 +08:00
}
}
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 ] ;
2025-07-04 18:19:41 +08:00
Console . WriteLine ( "Adding song to playlist: " + LanguageSongList [ songIndex ] . Song + " " + selectedSong . FileName ) ;
2025-04-07 16:54:10 +08:00
// 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 ] ;
2025-07-04 18:19:41 +08:00
Console . WriteLine ( "Adding song to playlist: " + LanguageSongList [ songIndex ] . Song + " " + selectedSong . FileName ) ;
2025-04-07 16:54:10 +08:00
// 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 :
2025-05-20 16:16:27 +08:00
currentLanguage = "英語" ;
2025-04-07 16:54:10 +08:00
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 ) ;
2025-05-27 11:27:43 +08:00
string query = $"SELECT * FROM song_library_cache WHERE artistA ='{selectedArtist.Name}' OR artistB='{selectedArtist.Name}' ORDER BY song_counts DESC;" ;
2025-04-23 11:46:55 +08:00
LanguageSongList = PrimaryForm . Instance . SearchSongs_Mysql ( query ) ;
//LanguageSongList = SongListManager.Instance.GetSongsByArtist(selectedArtist.Name);
2025-04-07 16:54:10 +08:00
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 )
{
2025-05-26 15:21:07 +08:00
selectedSong = LanguageSongList [ songIndex ] ;
2025-07-04 18:19:41 +08:00
Console . WriteLine ( "Adding song to playlist C: " + LanguageSongList [ songIndex ] . Song + " " + selectedSong . FileName ) ;
2025-04-07 16:54:10 +08:00
2025-05-26 15:21:07 +08:00
2025-05-28 11:04:03 +08:00
DisplaySongsWithArrows ( currentPage , songIndex ) ;
//AddSongToPlaylist(selectedSong);
2025-04-07 16:54:10 +08:00
}
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 < Control > ( ) . 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 ( ) ;
}
}
}
2025-05-08 10:09:14 +08:00
public void UpdateHistoryLabel ( List < SongData > historySongs , List < PlayState > playStates , int currentPage , int totalPages )
{
// 移除畫面上所有現有的 PictureBox( 用於刷新內容)
this . Controls . OfType < PictureBox > ( ) . ToList ( ) . ForEach ( p = > this . Controls . Remove ( p ) ) ;
2025-04-07 16:54:10 +08:00
2025-05-08 10:09:14 +08:00
// 建立標題文字,例如 "已點歌曲 (1 / 3)"
string headerText = $"已點歌曲 ({currentPage} / {totalPages})" ;
Font headerFont = new Font ( "Microsoft JhengHei" , 50 , FontStyle . Bold ) ;
2025-04-07 16:54:10 +08:00
2025-05-08 10:09:14 +08:00
// 將標題文字轉為圖片
Bitmap headerBitmap = GenerateTextImage ( headerText , headerFont , Color . White , Color . Transparent ) ;
2025-04-07 16:54:10 +08:00
2025-05-08 10:09:14 +08:00
// 將標題圖片置中顯示在 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 ) ;
2025-04-07 16:54:10 +08:00
2025-05-08 10:09:14 +08:00
Bitmap statusBitmap = null ;
// 如果有狀態文字,則也轉成圖片
if ( ! string . IsNullOrEmpty ( status ) )
{
Font statusFont = new Font ( "Microsoft JhengHei" , 50 , FontStyle . Bold ) ;
statusBitmap = GenerateTextImage ( status , statusFont , textColor , Color . Transparent ) ;
}
2025-04-07 16:54:10 +08:00
2025-05-08 10:09:14 +08:00
// 計算目前這首歌的 Y 座標位置
2025-05-27 11:27:43 +08:00
int fixedRowHeight = 80 ;
int y = startY + i * ( fixedRowHeight + verticalSpacing ) ;
//int y = startY + i * (songBitmap.Height + verticalSpacing);
2025-04-07 16:54:10 +08:00
2025-05-08 10:09:14 +08:00
// 加入歌名圖片到畫面左側
AddPicture ( songBitmap , leftMargin , y ) ;
// 若有狀態圖片,則加入到畫面右側
if ( statusBitmap ! = null )
{
int statusX = rightMargin - statusBitmap . Width ;
AddPicture ( statusBitmap , statusX , y ) ;
}
}
}
2025-04-07 16:54:10 +08:00
public void UpdateDisplayLabels ( string [ ] messages ) //新歌歌星排行首頁
{
2025-05-27 11:27:43 +08:00
// 移除畫面上現有的所有 PictureBox( 舊的文字圖片)
2025-04-07 16:54:10 +08:00
this . Controls . OfType < PictureBox > ( ) . ToList ( ) . ForEach ( p = > this . Controls . Remove ( p ) ) ;
2025-05-27 11:27:43 +08:00
// 如果沒有訊息內容,直接跳出,不進行後續繪製
2025-04-07 16:54:10 +08:00
if ( messages . Length = = 0 ) return ;
2025-05-27 11:27:43 +08:00
// 設定主標題字體大小
int mainTitleFontSize = 60 ;
// 設定選項字體大小
int optionFontSize = 50 ;
// 每行之間的垂直間距(像素)
int lineSpacing = 15 ;
2025-04-07 16:54:10 +08:00
2025-05-27 11:27:43 +08:00
// 取得主標題文字(第一行)
2025-04-07 16:54:10 +08:00
string mainTitle = messages [ 0 ] ;
2025-05-27 11:27:43 +08:00
// 建立主標題用的字型:微軟正黑體、粗體
2025-04-07 16:54:10 +08:00
Font mainTitleFont = new Font ( "Microsoft JhengHei" , mainTitleFontSize , FontStyle . Bold ) ;
2025-05-27 11:27:43 +08:00
// 使用自定義方法產生主標題的文字圖像(白色字+透明底)
2025-04-07 16:54:10 +08:00
Bitmap mainTitleBitmap = GenerateTextImage ( mainTitle , mainTitleFont , Color . White , Color . Transparent ) ;
2025-05-27 11:27:43 +08:00
// 主標題起始的 Y 座標
2025-04-07 16:54:10 +08:00
int startY = 130 ;
2025-05-27 11:27:43 +08:00
// 將主標題圖片水平置中並加入畫面
2025-04-07 16:54:10 +08:00
AddCenteredPicture ( mainTitleBitmap , startY ) ;
2025-05-27 11:27:43 +08:00
// 更新 Y 座標位置,準備繪製下一區塊(選項),加上主標題高度與間距
2025-04-07 16:54:10 +08:00
startY + = mainTitleBitmap . Height + lineSpacing ;
2025-05-27 11:27:43 +08:00
// 取得剩下的訊息文字(第二行起):當作選項
2025-04-07 16:54:10 +08:00
string [ ] options = messages . Skip ( 1 ) . ToArray ( ) ;
2025-05-27 11:27:43 +08:00
// 選項的起始 Y 座標
2025-04-07 16:54:10 +08:00
int optionsStartY = startY ;
2025-05-27 11:27:43 +08:00
// 每欄最多可放幾個項目(以兩欄平均分配)
2025-04-07 16:54:10 +08:00
int maxItemsPerColumn = ( int ) Math . Ceiling ( options . Length / 2.0 ) ;
2025-05-27 11:27:43 +08:00
// 設定左右欄的 X 座標(用於放置圖片)
int leftColumnX = 200 ;
2025-04-07 16:54:10 +08:00
int rightColumnX = this . Width / 2 + 150 ;
2025-05-27 11:27:43 +08:00
// 開始逐一處理每個選項
2025-04-07 16:54:10 +08:00
for ( int i = 0 ; i < options . Length ; i + + )
{
2025-05-27 11:27:43 +08:00
// 設定每個選項的字型
2025-04-07 16:54:10 +08:00
Font optionFont = new Font ( "Microsoft JhengHei" , optionFontSize , FontStyle . Bold ) ;
2025-05-27 11:27:43 +08:00
// 建立文字對應的圖像(白色字+透明底)
2025-04-07 16:54:10 +08:00
Bitmap optionBitmap = GenerateTextImage ( options [ i ] , optionFont , Color . White , Color . Transparent ) ;
2025-05-27 11:27:43 +08:00
// 根據目前索引,決定放在左欄還是右欄
2025-04-07 16:54:10 +08:00
int x = ( i < maxItemsPerColumn ) ? leftColumnX : rightColumnX ;
2025-05-27 11:27:43 +08:00
// 決定目前這張圖的 Y 座標位置(依序向下排列)
2025-04-07 16:54:10 +08:00
int currentY = optionsStartY + ( ( i % maxItemsPerColumn ) * ( optionBitmap . Height + lineSpacing ) ) ;
2025-05-27 11:27:43 +08:00
// 加入圖片到畫面上
2025-04-07 16:54:10 +08:00
AddPicture ( optionBitmap , x , currentY ) ;
}
2025-05-27 11:27:43 +08:00
2025-04-07 16:54:10 +08:00
}
private string strokeRange ;
private int totalArtists = 0 ;
2025-05-27 11:27:43 +08:00
private const int artistsPerPage = 8 ;
2025-04-07 16:54:10 +08:00
private List < Artist > currentArtistList = new List < Artist > ( ) ;
private void ProcessStrokeCountSelection ( int number )
{
List < Artist > selectedArtists = null ;
2025-04-23 11:46:55 +08:00
var manager = new ArtistManager ( ) ;
2025-04-07 16:54:10 +08:00
switch ( number )
{
case 1 :
2025-04-23 11:46:55 +08:00
selectedArtists = manager . GetArtistsByCategoryAndStrokeCountRange ( currentArtistCategory , 0 , 3 ) ;
2025-04-07 16:54:10 +08:00
strokeRange = "00~03" ;
break ;
case 2 :
2025-04-23 11:46:55 +08:00
selectedArtists = manager . GetArtistsByCategoryAndStrokeCountRange ( currentArtistCategory , 4 , 7 ) ;
2025-04-07 16:54:10 +08:00
strokeRange = "04~07" ;
break ;
case 3 :
2025-04-23 11:46:55 +08:00
selectedArtists = manager . GetArtistsByCategoryAndStrokeCountRange ( currentArtistCategory , 8 , 11 ) ;
2025-04-07 16:54:10 +08:00
strokeRange = "08~11" ;
break ;
case 4 :
2025-04-23 11:46:55 +08:00
selectedArtists = manager . GetArtistsByCategoryAndStrokeCountRange ( currentArtistCategory , 12 , 15 ) ;
2025-04-07 16:54:10 +08:00
strokeRange = "12~15" ;
break ;
case 5 :
2025-04-23 11:46:55 +08:00
selectedArtists = manager . GetArtistsByCategoryAndStrokeCountRange ( currentArtistCategory , 16 , int . MaxValue ) ;
2025-04-07 16:54:10 +08:00
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 < Artist > 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 < PictureBox > ( ) . ToList ( ) . ForEach ( p = > this . Controls . Remove ( p ) ) ;
2025-05-27 11:27:43 +08:00
int artistsPerColumn = 4 ;
2025-04-07 16:54:10 +08:00
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 ) ;
}
2025-04-23 11:46:55 +08:00
private void DisplaySongsInLanguage ( string language , Category category )
{
string sqlQuery ;
2025-04-07 16:54:10 +08:00
2025-04-23 11:46:55 +08:00
if ( category = = Category . NewSongs )
{
2025-07-03 18:15:21 +08:00
sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt(" NewSongLimit ", 100)};" ;
2025-04-23 11:46:55 +08:00
}
else if ( category = = Category . HotSongs )
{
2025-07-03 18:15:21 +08:00
sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY song_counts DESC LIMIT {Utils.Env.GetInt(" HotSongLimit ", 100)};" ;
2025-04-23 11:46:55 +08:00
}
else
{
ClearDisplay ( ) ;
displayLabel . Text = "無效的類別" ;
return ;
}
2025-04-07 16:54:10 +08:00
2025-04-23 11:46:55 +08:00
var songsInLanguage = PrimaryForm . Instance . SearchSongs_Mysql ( sqlQuery ) ;
2025-04-07 16:54:10 +08:00
2025-04-23 11:46:55 +08:00
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 ) ;
}
2025-05-21 10:29:18 +08:00
// 歌庫路徑調整
2025-04-07 16:54:10 +08:00
public void AddSongToPlaylist ( SongData songData )
{
try
{
2025-07-04 18:19:41 +08:00
//var filePath1 = songData.SongFilePathHost1;
//var filePath2 = songData.SongFilePathHost2;
2025-05-21 10:29:18 +08:00
// 之後還要設計成本地的資料夾位置
2025-07-04 18:19:41 +08:00
string pathToPlay = songData . FileName ;
if ( File . Exists ( pathToPlay ) )
2025-04-07 16:54:10 +08:00
{
bool wasEmpty = PrimaryForm . userRequestedSongs . Count = = 0 ;
PrimaryForm . userRequestedSongs . Add ( songData ) ;
PrimaryForm . playedSongsHistory . Add ( songData ) ;
PrimaryForm . playStates . Add ( wasEmpty ? PlayState . Playing : PlayState . NotPlayed ) ;
2025-05-08 10:09:14 +08:00
// 刷新頁面
2025-05-08 11:45:47 +08:00
2025-05-21 10:29:18 +08:00
if ( PrimaryForm . Instance . multiPagePanel . get_currentSongList ( ) = = PrimaryForm . playedSongsHistory )
2025-05-08 10:09:14 +08:00
PrimaryForm . Instance . multiPagePanel . LoadSongs ( PrimaryForm . Instance . currentSongList ) ;
2025-04-07 16:54:10 +08:00
if ( wasEmpty )
{
VideoPlayerForm . Instance . SetPlayingSongList ( PrimaryForm . userRequestedSongs ) ;
2025-05-21 10:29:18 +08:00
PrimaryForm . currentSongIndexInHistory + = 1 ;
2025-04-07 16:54:10 +08:00
}
VideoPlayerForm . Instance . UpdateNextSongFromPlaylist ( ) ;
PrimaryForm . PrintPlayingSongList ( ) ;
2025-05-14 11:18:01 +08:00
// 點播次數+1
PrimaryForm . Instance . AddSongCount ( songData . SongNumber ) ;
2025-04-07 16:54:10 +08:00
}
}
catch ( Exception ex )
{
Console . WriteLine ( "Error occurred: " + ex . Message ) ;
}
}
public void InsertSongToPlaylist ( SongData songData )
{
try
{
2025-05-08 10:09:14 +08:00
// 從 songData 中取得兩個可能的檔案路徑( 主機1與主機2)
2025-07-04 18:19:41 +08:00
var pathToPlay = songData . FileName ;
2025-05-08 10:09:14 +08:00
// 檢查兩個主機上的檔案是否皆不存在
2025-07-04 18:19:41 +08:00
if ( File . Exists ( pathToPlay ) )
2025-04-07 16:54:10 +08:00
{
2025-05-08 10:09:14 +08:00
// 若其中一個存在,就用第一個存在的那個檔案
2025-07-04 18:19:41 +08:00
2025-05-08 10:09:14 +08:00
Console . WriteLine ( "path to play" + pathToPlay ) ;
// 檢查目前使用者歌單是否為空
2025-04-07 16:54:10 +08:00
bool wasEmpty = PrimaryForm . userRequestedSongs . Count = = 0 ;
if ( wasEmpty )
{
2025-05-08 10:09:14 +08:00
// 若是空的,代表是第一首歌,直接加入並設為正在播放
PrimaryForm . userRequestedSongs . Add ( songData ) ; // 加入播放清單
VideoPlayerForm . Instance . SetPlayingSongList ( PrimaryForm . userRequestedSongs ) ; // 傳給播放器
PrimaryForm . playedSongsHistory . Add ( songData ) ; // 加入播放歷史
PrimaryForm . playStates . Add ( PlayState . Playing ) ; // 設定狀態為正在播放
PrimaryForm . currentSongIndexInHistory + = 1 ; // 歷史索引 +1
2025-04-07 16:54:10 +08:00
}
else if ( PrimaryForm . userRequestedSongs . Count = = 1 )
{
2025-05-08 10:09:14 +08:00
// 若清單中已有一首,插入新歌在 index 1 (即目前播放之後的位置)
2025-04-07 16:54:10 +08:00
PrimaryForm . userRequestedSongs . Insert ( 1 , songData ) ;
PrimaryForm . playedSongsHistory . Insert ( PrimaryForm . currentSongIndexInHistory + 1 , songData ) ;
2025-05-08 10:09:14 +08:00
PrimaryForm . playStates . Insert ( PrimaryForm . currentSongIndexInHistory + 1 , PlayState . NotPlayed ) ; // 尚未播放
2025-04-07 16:54:10 +08:00
}
else
{
2025-05-08 10:09:14 +08:00
// 若清單中超過一首,同樣插入在 index 1, 也是在當前歌曲之後的位置
2025-04-07 16:54:10 +08:00
PrimaryForm . userRequestedSongs . Insert ( 1 , songData ) ;
PrimaryForm . playedSongsHistory . Insert ( PrimaryForm . currentSongIndexInHistory + 1 , songData ) ;
PrimaryForm . playStates . Insert ( PrimaryForm . currentSongIndexInHistory + 1 , PlayState . NotPlayed ) ;
}
2025-05-08 10:09:14 +08:00
// 更新下一首即將播放的資訊(畫面或播放器邏輯用)
2025-04-07 16:54:10 +08:00
VideoPlayerForm . Instance . UpdateNextSongFromPlaylist ( ) ;
2025-05-08 10:09:14 +08:00
// 印出目前播放清單資訊到畫面或 console
2025-04-07 16:54:10 +08:00
PrimaryForm . PrintPlayingSongList ( ) ;
}
}
catch ( Exception ex )
{
2025-05-08 10:09:14 +08:00
// 捕捉所有例外並印出錯誤訊息(避免整個流程當掉)
2025-04-07 16:54:10 +08:00
Console . WriteLine ( "Error occurred: " + ex . Message ) ;
}
}
2025-05-27 11:27:43 +08:00
// 測試
2025-04-07 16:54:10 +08:00
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 < PictureBox > ( ) . 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 < PictureBox > ( ) . 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<PictureBox>().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 ( ) ;
}
}
}