/ src / Ryujinx.Gtk3 / UI / Applet / GtkHostUITheme.cs
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  }