Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjusted text v-alignment on labels, checkbox and buttons #16592

Merged
merged 9 commits into from Jun 21, 2019
37 changes: 37 additions & 0 deletions OpenRA.Game/Fonts.cs
@@ -0,0 +1,37 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion

using System.Collections.Generic;

namespace OpenRA
{
public class FontData
{
public readonly string Font;
public readonly int Size;
public readonly int Ascender;
}

public class Fonts : IGlobalModData
{
[FieldLoader.LoadUsing("LoadFonts")]
public readonly Dictionary<string, FontData> FontList;

static object LoadFonts(MiniYaml y)
{
var ret = new Dictionary<string, FontData>();
foreach (var node in y.Nodes)
ret.Add(node.Key, FieldLoader.Load<FontData>(node.Value));

return ret;
}
}
}
4 changes: 1 addition & 3 deletions OpenRA.Game/Graphics/PlatformInterfaces.cs
Expand Up @@ -133,9 +133,7 @@ public enum WindowMode

public interface IFont : IDisposable
{
FontGlyph CreateGlyph(char c);
void SetSize(int size, float deviceScale);
int Height { get; }
FontGlyph CreateGlyph(char c, int size, float deviceScale);
}

public struct FontGlyph
Expand Down
11 changes: 3 additions & 8 deletions OpenRA.Game/Graphics/SpriteFont.cs
Expand Up @@ -28,7 +28,7 @@ public sealed class SpriteFont : IDisposable

float deviceScale;

public SpriteFont(string name, byte[] data, int size, float scale, SheetBuilder builder)
public SpriteFont(string name, byte[] data, int size, int ascender, float scale, SheetBuilder builder)
{
if (builder.Type != SheetType.BGRA)
throw new ArgumentException("The sheet builder must create BGRA sheets.", "builder");
Expand All @@ -38,7 +38,6 @@ public SpriteFont(string name, byte[] data, int size, float scale, SheetBuilder
this.builder = builder;

font = Game.Renderer.CreateFont(data);
font.SetSize(size, deviceScale);

glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);

Expand All @@ -49,17 +48,13 @@ public SpriteFont(string name, byte[] data, int size, float scale, SheetBuilder
if (size <= 24)
PrecacheColor(Color.White, name);

TopOffset = size - font.Height;
TopOffset = size - ascender;
}

public void SetScale(float scale)
{
deviceScale = scale;

font.SetSize(size, scale);
glyphs.Clear();

TopOffset = size - font.Height;
}

void PrecacheColor(Color c, string name)
Expand Down Expand Up @@ -139,7 +134,7 @@ public int2 Measure(string text)

GlyphInfo CreateGlyph(Pair<char, Color> c)
{
var glyph = font.CreateGlyph(c.First);
var glyph = font.CreateGlyph(c.First, size, deviceScale);

if (glyph.Data == null)
{
Expand Down
9 changes: 1 addition & 8 deletions OpenRA.Game/Manifest.cs
Expand Up @@ -66,7 +66,6 @@ public class Manifest
public readonly IReadOnlyDictionary<string, string> Packages;
public readonly IReadOnlyDictionary<string, string> MapFolders;
public readonly MiniYaml LoadScreen;
public readonly Dictionary<string, Pair<string, int>> Fonts;

public readonly string[] SoundFormats = { };
public readonly string[] SpriteFormats = { };
Expand All @@ -77,7 +76,7 @@ public class Manifest
"Metadata", "Folders", "MapFolders", "Packages", "Rules",
"Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons",
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", "Hotkeys",
"ServerTraits", "LoadScreen", "Fonts", "SupportsMapsFrom", "SoundFormats", "SpriteFormats",
"ServerTraits", "LoadScreen", "SupportsMapsFrom", "SoundFormats", "SpriteFormats",
"RequiresMods", "PackageFormats"
};

Expand Down Expand Up @@ -123,12 +122,6 @@ public Manifest(string modId, IReadOnlyPackage package)
if (!yaml.TryGetValue("LoadScreen", out LoadScreen))
throw new InvalidDataException("`LoadScreen` section is not defined.");

Fonts = yaml["Fonts"].ToDictionary(my =>
{
var nd = my.ToDictionary();
return Pair.New(nd["Font"].Value, Exts.ParseIntegerInvariant(nd["Size"].Value));
});

// Allow inherited mods to import parent maps.
var compat = new List<string> { Id };

Expand Down
6 changes: 3 additions & 3 deletions OpenRA.Game/Renderer.cs
Expand Up @@ -90,9 +90,9 @@ public void InitializeFonts(ModData modData)
if (fontSheetBuilder != null)
fontSheetBuilder.Dispose();
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, 512);
Fonts = modData.Manifest.Fonts.ToDictionary(x => x.Key,
x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(),
x.Value.Second, Window.WindowScale, fontSheetBuilder)).AsReadOnly();
Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key,
x => new SpriteFont(x.Value.Font, modData.DefaultFileSystem.Open(x.Value.Font).ReadAllBytes(),
x.Value.Size, x.Value.Ascender, Window.WindowScale, fontSheetBuilder)).AsReadOnly();
}

