SystemTheme.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2022, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/LexicalPath.h>
  9. #include <AK/QuickSort.h>
  10. #include <LibCore/ConfigFile.h>
  11. #include <LibCore/DirIterator.h>
  12. #include <LibGfx/SystemTheme.h>
  13. #include <string.h>
  14. namespace Gfx {
  15. static SystemTheme dummy_theme;
  16. static SystemTheme const* theme_page = &dummy_theme;
  17. static Core::AnonymousBuffer theme_buffer;
  18. Core::AnonymousBuffer& current_system_theme_buffer()
  19. {
  20. VERIFY(theme_buffer.is_valid());
  21. return theme_buffer;
  22. }
  23. void set_system_theme(Core::AnonymousBuffer buffer)
  24. {
  25. theme_buffer = move(buffer);
  26. theme_page = theme_buffer.data<SystemTheme>();
  27. }
  28. ErrorOr<Core::AnonymousBuffer> load_system_theme(Core::ConfigFile const& file)
  29. {
  30. auto buffer = TRY(Core::AnonymousBuffer::create_with_size(sizeof(SystemTheme)));
  31. auto* data = buffer.data<SystemTheme>();
  32. auto get_color = [&](auto& name) {
  33. auto color_string = file.read_entry("Colors", name);
  34. auto color = Color::from_string(color_string);
  35. if (!color.has_value()) {
  36. auto maybe_color_config = Core::ConfigFile::open(data->path[(int)PathRole::ColorScheme]);
  37. if (maybe_color_config.is_error())
  38. maybe_color_config = Core::ConfigFile::open("/res/color-schemes/Default.ini");
  39. auto color_config = maybe_color_config.release_value();
  40. if (name == "ColorSchemeBackground"sv)
  41. color = Gfx::Color::from_string(color_config->read_entry("Primary", "Background"));
  42. else if (name == "ColorSchemeForeground"sv)
  43. color = Gfx::Color::from_string(color_config->read_entry("Primary", "Foreground"));
  44. else if (strncmp(name, "Bright", 6) == 0)
  45. color = Gfx::Color::from_string(color_config->read_entry("Bright", name + 6));
  46. else
  47. color = Gfx::Color::from_string(color_config->read_entry("Normal", name));
  48. if (!color.has_value())
  49. return Color(Color::Black);
  50. }
  51. return color.value();
  52. };
  53. auto get_flag = [&](auto& name) {
  54. return file.read_bool_entry("Flags", name, false);
  55. };
  56. auto get_alignment = [&](auto& name, auto role) {
  57. auto alignment = file.read_entry("Alignments", name).to_lowercase();
  58. if (alignment.is_empty()) {
  59. switch (role) {
  60. case (int)AlignmentRole::TitleAlignment:
  61. return Gfx::TextAlignment::CenterLeft;
  62. default:
  63. dbgln("Alignment {} has no fallback value!", name);
  64. return Gfx::TextAlignment::CenterLeft;
  65. }
  66. }
  67. if (alignment == "left" || alignment == "centerleft")
  68. return Gfx::TextAlignment::CenterLeft;
  69. else if (alignment == "right" || alignment == "centerright")
  70. return Gfx::TextAlignment::CenterRight;
  71. else if (alignment == "center")
  72. return Gfx::TextAlignment::Center;
  73. dbgln("Alignment {} has an invalid value!", name);
  74. return Gfx::TextAlignment::CenterLeft;
  75. };
  76. auto get_metric = [&](auto& name, auto role) {
  77. int metric = file.read_num_entry("Metrics", name, -1);
  78. if (metric == -1) {
  79. switch (role) {
  80. case (int)MetricRole::BorderThickness:
  81. return 4;
  82. case (int)MetricRole::BorderRadius:
  83. return 0;
  84. case (int)MetricRole::TitleHeight:
  85. return 19;
  86. case (int)MetricRole::TitleButtonHeight:
  87. return 15;
  88. case (int)MetricRole::TitleButtonWidth:
  89. return 15;
  90. default:
  91. dbgln("Metric {} has no fallback value!", name);
  92. return 16;
  93. }
  94. }
  95. return metric;
  96. };
  97. auto get_path = [&](auto& name, auto role, bool allow_empty) {
  98. auto path = file.read_entry("Paths", name);
  99. if (path.is_empty()) {
  100. switch (role) {
  101. case (int)PathRole::TitleButtonIcons:
  102. return "/res/icons/16x16/";
  103. default:
  104. return allow_empty ? "" : "/res/";
  105. }
  106. }
  107. return &path[0];
  108. };
  109. #define ENCODE_PATH(x, allow_empty) \
  110. do { \
  111. auto path = get_path(#x, (int)PathRole::x, allow_empty); \
  112. memcpy(data->path[(int)PathRole::x], path, min(strlen(path) + 1, sizeof(data->path[(int)PathRole::x]))); \
  113. data->path[(int)PathRole::x][sizeof(data->path[(int)PathRole::x]) - 1] = '\0'; \
  114. } while (0)
  115. ENCODE_PATH(TitleButtonIcons, false);
  116. ENCODE_PATH(ActiveWindowShadow, true);
  117. ENCODE_PATH(InactiveWindowShadow, true);
  118. ENCODE_PATH(TaskbarShadow, true);
  119. ENCODE_PATH(MenuShadow, true);
  120. ENCODE_PATH(TooltipShadow, true);
  121. ENCODE_PATH(ColorScheme, true);
  122. #undef __ENUMERATE_COLOR_ROLE
  123. #define __ENUMERATE_COLOR_ROLE(role) \
  124. data->color[(int)ColorRole::role] = get_color(#role).value();
  125. ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)
  126. #undef __ENUMERATE_COLOR_ROLE
  127. #undef __ENUMERATE_ALIGNMENT_ROLE
  128. #define __ENUMERATE_ALIGNMENT_ROLE(role) \
  129. data->alignment[(int)AlignmentRole::role] = get_alignment(#role, (int)AlignmentRole::role);
  130. ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE)
  131. #undef __ENUMERATE_ALIGNMENT_ROLE
  132. #undef __ENUMERATE_FLAG_ROLE
  133. #define __ENUMERATE_FLAG_ROLE(role) \
  134. data->flag[(int)FlagRole::role] = get_flag(#role);
  135. ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE)
  136. #undef __ENUMERATE_FLAG_ROLE
  137. #undef __ENUMERATE_METRIC_ROLE
  138. #define __ENUMERATE_METRIC_ROLE(role) \
  139. data->metric[(int)MetricRole::role] = get_metric(#role, (int)MetricRole::role);
  140. ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE)
  141. #undef __ENUMERATE_METRIC_ROLE
  142. data->flag[(int)FlagRole::BoldTextAsBright] = true;
  143. auto maybe_color_config = Core::ConfigFile::open(data->path[(int)PathRole::ColorScheme]);
  144. if (!maybe_color_config.is_error()) {
  145. auto color_config = maybe_color_config.release_value();
  146. data->flag[(int)FlagRole::BoldTextAsBright] = color_config->read_bool_entry("Options", "ShowBoldTextAsBright", true);
  147. }
  148. return buffer;
  149. }
  150. ErrorOr<Core::AnonymousBuffer> load_system_theme(DeprecatedString const& path)
  151. {
  152. auto config_file = TRY(Core::ConfigFile::open(path));
  153. return TRY(load_system_theme(config_file));
  154. }
  155. ErrorOr<Vector<SystemThemeMetaData>> list_installed_system_themes()
  156. {
  157. Vector<SystemThemeMetaData> system_themes;
  158. Core::DirIterator dt("/res/themes", Core::DirIterator::SkipDots);
  159. while (dt.has_next()) {
  160. auto theme_name = dt.next_path();
  161. auto theme_path = DeprecatedString::formatted("/res/themes/{}", theme_name);
  162. TRY(system_themes.try_append({ LexicalPath::title(theme_name), theme_path }));
  163. }
  164. quick_sort(system_themes, [](auto& a, auto& b) { return a.name < b.name; });
  165. return system_themes;
  166. }
  167. }