GtkHostUITheme.cs
1 using Gtk; 2 using Ryujinx.HLE.UI; 3 using System.Diagnostics; 4 5 namespace Ryujinx.UI.Applet 6 { 7 internal class GtkHostUITheme : IHostUITheme 8 { 9 private const int RenderSurfaceWidth = 32; 10 private const int RenderSurfaceHeight = 32; 11 12 public string FontFamily { get; private set; } 13 14 public ThemeColor DefaultBackgroundColor { get; } 15 public ThemeColor DefaultForegroundColor { get; } 16 public ThemeColor DefaultBorderColor { get; } 17 public ThemeColor SelectionBackgroundColor { get; } 18 public ThemeColor SelectionForegroundColor { get; } 19 20 public GtkHostUITheme(Window parent) 21 { 22 Entry entry = new(); 23 entry.SetStateFlags(StateFlags.Selected, true); 24 25 // Get the font and some colors directly from GTK. 26 FontFamily = entry.PangoContext.FontDescription.Family; 27 28 // Get foreground colors from the style context. 29 30 var defaultForegroundColor = entry.StyleContext.GetColor(StateFlags.Normal); 31 var selectedForegroundColor = entry.StyleContext.GetColor(StateFlags.Selected); 32 33 DefaultForegroundColor = new ThemeColor((float)defaultForegroundColor.Alpha, (float)defaultForegroundColor.Red, (float)defaultForegroundColor.Green, (float)defaultForegroundColor.Blue); 34 SelectionForegroundColor = new ThemeColor((float)selectedForegroundColor.Alpha, (float)selectedForegroundColor.Red, (float)selectedForegroundColor.Green, (float)selectedForegroundColor.Blue); 35 36 ListBoxRow row = new(); 37 row.SetStateFlags(StateFlags.Selected, true); 38 39 // Request the main thread to render some UI elements to an image to get an approximation for the color. 40 // NOTE (caian): This will only take the color of the top-left corner of the background, which may be incorrect 41 // if someone provides a custom style with a gradient or image. 42 43 using (var surface = new Cairo.ImageSurface(Cairo.Format.Argb32, RenderSurfaceWidth, RenderSurfaceHeight)) 44 using (var context = new Cairo.Context(surface)) 45 { 46 context.SetSourceRGBA(1, 1, 1, 1); 47 context.Rectangle(0, 0, RenderSurfaceWidth, RenderSurfaceHeight); 48 context.Fill(); 49 50 // The background color must be from the main Window because entry uses a different color. 51 parent.StyleContext.RenderBackground(context, 0, 0, RenderSurfaceWidth, RenderSurfaceHeight); 52 53 DefaultBackgroundColor = ToThemeColor(surface.Data); 54 55 context.SetSourceRGBA(1, 1, 1, 1); 56 context.Rectangle(0, 0, RenderSurfaceWidth, RenderSurfaceHeight); 57 context.Fill(); 58 59 // Use the background color of the list box row when selected as the text box frame color because they are the 60 // same in the default theme. 61 row.StyleContext.RenderBackground(context, 0, 0, RenderSurfaceWidth, RenderSurfaceHeight); 62 63 DefaultBorderColor = ToThemeColor(surface.Data); 64 } 65 66 // Use the border color as the text selection color. 67 SelectionBackgroundColor = DefaultBorderColor; 68 } 69 70 private static ThemeColor ToThemeColor(byte[] data) 71 { 72 Debug.Assert(data.Length == 4 * RenderSurfaceWidth * RenderSurfaceHeight); 73 74 // Take the center-bottom pixel of the surface. 75 int position = 4 * (RenderSurfaceWidth * (RenderSurfaceHeight - 1) + RenderSurfaceWidth / 2); 76 77 if (position + 4 > data.Length) 78 { 79 return new ThemeColor(1, 0, 0, 0); 80 } 81 82 float a = data[position + 3] / 255.0f; 83 float r = data[position + 2] / 255.0f; 84 float g = data[position + 1] / 255.0f; 85 float b = data[position + 0] / 255.0f; 86 87 return new ThemeColor(a, r, g, b); 88 } 89 } 90 }