Window.OnWindowScaleChanged += (before, after) =>
Expand Down
Expand Up @@ -41,7 +41,7 @@ public class WithTextControlGroupDecorationInfo : ITraitInfo, IRulesetLoaded, Re

void IRulesetLoaded<ActorInfo>.RulesetLoaded(Ruleset rules, ActorInfo info)
{
if (!Game.ModData.Manifest.Fonts.ContainsKey(Font))
if (!Game.ModData.Manifest.Get<Fonts>().FontList.ContainsKey(Font))
throw new YamlException("Font '{0}' is not listed in the mod.yaml's Fonts section".F(Font));
}

Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Traits/Render/WithTextDecoration.cs
Expand Up @@ -50,7 +50,7 @@ public class WithTextDecorationInfo : ConditionalTraitInfo, Requires<IDecoration

public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
if (!Game.ModData.Manifest.Fonts.ContainsKey(Font))
if (!Game.ModData.Manifest.Get<Fonts>().FontList.ContainsKey(Font))
throw new YamlException("Font '{0}' is not listed in the mod.yaml's Fonts section".F(Font));

base.RulesetLoaded(rules, ai);
Expand Down
12 changes: 6 additions & 6 deletions OpenRA.Mods.Common/Widgets/ButtonWidget.cs
Expand Up @@ -11,6 +11,7 @@

using System;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Widgets;

Expand All @@ -33,7 +34,6 @@ public class ButtonWidget : Widget
public string Background = "button";
public bool Depressed = false;
public int VisualHeight = ChromeMetrics.Get<int>("ButtonDepth");
public int BaseLine = ChromeMetrics.Get<int>("ButtonBaseLine");
public string Font = ChromeMetrics.Get<string>("ButtonFont");
public Color TextColor = ChromeMetrics.Get<Color>("ButtonTextColor");
public Color TextColorDisabled = ChromeMetrics.Get<Color>("ButtonTextColorDisabled");
Expand Down Expand Up @@ -102,7 +102,6 @@ protected ButtonWidget(ButtonWidget other)
LeftMargin = other.LeftMargin;
RightMargin = other.RightMargin;
Font = other.Font;
BaseLine = other.BaseLine;
TextColor = other.TextColor;
TextColorDisabled = other.TextColorDisabled;
Contrast = other.Contrast;
Expand Down Expand Up @@ -243,10 +242,10 @@ public override void Draw()
var colordisabled = GetColorDisabled();
var bgDark = GetContrastColorDark();
var bgLight = GetContrastColorLight();
var textSize = font.Measure(text);

var stateOffset = Depressed ? new int2(VisualHeight, VisualHeight) : new int2(0, 0);

var position = GetTextPosition(textSize, rb);
var position = GetTextPosition(text, font, rb);

var hover = Ui.MouseOverWidget == this || Children.Any(c => c == Ui.MouseOverWidget);
DrawBackground(rb, disabled, Depressed, hover, highlighted);
Expand All @@ -260,9 +259,10 @@ public override void Draw()
disabled ? colordisabled : color);
}

