From 7208d2e7dcdff874ce00ff05e2e8f9c785b0c451 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 14 Nov 2015 22:02:45 +1100 Subject: [PATCH] Make text input bar have a small background when empty, allowing entering colour codes directly into chat, add support for clicking on urls. --- ClassicalSharp/2D/Drawing/IDrawer2D.cs | 28 +++-- ClassicalSharp/2D/Screens/ChatScreen.cs | 66 ++++++---- ClassicalSharp/2D/Screens/HudScreen.cs | 39 +++--- .../2D/Widgets/BlockHotbarWidget.cs | 40 +++--- ClassicalSharp/2D/Widgets/ButtonWidget.cs | 22 ++-- .../2D/Widgets/Chat/AltTextInputWidget.cs | 26 ++-- .../2D/Widgets/Chat/ChatTextWidget.cs | 4 +- .../Widgets/Chat/TextGroupWidget.Formatter.cs | 115 ++++++++++++++++++ .../2D/Widgets/Chat/TextGroupWidget.cs | 60 ++++----- .../2D/Widgets/Chat/TextInputWidget.cs | 87 +++++++------ .../2D/Widgets/Menu/MenuInputWidget.cs | 34 +++--- ClassicalSharp/ClassicalSharp.csproj | 1 + ClassicalSharp/Game/ChatLog.cs | 4 +- 13 files changed, 341 insertions(+), 185 deletions(-) create mode 100644 ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.Formatter.cs diff --git a/ClassicalSharp/2D/Drawing/IDrawer2D.cs b/ClassicalSharp/2D/Drawing/IDrawer2D.cs index feda480df..91efe4cb4 100644 --- a/ClassicalSharp/2D/Drawing/IDrawer2D.cs +++ b/ClassicalSharp/2D/Drawing/IDrawer2D.cs @@ -64,7 +64,7 @@ namespace ClassicalSharp { public abstract void DrawBitmappedText( ref DrawTextArgs args, int x, int y ); /// Draws a string using the specified arguments, using the specified font or - /// the current bitmapped font depending on the 'useFont' argument, at the + /// the current bitmapped font depending on 'UseBitmappedChat', at the /// specified coordinates in the currently bound bitmap. public void DrawChatText( ref DrawTextArgs args, int windowX, int windowY ) { if( !UseBitmappedChat ) @@ -81,7 +81,7 @@ namespace ClassicalSharp { public abstract Size MeasureBitmappedSize( ref DrawTextArgs args ); /// Returns the size of a bitmap needed to contain the specified text with the given arguments, - /// when drawn with the specified font or the current bitmapped font depending on the 'useFont' argument. + /// when drawn with the specified font or the current bitmapped font depending on 'UseBitmappedChat'. public Size MeasureChatSize( ref DrawTextArgs args ) { return !UseBitmappedChat ? MeasureSize( ref args ) : MeasureBitmappedSize( ref args ); @@ -94,24 +94,28 @@ namespace ClassicalSharp { /// Draws the specified string from the arguments into a new bitmap, /// then creates a 2D texture with origin at the specified window coordinates. public Texture MakeTextTexture( ref DrawTextArgs args, int windowX, int windowY ) { - Size size = MeasureSize( ref args ); - if( parts.Count == 0 ) - return new Texture( -1, windowX, windowY, 0, 0, 1, 1 ); - return MakeTextureImpl( size, ref args, windowX, windowY, false ); + return MakeTextureImpl( ref args, windowX, windowY, false ); } /// Draws the specified string from the arguments into a new bitmap, /// using the current bitmap font, then creates a 2D texture with origin at the /// specified window coordinates. public Texture MakeBitmappedTextTexture( ref DrawTextArgs args, int windowX, int windowY ) { - Size size = MeasureBitmappedSize( ref args ); - if( parts.Count == 0 ) - return new Texture( -1, windowX, windowY, 0, 0, 1, 1 ); - return MakeTextureImpl( size, ref args, windowX, windowY, true ); + return MakeTextureImpl( ref args, windowX, windowY, true ); } - Texture MakeTextureImpl( Size size, ref DrawTextArgs args, - int windowX, int windowY, bool bitmapped ) { + /// Draws the specified string from the arguments into a new bitmap, + /// using the specified font or the current bitmapped font depending on 'UseBitmappedChat', + /// then creates a 2D texture with origin at the specified window coordinates. + public Texture MakeChatTextTexture( ref DrawTextArgs args, int windowX, int windowY ) { + return MakeTextureImpl( ref args, windowX, windowY, UseBitmappedChat ); + } + + Texture MakeTextureImpl( ref DrawTextArgs args, int windowX, int windowY, bool bitmapped ) { + Size size = bitmapped ? MeasureBitmappedSize( ref args ) : MeasureSize( ref args ); + if( parts.Count == 0 ) + return new Texture( -1, windowX, windowY, 0, 0, 1, 1 ); + using( Bitmap bmp = CreatePow2Bitmap( size ) ) { SetBitmap( bmp ); args.SkipPartsCheck = true; diff --git a/ClassicalSharp/2D/Screens/ChatScreen.cs b/ClassicalSharp/2D/Screens/ChatScreen.cs index ddee33af0..30762755e 100644 --- a/ClassicalSharp/2D/Screens/ChatScreen.cs +++ b/ClassicalSharp/2D/Screens/ChatScreen.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using OpenTK.Input; @@ -76,25 +77,27 @@ namespace ClassicalSharp { } } - Font chatFont, chatInputFont, announcementFont; + Font chatFont, chatInputFont, chatUnderlineFont, announcementFont; public override void Init() { - chatFont = new Font( "Arial", game.Chat.FontSize ); - chatInputFont = new Font( "Arial", game.Chat.FontSize, FontStyle.Bold ); + int fontSize = game.Chat.FontSize;//(int)(14 * Utils.GuiScale( game.Width, game.Height )); + chatFont = new Font( "Arial", fontSize ); + chatInputFont = new Font( "Arial", fontSize, FontStyle.Bold ); + chatUnderlineFont = new Font( "Arial", fontSize, FontStyle.Underline ); announcementFont = new Font( "Arial", 14 ); blockSize = (int)(40 * Utils.GuiScale( game.Width, game.Height )); textInput = new TextInputWidget( game, chatFont, chatInputFont ); textInput.YOffset = blockSize + 5; - status = new TextGroupWidget( game, 3, chatFont ); + status = new TextGroupWidget( game, 3, chatFont, chatUnderlineFont ); status.VerticalAnchor = Anchor.LeftOrTop; status.HorizontalAnchor = Anchor.BottomOrRight; status.Init(); - bottomRight = new TextGroupWidget( game, 3, chatFont ); + bottomRight = new TextGroupWidget( game, 3, chatFont, chatUnderlineFont ); bottomRight.VerticalAnchor = Anchor.BottomOrRight; bottomRight.HorizontalAnchor = Anchor.BottomOrRight; bottomRight.YOffset = blockSize * 3 / 2; bottomRight.Init(); - normalChat = new TextGroupWidget( game, chatLines, chatFont ); + normalChat = new TextGroupWidget( game, chatLines, chatFont, chatUnderlineFont ); normalChat.XOffset = 10; normalChat.YOffset = blockSize * 2 + 15; normalChat.HorizontalAnchor = Anchor.LeftOrTop; @@ -152,6 +155,7 @@ namespace ClassicalSharp { } chatFont.Dispose(); chatInputFont.Dispose(); + chatUnderlineFont.Dispose(); announcementFont.Dispose(); normalChat.Dispose(); @@ -280,21 +284,41 @@ namespace ClassicalSharp { return true; } - public override bool HandlesMouseClick( int mouseX, int mouseY, MouseButton button ) { - if( !HandlesAllInput ) return false; - if( normalChat.Bounds.Contains( mouseX, mouseY ) ) { - int height = normalChat.GetUsedHeight(); - int y = normalChat.Y + normalChat.Height - height; - if( new Rectangle( normalChat.X, y, normalChat.Width, height ).Contains( mouseX, mouseY ) ) { - string text = normalChat.GetSelected( mouseX, mouseY ); - if( text != null ) { - textInput.AppendText(text); - return true; - } - } - return false; - } - return textInput.HandlesMouseClick( mouseX, mouseY, button ); + public override bool HandlesMouseClick( int mouseX, int mouseY, MouseButton button ) { + if( !HandlesAllInput ) return false; + if( normalChat.Bounds.Contains( mouseX, mouseY ) ) { + int height = normalChat.GetUsedHeight(); + int y = normalChat.Y + normalChat.Height - height; + if( new Rectangle( normalChat.X, y, normalChat.Width, height ).Contains( mouseX, mouseY ) ) { + string text = normalChat.GetSelected( mouseX, mouseY ); + if( text != null ) { + if( Utils.IsUrlPrefix( text ) ) { + game.ShowWarning( new WarningScreen( + game, text, OpenUrl, AppendUrl, + "Are you sure you want to go to this url?", + text, + "Be careful - urls from strangers may link to websites that", + " may have viruses, or things you may not want to open/see." + ) ); + } + return true; + } + } + return false; + } + return textInput.HandlesMouseClick( mouseX, mouseY, button ); + } + + void OpenUrl( object metadata ) { + try { + Process.Start( (string)metadata ); + } catch( Exception ex ) { + ErrorHandler.LogError( "ChatScreen.OpenUrl", ex ); + } + } + + void AppendUrl( object metadata ) { + textInput.AppendText( (string)metadata ); } void ResetIndex() { diff --git a/ClassicalSharp/2D/Screens/HudScreen.cs b/ClassicalSharp/2D/Screens/HudScreen.cs index 3e3ba6d77..488405399 100644 --- a/ClassicalSharp/2D/Screens/HudScreen.cs +++ b/ClassicalSharp/2D/Screens/HudScreen.cs @@ -72,20 +72,17 @@ namespace ClassicalSharp { } public void LoseFocus() { - if( playerList != null ) { + if( playerList != null ) playerList.Dispose(); - } if( !game.CursorVisible ) game.CursorVisible = true; } public override void OnResize( int oldWidth, int oldHeight, int width, int height ) { - chat.OnResize( oldWidth, oldHeight, width, height ); - hotbar.OnResize( oldWidth, oldHeight, width, height ); - if( playerList != null ) { - int deltaX = CalcDelta( width, oldWidth, Anchor.Centre ); - playerList.MoveTo( playerList.X + deltaX, height / 4 ); - } + PlayerListWidget widget = playerList; + game.RefreshHud(); + if( widget != null ) + CreatePlayerListWidget(); } public override void Init() { @@ -113,22 +110,26 @@ namespace ClassicalSharp { public override bool HandlesKeyDown( Key key ) { if( key == game.Mapping( KeyBinding.PlayerList ) ) { - if( playerList == null ) { - if( game.Network.UsingExtPlayerList ) { - playerList = new ExtPlayerListWidget( game, playerFont ); - } else { - playerList = new NormalPlayerListWidget( game, playerFont ); - } - playerList.Init(); - playerList.MoveTo( playerList.X, game.Height / 4 ); - } - } - if( chat.HandlesKeyDown( key ) ) { + if( playerList == null ) + CreatePlayerListWidget(); return true; } + + if( chat.HandlesKeyDown( key ) ) + return true; return hotbar.HandlesKeyDown( key ); } + void CreatePlayerListWidget() { + if( game.Network.UsingExtPlayerList ) { + playerList = new ExtPlayerListWidget( game, playerFont ); + } else { + playerList = new NormalPlayerListWidget( game, playerFont ); + } + playerList.Init(); + playerList.MoveTo( playerList.X, game.Height / 4 ); + } + public override bool HandlesKeyUp( Key key ) { if( key == game.Mapping( KeyBinding.PlayerList ) ) { if( playerList != null ) { diff --git a/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs b/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs index 93538e1ef..c93ff8723 100644 --- a/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs +++ b/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs @@ -40,21 +40,21 @@ namespace ClassicalSharp { MakeBackgroundTexture( width ); MakeSelectionTexture(); } - + public override void Render( double delta ) { graphicsApi.Texturing = true; background.Render( graphicsApi ); - graphicsApi.BindTexture( game.TerrainAtlas.TexId ); + graphicsApi.BindTexture( game.TerrainAtlas.TexId ); graphicsApi.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b ); for( int i = 0; i < hotbarCount; i++ ) { int x = X + i * blockSize; - IsometricBlockDrawer.Draw( game, (byte)game.Inventory.Hotbar[i], blockSize / 2 - borderSize - 2, + IsometricBlockDrawer.Draw( game, (byte)game.Inventory.Hotbar[i], blockSize / 2 - borderSize - 2, x + 1 + blockSize / 2, game.Height - blockSize / 2 ); if( i == game.Inventory.HeldBlockIndex ) selectedBlock.X1 = x; - } - + } + selectedBlock.Render( graphicsApi ); graphicsApi.Texturing = false; } @@ -73,27 +73,27 @@ namespace ClassicalSharp { void MakeBackgroundTexture( int width ) { Size size = new Size( width, blockSize ); - using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) { - using( IDrawer2D drawer = game.Drawer2D ) { - drawer.SetBitmap( bmp ); - drawer.Clear( backCol ); - for( int xx = 0; xx < hotbarCount; xx++ ) { - drawer.DrawRectBounds( outlineCol, borderSize, xx * blockSize, - 0, blockSize, blockSize ); - } - background = drawer.Make2DTexture( bmp, size, X, Y ); + using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) + using( IDrawer2D drawer = game.Drawer2D ) + { + drawer.SetBitmap( bmp ); + drawer.Clear( backCol ); + for( int xx = 0; xx < hotbarCount; xx++ ) { + drawer.DrawRectBounds( outlineCol, borderSize, xx * blockSize, + 0, blockSize, blockSize ); } + background = drawer.Make2DTexture( bmp, size, X, Y ); } } void MakeSelectionTexture() { Size size = new Size( blockSize, blockSize ); - using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) { - using( IDrawer2D drawer = game.Drawer2D ) { - drawer.SetBitmap( bmp ); - drawer.DrawRectBounds( selCol, borderSize, 0, 0, blockSize, blockSize ); - selectedBlock = drawer.Make2DTexture( bmp, size, 0, Y ); - } + using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) + using( IDrawer2D drawer = game.Drawer2D ) + { + drawer.SetBitmap( bmp ); + drawer.DrawRectBounds( selCol, borderSize, 0, 0, blockSize, blockSize ); + selectedBlock = drawer.Make2DTexture( bmp, size, 0, Y ); } } } diff --git a/ClassicalSharp/2D/Widgets/ButtonWidget.cs b/ClassicalSharp/2D/Widgets/ButtonWidget.cs index b5c597317..8410a70c0 100644 --- a/ClassicalSharp/2D/Widgets/ButtonWidget.cs +++ b/ClassicalSharp/2D/Widgets/ButtonWidget.cs @@ -88,17 +88,17 @@ namespace ClassicalSharp { const int borderSize = 3; // 1 px for base border, 2 px for shadow, 1 px for offset text size.Width += borderSize; size.Height += borderSize; - using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) { - using( IDrawer2D drawer = game.Drawer2D ) { - drawer.SetBitmap( bmp ); - drawer.DrawRoundedRect( shadowCol, 3, IDrawer2D.Offset, IDrawer2D.Offset, - baseSize.Width, baseSize.Height ); - drawer.DrawRoundedRect( boxCol, 3, 0, 0, baseSize.Width, baseSize.Height ); - - args.SkipPartsCheck = true; - drawer.DrawText( ref args, 1 + xOffset / 2, 1 + yOffset / 2 ); - texture = drawer.Make2DTexture( bmp, size, 0, 0 ); - } + using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) + using( IDrawer2D drawer = game.Drawer2D ) + { + drawer.SetBitmap( bmp ); + drawer.DrawRoundedRect( shadowCol, 3, IDrawer2D.Offset, IDrawer2D.Offset, + baseSize.Width, baseSize.Height ); + drawer.DrawRoundedRect( boxCol, 3, 0, 0, baseSize.Width, baseSize.Height ); + + args.SkipPartsCheck = true; + drawer.DrawText( ref args, 1 + xOffset / 2, 1 + yOffset / 2 ); + texture = drawer.Make2DTexture( bmp, size, 0, 0 ); } } } diff --git a/ClassicalSharp/2D/Widgets/Chat/AltTextInputWidget.cs b/ClassicalSharp/2D/Widgets/Chat/AltTextInputWidget.cs index 6f011fbe9..2467b36e3 100644 --- a/ClassicalSharp/2D/Widgets/Chat/AltTextInputWidget.cs +++ b/ClassicalSharp/2D/Widgets/Chat/AltTextInputWidget.cs @@ -39,7 +39,7 @@ namespace ClassicalSharp { public void Redraw() { Make( elements[selectedIndex], font ); Width = texture.Width; - Height = texture.Height; + Height = texture.Height; } unsafe void Make( Element e, Font font ) { @@ -49,16 +49,16 @@ namespace ClassicalSharp { int titleWidth = MeasureTitles( font ), titleHeight = elements[0].TitleSize.Height; Size size = new Size( Math.Max( bodySize.Width, titleWidth ), bodySize.Height + titleHeight ); - using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) { - using( IDrawer2D drawer = game.Drawer2D ) { - drawer.SetBitmap( bmp ); - DrawTitles( drawer, font ); - drawer.Clear( new FastColour( 30, 30, 30, 200 ), 0, titleHeight, - size.Width, bodySize.Height ); - - DrawContent( drawer, font, e, titleHeight ); - texture = drawer.Make2DTexture( bmp, size, X, Y ); - } + using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) + using( IDrawer2D drawer = game.Drawer2D ) + { + drawer.SetBitmap( bmp ); + DrawTitles( drawer, font ); + drawer.Clear( new FastColour( 30, 30, 30, 200 ), 0, titleHeight, + size.Width, bodySize.Height ); + + DrawContent( drawer, font, e, titleHeight ); + texture = drawer.Make2DTexture( bmp, size, X, Y ); } } @@ -96,8 +96,8 @@ namespace ClassicalSharp { if( index < e.Contents.Length ) { if( selectedIndex == 0 ) { // TODO: need to insert characters that don't affect caret index, adjust caret colour - //parent.AppendChar( e.Contents[index * e.CharsPerItem] ); - //parent.AppendChar( e.Contents[index * e.CharsPerItem + 1] ); + parent.AppendChar( e.Contents[index * e.CharsPerItem] ); + parent.AppendChar( e.Contents[index * e.CharsPerItem + 1] ); } else { parent.AppendChar( e.Contents[index] ); } diff --git a/ClassicalSharp/2D/Widgets/Chat/ChatTextWidget.cs b/ClassicalSharp/2D/Widgets/Chat/ChatTextWidget.cs index a32a2568d..0dc7c9bf3 100644 --- a/ClassicalSharp/2D/Widgets/Chat/ChatTextWidget.cs +++ b/ClassicalSharp/2D/Widgets/Chat/ChatTextWidget.cs @@ -32,9 +32,7 @@ namespace ClassicalSharp { Height = defaultHeight; } else { DrawTextArgs args = new DrawTextArgs( text, font, true ); - texture = game.Drawer2D.UseBitmappedChat ? - game.Drawer2D.MakeBitmappedTextTexture( ref args, 0, 0 ) : - game.Drawer2D.MakeTextTexture( ref args, 0, 0 ); + texture = game.Drawer2D.MakeChatTextTexture( ref args, 0, 0 ); X = texture.X1 = CalcOffset( game.Width, texture.Width, XOffset, HorizontalAnchor ); Y = texture.Y1 = CalcOffset( game.Height, texture.Height, YOffset, VerticalAnchor ); diff --git a/ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.Formatter.cs b/ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.Formatter.cs new file mode 100644 index 000000000..af1038d50 --- /dev/null +++ b/ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.Formatter.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace ClassicalSharp { + + public sealed partial class TextGroupWidget : Widget { + + public void SetText( int index, string text ) { + graphicsApi.DeleteTexture( ref Textures[index] ); + DrawTextArgs args = new DrawTextArgs( text, font, true ); + Font underlineFont = new Font( font, FontStyle.Underline ); + urlBounds[index] = null; + + if( !String.IsNullOrEmpty( text ) ) { + Texture tex = NextToken( text, 0 ) == -1 ? DrawSimple( ref args ) : + DrawAdvanced( ref args, index, text ); + tex.X1 = CalcOffset( game.Width, tex.Width, XOffset, HorizontalAnchor ); + tex.Y1 = CalcY( index, tex.Height ); + Textures[index] = tex; + lines[index] = text; + } else { + Textures[index] = new Texture( -1, 0, 0, 0, defaultHeight, 0, 0 ); + lines[index] = null; + } + UpdateDimensions(); + } + + Texture DrawSimple( ref DrawTextArgs args ) { + return game.Drawer2D.MakeChatTextTexture( ref args, 0, 0 ); + } + + Texture DrawAdvanced( ref DrawTextArgs args, int index, string text ) { + string[] items = Split( index, text ); + Size total = Size.Empty; + Size[] partSizes = new Size[items.Length]; + + for( int i = 0; i < items.Length; i++ ) { + args.Text = items[i]; + args.Font = (i & 1) == 0 ? font : underlineFont; + partSizes[i] = game.Drawer2D.MeasureChatSize( ref args ); + total.Height = Math.Max( partSizes[i].Height, total.Height ); + total.Width += partSizes[i].Width; + } + + using( IDrawer2D drawer = game.Drawer2D ) + using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( total ) ) + { + drawer.SetBitmap( bmp ); + int x = 0; + + for( int i = 0; i < items.Length; i++ ) { + args.Text = items[i]; + args.Font = (i & 1) == 0 ? font : underlineFont; + Size size = partSizes[i]; + + drawer.DrawChatText( ref args, x, 0 ); + urlBounds[index][i].X = x; + urlBounds[index][i].Width = size.Width; + x += size.Width; + } + return drawer.Make2DTexture( bmp, total, 0, 0 ); + } + } + + string[] Split( int index, string line ) { + int start = 0, lastEnd = 0, count = 0; + string[] items = new string[GetTokensCount( line )]; + Rectangle[] parts = new Rectangle[items.Length]; + + while( (start = NextToken( line, start )) >= 0 ) { + int nextEnd = line.IndexOf( ' ', start ); + if( nextEnd == -1 ) + nextEnd = line.Length; + + parts[count].Y = lastEnd << 12 | (start - lastEnd); + items[count++] = line.Substring( lastEnd, start - lastEnd ); // word bit + parts[count].Y = start << 12 | (nextEnd - start); + items[count++] = line.Substring( start, nextEnd - start ); // url bit + start = nextEnd; + lastEnd = nextEnd; + } + + if( lastEnd < line.Length ) { + parts[count].Y = lastEnd << 12 | (line.Length - lastEnd); + items[count++] = line.Substring( lastEnd, line.Length - lastEnd ); + } + urlBounds[index] = parts; + return items; + } + + int NextToken( string line, int start ) { + int nextHttp = line.IndexOf( "http://", start ); + int nextHttps = line.IndexOf( "https://", start ); + return nextHttp == -1 ? nextHttps : nextHttp; + } + + int GetTokensCount( string line ) { + int start = 0, lastEnd = 0, count = 0; + while( (start = NextToken( line, start )) >= 0 ) { + int nextEnd = line.IndexOf( ' ', start ); + if( nextEnd == -1 ) + nextEnd = line.Length; + + start = nextEnd; + lastEnd = nextEnd; + count += 2; + } + + if( lastEnd < line.Length ) + count++; + return count; + } + } +} \ No newline at end of file diff --git a/ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.cs b/ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.cs index 3e8d2ebe9..c15cb26ff 100644 --- a/ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.cs +++ b/ClassicalSharp/2D/Widgets/Chat/TextGroupWidget.cs @@ -4,22 +4,26 @@ using System.Drawing; namespace ClassicalSharp { - public sealed class TextGroupWidget : Widget { + public sealed partial class TextGroupWidget : Widget { - public TextGroupWidget( Game game, int elementsCount, Font font ) : base( game ) { + public TextGroupWidget( Game game, int elementsCount, + Font font, Font underlineFont ) : base( game ) { ElementsCount = elementsCount; this.font = font; + this.underlineFont = underlineFont; } public Texture[] Textures; string[] lines; + Rectangle[][] urlBounds; int ElementsCount, defaultHeight; public int XOffset = 0, YOffset = 0; - readonly Font font; + readonly Font font, underlineFont; public override void Init() { Textures = new Texture[ElementsCount]; lines = new string[ElementsCount]; + urlBounds = new Rectangle[ElementsCount][]; DrawTextArgs args = new DrawTextArgs( "I", font, true ); defaultHeight = game.Drawer2D.MeasureChatSize( ref args ).Height; @@ -28,26 +32,6 @@ namespace ClassicalSharp { UpdateDimensions(); } - public void SetText( int index, string text ) { - graphicsApi.DeleteTexture( ref Textures[index] ); - DrawTextArgs args = new DrawTextArgs( text, font, true ); - - if( !String.IsNullOrEmpty( text ) ) { - Texture tex = game.Drawer2D.UseBitmappedChat ? - game.Drawer2D.MakeBitmappedTextTexture( ref args, 0, 0 ) : - game.Drawer2D.MakeTextTexture( ref args, 0, 0 ); - - tex.X1 = CalcOffset( game.Width, tex.Width, XOffset, HorizontalAnchor ); - tex.Y1 = CalcY( index, tex.Height ); - Textures[index] = tex; - lines[index] = text; - } else { - Textures[index] = new Texture( -1, 0, 0, 0, defaultHeight, 0, 0 ); - lines[index] = null; - } - UpdateDimensions(); - } - public void PushUpAndReplaceLast( string text ) { int y = Y; graphicsApi.DeleteTexture( ref Textures[0] ); @@ -81,7 +65,7 @@ namespace ClassicalSharp { y -= newHeight; for( int i = 0; i < index; i++ ) { Textures[i].Y1 -= deltaY; - } + } } return y; } @@ -96,11 +80,11 @@ namespace ClassicalSharp { } void UpdateDimensions() { - Width = 0; + Width = 0; Height = 0; for( int i = 0; i < Textures.Length; i++ ) { Width = Math.Max( Width, Textures[i].Width ); - Height += Textures[i].Height; + Height += Textures[i].Height; } X = CalcOffset( game.Width, Width, XOffset, HorizontalAnchor ); @@ -131,9 +115,27 @@ namespace ClassicalSharp { public string GetSelected( int mouseX, int mouseY ) { for( int i = 0; i < Textures.Length; i++ ) { - if( Textures[i].IsValid && - Textures[i].Bounds.Contains( mouseX, mouseY ) ) - return lines[i]; + Texture tex = Textures[i]; + if( tex.IsValid && tex.Bounds.Contains( mouseX, mouseY ) ) { + return GetUrl( i, mouseX ) ?? lines[i]; + } + } + return null; + } + + string GetUrl( int index, int mouseX ) { + Rectangle[] partBounds = urlBounds[index]; + if( partBounds == null ) + return null; + Texture tex = Textures[index]; + mouseX -= tex.X1; + string text = lines[index]; + + for( int i = 1; i < partBounds.Length; i += 2 ) { + if( mouseX >= partBounds[i].Left && mouseX < partBounds[i].Right ) { + int packed = partBounds[i].Y; + return text.Substring( packed >> 12, packed & 0xFFF ); + } } return null; } diff --git a/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.cs b/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.cs index 44f14ed53..c3c7557d6 100644 --- a/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.cs +++ b/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.cs @@ -15,21 +15,24 @@ namespace ClassicalSharp { VerticalAnchor = Anchor.BottomOrRight; typingLogPos = game.Chat.InputLog.Count; // Index of newest entry + 1. + chatInputText = new WrappableStringBuffer( len ); + DrawTextArgs args = new DrawTextArgs( "A", boldFont, true ); + Size defSize = game.Drawer2D.MeasureChatSize( ref args ); + defaultWidth = defSize.Width; defaultHeight = defSize.Height; + Width = defaultWidth; Height = defaultHeight; + this.font = font; this.boldFont = boldFont; - chatInputText = new WrappableStringBuffer( len ); - DrawTextArgs args = new DrawTextArgs( "_", boldFont, false ); - defaultHeight = game.Drawer2D.MeasureChatSize( ref args ).Height; - Height = defaultHeight; altText = new AltTextInputWidget( game, font, boldFont, this ); altText.Init(); } public int RealHeight { get { return Height + altText.Height; } } - Texture chatInputTexture, caretTexture; + Texture inputTex, caretTex; int caretPos = -1, typingLogPos = 0; - public int YOffset, defaultHeight; + public int YOffset; + int defaultWidth, defaultHeight; internal WrappableStringBuffer chatInputText; readonly Font font, boldFont; @@ -38,8 +41,19 @@ namespace ClassicalSharp { FastColour caretCol; static FastColour backColour = new FastColour( 60, 60, 60, 200 ); public override void Render( double delta ) { - chatInputTexture.Render( graphicsApi ); - caretTexture.Render( graphicsApi, caretCol ); + graphicsApi.Texturing = false; + int y = Y, x = X; + for( int i = 0; i < sizes.Length; i++ ) { + int offset = (caretTex.Y1 == y) ? defaultWidth : 0; + graphicsApi.Draw2DQuad( x + 5, y, sizes[i].Width + offset, sizes[i].Height, backColour ); + y += sizes[i].Height; + } + if( sizes.Length == 0 || sizes[0] == Size.Empty ) + graphicsApi.Draw2DQuad( x + 5, y, defaultWidth, defaultHeight, backColour ); + graphicsApi.Texturing = true; + + inputTex.Render( graphicsApi ); + caretTex.Render( graphicsApi, caretCol ); if( altText.Active ) altText.Render( delta ); } @@ -51,14 +65,12 @@ namespace ClassicalSharp { public override void Init() { X = 5; - DrawTextArgs args = new DrawTextArgs( "_", boldFont, false ); - caretTexture = game.Drawer2D.UseBitmappedChat ? - game.Drawer2D.MakeBitmappedTextTexture( ref args, 0, 0 ) : - game.Drawer2D.MakeTextTexture( ref args, 0, 0 ); + DrawTextArgs args = new DrawTextArgs( "_", boldFont, true ); + caretTex = game.Drawer2D.MakeChatTextTexture( ref args, 0, 0 ); chatInputText.WordWrap( ref parts, ref partLens, 64 ); maxWidth = 0; - args = new DrawTextArgs( null, font, false ); + args = new DrawTextArgs( null, font, true ); for( int i = 0; i < lines; i++ ) { args.Text = parts[i]; sizes[i] = game.Drawer2D.MeasureChatSize( ref args ); @@ -84,23 +96,23 @@ namespace ClassicalSharp { if( indexX == -1 ) indexX = partLens[indexY]; if( indexX == 64 ) { - caretTexture.X1 = 10 + sizes[indexY].Width; - sizes[indexY].Width += caretTexture.Width; + caretTex.X1 = 10 + sizes[indexY].Width; + sizes[indexY].Width += caretTex.Width; maxWidth = Math.Max( maxWidth, sizes[indexY].Width ); - caretTexture.Y1 = sizes[0].Height * indexY; + caretTex.Y1 = sizes[0].Height * indexY; caretCol = nextCaretCol; } else { args.Text = parts[indexY].Substring( 0, indexX ); Size trimmedSize = game.Drawer2D.MeasureChatSize( ref args ); - caretTexture.X1 = 10 + trimmedSize.Width; + caretTex.X1 = 10 + trimmedSize.Width; string line = parts[indexY]; args.Text = indexX < line.Length ? new String( line[indexX], 1 ) : " "; Size charSize = game.Drawer2D.MeasureChatSize( ref args ); - caretTexture.Width = charSize.Width; + caretTex.Width = charSize.Width; - caretTexture.Y1 = sizes[0].Height * indexY; + caretTex.Y1 = sizes[0].Height * indexY; caretCol = normalCaretCol; } DrawString(); @@ -115,40 +127,39 @@ namespace ClassicalSharp { Size size = new Size( maxWidth, totalHeight ); int realHeight = 0; - using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) { - using( IDrawer2D drawer = game.Drawer2D ) { - drawer.SetBitmap( bmp ); - DrawTextArgs args = new DrawTextArgs( null, font, false ); + using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) + using( IDrawer2D drawer = game.Drawer2D ) + { + drawer.SetBitmap( bmp ); + DrawTextArgs args = new DrawTextArgs( null, font, true ); + + for( int i = 0; i < parts.Length; i++ ) { + if( parts[i] == null ) break; + args.Text = parts[i]; - for( int i = 0; i < parts.Length; i++ ) { - if( parts[i] == null ) break; - args.Text = parts[i]; - - drawer.Clear( backColour, 0, realHeight, sizes[i].Width, sizes[i].Height ); - drawer.DrawChatText( ref args, 0, realHeight ); - realHeight += sizes[i].Height; - } - chatInputTexture = drawer.Make2DTexture( bmp, size, 10, 0 ); + drawer.DrawChatText( ref args, 0, realHeight ); + realHeight += sizes[i].Height; } + inputTex = drawer.Make2DTexture( bmp, size, 10, 0 ); } Height = realHeight == 0 ? defaultHeight : realHeight; Y = game.Height - Height - YOffset; - chatInputTexture.Y1 = Y; - caretTexture.Y1 += Y; + inputTex.Y1 = Y; + caretTex.Y1 += Y; Width = size.Width; } public override void Dispose() { - graphicsApi.DeleteTexture( ref caretTexture ); - graphicsApi.DeleteTexture( ref chatInputTexture ); + graphicsApi.DeleteTexture( ref caretTex ); + graphicsApi.DeleteTexture( ref inputTex ); } public override void MoveTo( int newX, int newY ) { int diffX = newX - X, diffY = newY - Y; X = newX; Y = newY; - caretTexture.Y1 += diffY; - chatInputTexture.Y1 += diffY; + caretTex.Y1 += diffY; + inputTex.Y1 += diffY; altText.texture.Y1 = game.Height - (YOffset + Height + altText.texture.Height); altText.Y = altText.texture.Y1; diff --git a/ClassicalSharp/2D/Widgets/Menu/MenuInputWidget.cs b/ClassicalSharp/2D/Widgets/Menu/MenuInputWidget.cs index 0b26c6d33..902dc401a 100644 --- a/ClassicalSharp/2D/Widgets/Menu/MenuInputWidget.cs +++ b/ClassicalSharp/2D/Widgets/Menu/MenuInputWidget.cs @@ -60,23 +60,23 @@ namespace ClassicalSharp { Size size = new Size( Math.Max( textSize.Width, DesiredMaxWidth ), Math.Max( textSize.Height, DesiredMaxHeight ) ); - using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) { - using( IDrawer2D drawer = game.Drawer2D ) { - drawer.SetBitmap( bmp ); - drawer.DrawRect( backColour, 0, 0, size.Width, size.Height ); - args.SkipPartsCheck = true; - drawer.DrawText( ref args, 0, 0 ); - - args.Text = Validator.Range; - args.SkipPartsCheck = false; - Size hintSize = drawer.MeasureSize( ref args ); - - args.SkipPartsCheck = true; - int hintX = size.Width - hintSize.Width; - if( textSize.Width < hintX ) - drawer.DrawText( ref args, hintX, 0 ); - chatInputTexture = drawer.Make2DTexture( bmp, size, 0, 0 ); - } + using( Bitmap bmp = IDrawer2D.CreatePow2Bitmap( size ) ) + using( IDrawer2D drawer = game.Drawer2D ) + { + drawer.SetBitmap( bmp ); + drawer.DrawRect( backColour, 0, 0, size.Width, size.Height ); + args.SkipPartsCheck = true; + drawer.DrawText( ref args, 0, 0 ); + + args.Text = Validator.Range; + args.SkipPartsCheck = false; + Size hintSize = drawer.MeasureSize( ref args ); + + args.SkipPartsCheck = true; + int hintX = size.Width - hintSize.Width; + if( textSize.Width < hintX ) + drawer.DrawText( ref args, hintX, 0 ); + chatInputTexture = drawer.Make2DTexture( bmp, size, 0, 0 ); } X = CalcOffset( game.Width, size.Width, XOffset, HorizontalAnchor ); diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index c3055eb77..f795bf055 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -109,6 +109,7 @@ + diff --git a/ClassicalSharp/Game/ChatLog.cs b/ClassicalSharp/Game/ChatLog.cs index 00f2d8502..753103b7a 100644 --- a/ClassicalSharp/Game/ChatLog.cs +++ b/ClassicalSharp/Game/ChatLog.cs @@ -7,8 +7,8 @@ namespace ClassicalSharp { public sealed class ChatLog : IDisposable { - public ChatLine Status1, Status2, Status3, BottomRight1, - BottomRight2, BottomRight3, Announcement; + public ChatLine Status1, Status2, Status3, BottomRight1 = "F", + BottomRight2 = "G", BottomRight3 = "H", Announcement; Game game; public ChatLog( Game game ) {