int2 GetTextPosition(int2 textSize, Rectangle rb)
int2 GetTextPosition(string text, SpriteFont font, Rectangle rb)
{
var y = rb.Y - BaseLine + (Bounds.Height - textSize.Y) / 2;
var textSize = font.Measure(text);
var y = rb.Y + (Bounds.Height - textSize.Y - font.TopOffset) / 2;

switch (Align)
{
Expand Down
37 changes: 27 additions & 10 deletions OpenRA.Mods.Common/Widgets/ChatDisplayWidget.cs
Expand Up @@ -9,7 +9,6 @@
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Primitives;
Expand All @@ -25,6 +24,8 @@ public class ChatDisplayWidget : Widget
public readonly Color BackgroundColorDark = ChromeMetrics.Get<Color>("TextContrastColorDark");
public readonly Color BackgroundColorLight = ChromeMetrics.Get<Color>("TextContrastColorLight");
public string Notification = "";
public readonly int TextLineBoxHeight = 16;
public readonly int Space = 4;

const int LogLength = 9;
List<ChatLine> recentLines = new List<ChatLine>();
Expand All @@ -35,13 +36,14 @@ public override void Draw()
{
var pos = RenderOrigin;
var chatLogArea = new Rectangle(pos.X, pos.Y, Bounds.Width, Bounds.Height);
var chatpos = new int2(chatLogArea.X + 5, chatLogArea.Bottom - 5);
var chatPos = new int2(chatLogArea.X + 5, chatLogArea.Bottom - 8);

var font = Game.Renderer.Fonts["Regular"];
Game.Renderer.EnableScissor(chatLogArea);

foreach (var line in recentLines.AsEnumerable().Reverse())
{
var lineHeight = TextLineBoxHeight;
var inset = 0;
string name = null;

Expand All @@ -52,31 +54,46 @@ public override void Draw()
}

var text = WidgetUtils.WrapText(line.Text, chatLogArea.Width - inset - 6, font);
chatpos = chatpos.WithY(chatpos.Y - (Math.Max(15, font.Measure(text).Y) + 5));
var textSize = font.Measure(text).Y;
var offset = font.TopOffset;

if (chatpos.Y < pos.Y)
if (chatPos.Y < pos.Y)
break;

var textLineHeight = lineHeight;

var dh = textSize - textLineHeight;
if (dh > 0)
textLineHeight += dh;

var textOffset = textLineHeight - (textLineHeight - textSize - offset) / 2;
var textPos = new int2(chatPos.X + inset, chatPos.Y - textOffset);

if (name != null)
{
var nameSize = font.Measure(name).Y;
var namePos = chatPos.WithY(chatPos.Y - (textLineHeight - (lineHeight - nameSize - offset) / 2));

if (UseContrast)
font.DrawTextWithContrast(name, chatpos,
font.DrawTextWithContrast(name, namePos,
line.NameColor, BackgroundColorDark, BackgroundColorLight, 1);
else if (UseShadow)
font.DrawTextWithShadow(name, chatpos,
font.DrawTextWithShadow(name, namePos,
line.NameColor, BackgroundColorDark, BackgroundColorLight, 1);
else
font.DrawText(name, chatpos, line.NameColor);
font.DrawText(name, namePos, line.NameColor);
}

if (UseContrast)
font.DrawTextWithContrast(text, chatpos + new int2(inset, 0),
font.DrawTextWithContrast(text, textPos,
line.TextColor, Color.Black, 1);
else if (UseShadow)
font.DrawTextWithShadow(text, chatpos + new int2(inset, 0),
font.DrawTextWithShadow(text, textPos,
line.TextColor, Color.Black, 1);
else
font.DrawText(text, chatpos + new int2(inset, 0), Color.White);
font.DrawText(text, textPos, Color.White);

chatPos = chatPos.WithY(chatPos.Y - Space - textLineHeight);
}

Game.Renderer.DisableScissor();
Expand Down
4 changes: 3 additions & 1 deletion OpenRA.Mods.Common/Widgets/CheckboxWidget.cs
Expand Up @@ -61,7 +61,9 @@ public override void Draw()
"checkbox";

WidgetUtils.DrawPanel(state, check);
var position = new float2(rect.Left + rect.Height * 1.5f, RenderOrigin.Y - BaseLine + (Bounds.Height - textSize.Y) / 2);

var topOffset = font.TopOffset;
var position = new float2(rect.Left + rect.Height * 1.5f, RenderOrigin.Y + (Bounds.Height - textSize.Y - topOffset) / 2);

if (Contrast)
font.DrawTextWithContrast(text, position,
Expand Down
24 changes: 2 additions & 22 deletions OpenRA.Platforms.Default/FreeTypeFont.cs
Expand Up @@ -27,11 +27,6 @@ internal static class FreeType
internal const int FT_LOAD_RENDER = 0x04;

internal static readonly int FaceRecGlyphOffset = IntPtr.Size == 8 ? 152 : 84; // offsetof(FT_FaceRec, glyph)
internal static readonly int FaceRecSizeOffset = IntPtr.Size == 8 ? 160 : 88; // offsetof(FT_FaceRec, size)
internal static readonly int SizeRecMetricsOffset = IntPtr.Size == 8 ? 24 : 12; // offsetof(FT_SizeRec, metrics)
internal static readonly int SizeMetricsAscenderOffset = IntPtr.Size == 8 ? 24 : 12; // offsetof(FT_Size_Metrics, ascender)
internal static readonly int SizeMetricsDescenderOffset = IntPtr.Size == 8 ? 32 : 16; // offsetof(FT_Size_Metrics, descender)

internal static readonly int GlyphSlotMetricsOffset = IntPtr.Size == 8 ? 48 : 24; // offsetof(FT_GlyphSlotRec, metrics)
internal static readonly int GlyphSlotBitmapOffset = IntPtr.Size == 8 ? 152 : 76; // offsetof(FT_GlyphSlotRec, bitmap)
internal static readonly int GlyphSlotBitmapLeftOffset = IntPtr.Size == 8 ? 192 : 100; // offsetof(FT_GlyphSlotRec, bitmap_left)
Expand Down Expand Up @@ -73,8 +68,6 @@ public sealed class FreeTypeFont : IFont
readonly IntPtr face;
bool disposed;

public int Height { get; private set; }

public FreeTypeFont(byte[] data)
{
if (library == IntPtr.Zero && FreeType.FT_Init_FreeType(out library) != FreeType.OK)
Expand All @@ -85,25 +78,12 @@ public FreeTypeFont(byte[] data)
throw new InvalidDataException("Failed to initialize font");
}

public void SetSize(int size, float deviceScale)
public FontGlyph CreateGlyph(char c, int size, float deviceScale)
{
var scaledSize = (uint)(size * deviceScale);
if (FreeType.FT_Set_Pixel_Sizes(face, scaledSize, scaledSize) != FreeType.OK)
throw new InvalidDataException("Failed to set size");

var faceSize = Marshal.ReadIntPtr(IntPtr.Add(face, FreeType.FaceRecSizeOffset)); // face->size
var metrics = IntPtr.Add(faceSize, FreeType.SizeRecMetricsOffset); // face->size->metrics
var ascender = Marshal.ReadIntPtr(IntPtr.Add(metrics, FreeType.SizeMetricsAscenderOffset)); // face->size->metrics.ascender
var descender = Marshal.ReadIntPtr(IntPtr.Add(metrics, FreeType.SizeMetricsDescenderOffset)); // face->size->metrics.descender

var ascenderValue = (int)ascender >> 6;
var descenderValue = (int)descender >> 6;

Height = (int)((ascenderValue - Math.Abs(descenderValue)) / deviceScale);
}
return EmptyGlyph;

public FontGlyph CreateGlyph(char c)
{
if (FreeType.FT_Load_Char(face, c, FreeType.FT_LOAD_RENDER) != FreeType.OK)
return EmptyGlyph;

Expand Down
9 changes: 5 additions & 4 deletions mods/cnc/chrome/assetbrowser.yaml
Expand Up @@ -10,7 +10,7 @@ Container@ASSETBROWSER_PANEL:
ColorPreviewManager@COLOR_MANAGER:
Label@ASSETBROWSER_TITLE:
Width: PARENT_RIGHT
Y: 0 - 25
Y: 0 - 22
Font: BigBold
Contrast: true
Align: Center
Expand All @@ -22,7 +22,7 @@ Container@ASSETBROWSER_PANEL:
Children:
Label@SOURCE_SELECTOR_DESC:
X: 15
Y: 5
Y: 6
Width: 160
Height: 25
Font: TinyBold
Expand Down Expand Up @@ -58,7 +58,7 @@ Container@ASSETBROWSER_PANEL:
TooltipTemplate: SIMPLE_TOOLTIP
Label@FILENAME_DESC:
X: 15
Y: 340
Y: 341
Width: 160
Height: 25
Font: TinyBold
Expand All @@ -72,7 +72,7 @@ Container@ASSETBROWSER_PANEL:
Type: Filename
Label@PALETTE_DESC:
X: PARENT_RIGHT - WIDTH - 270
Y: 30
Y: 31
Width: 150
Height: 25
Font: Bold
Expand Down Expand Up @@ -193,6 +193,7 @@ Container@ASSETBROWSER_PANEL:
MinimumValue: 0
Label@FRAME_COUNT:
X: 445
Y: 1
Width: 40
Height: 25
Font: TinyBold
Expand Down