StylePainter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. #include <LibDraw/GraphicsBitmap.h>
  2. #include <LibDraw/Painter.h>
  3. #include <LibDraw/Palette.h>
  4. #include <LibDraw/StylePainter.h>
  5. void StylePainter::paint_tab_button(Painter& painter, const Rect& rect, const Palette& palette, bool active, bool hovered, bool enabled)
  6. {
  7. Color base_color = palette.button();
  8. Color highlight_color2 = palette.threed_highlight();
  9. Color shadow_color1 = palette.threed_shadow1();
  10. Color shadow_color2 = palette.threed_shadow2();
  11. if (hovered && enabled && !active)
  12. base_color = palette.hover_highlight();
  13. PainterStateSaver saver(painter);
  14. painter.translate(rect.location());
  15. // Base
  16. painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 1 }, base_color);
  17. // Top line
  18. painter.draw_line({ 2, 0 }, { rect.width() - 3, 0 }, highlight_color2);
  19. // Left side
  20. painter.draw_line({ 0, 2 }, { 0, rect.height() - 1 }, highlight_color2);
  21. painter.set_pixel({ 1, 1 }, highlight_color2);
  22. // Right side
  23. painter.draw_line({
  24. rect.width() - 1,
  25. 2,
  26. },
  27. { rect.width() - 1, rect.height() - 1 }, shadow_color2);
  28. painter.draw_line({
  29. rect.width() - 2,
  30. 2,
  31. },
  32. { rect.width() - 2, rect.height() - 1 }, shadow_color1);
  33. painter.set_pixel({
  34. rect.width() - 2,
  35. 1,
  36. },
  37. shadow_color2);
  38. }
  39. static void paint_button_new(Painter& painter, const Rect& rect, const Palette& palette, bool pressed, bool checked, bool hovered, bool enabled)
  40. {
  41. Color button_color = palette.button();
  42. Color highlight_color2 = palette.threed_highlight();
  43. Color shadow_color1 = palette.threed_shadow1();
  44. Color shadow_color2 = palette.threed_shadow2();
  45. if (checked && enabled) {
  46. if (hovered)
  47. button_color = palette.hover_highlight();
  48. else
  49. button_color = palette.button();
  50. } else if (hovered && enabled)
  51. button_color = palette.hover_highlight();
  52. PainterStateSaver saver(painter);
  53. painter.translate(rect.location());
  54. if (pressed || checked) {
  55. // Base
  56. painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 2 }, button_color);
  57. painter.draw_rect({ {}, rect.size() }, shadow_color2);
  58. // Sunken shadow
  59. painter.draw_line({ 1, 1 }, { rect.width() - 2, 1 }, shadow_color1);
  60. painter.draw_line({ 1, 2 }, { 1, rect.height() - 2 }, shadow_color1);
  61. } else {
  62. // Base
  63. painter.fill_rect({ 1, 1, rect.width() - 3, rect.height() - 3 }, button_color);
  64. // Outer highlight
  65. painter.draw_line({ 0, 0 }, { rect.width() - 2, 0 }, highlight_color2);
  66. painter.draw_line({ 0, 1 }, { 0, rect.height() - 2 }, highlight_color2);
  67. // Outer shadow
  68. painter.draw_line({ 0, rect.height() - 1 }, { rect.width() - 1, rect.height() - 1 }, shadow_color2);
  69. painter.draw_line({ rect.width() - 1, 0 }, { rect.width() - 1, rect.height() - 2 }, shadow_color2);
  70. // Inner shadow
  71. painter.draw_line({ 1, rect.height() - 2 }, { rect.width() - 2, rect.height() - 2 }, shadow_color1);
  72. painter.draw_line({ rect.width() - 2, 1 }, { rect.width() - 2, rect.height() - 3 }, shadow_color1);
  73. }
  74. }
  75. void StylePainter::paint_button(Painter& painter, const Rect& rect, const Palette& palette, ButtonStyle button_style, bool pressed, bool hovered, bool checked, bool enabled)
  76. {
  77. if (button_style == ButtonStyle::Normal)
  78. return paint_button_new(painter, rect, palette, pressed, checked, hovered, enabled);
  79. Color button_color = palette.button();
  80. Color highlight_color = palette.threed_highlight();
  81. Color shadow_color = palette.threed_shadow1();
  82. if (button_style == ButtonStyle::CoolBar && !enabled)
  83. return;
  84. PainterStateSaver saver(painter);
  85. painter.translate(rect.location());
  86. if (pressed || checked) {
  87. // Base
  88. painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 2 }, button_color);
  89. // Sunken shadow
  90. painter.draw_line({ 1, 1 }, { rect.width() - 2, 1 }, shadow_color);
  91. painter.draw_line({ 1, 2 }, { 1, rect.height() - 2 }, shadow_color);
  92. // Bottom highlight
  93. painter.draw_line({ rect.width() - 2, 1 }, { rect.width() - 2, rect.height() - 3 }, highlight_color);
  94. painter.draw_line({ 1, rect.height() - 2 }, { rect.width() - 2, rect.height() - 2 }, highlight_color);
  95. } else if (button_style == ButtonStyle::CoolBar && hovered) {
  96. // Base
  97. painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 2 }, button_color);
  98. // White highlight
  99. painter.draw_line({ 1, 1 }, { rect.width() - 2, 1 }, highlight_color);
  100. painter.draw_line({ 1, 2 }, { 1, rect.height() - 2 }, highlight_color);
  101. // Gray shadow
  102. painter.draw_line({ rect.width() - 2, 1 }, { rect.width() - 2, rect.height() - 3 }, shadow_color);
  103. painter.draw_line({ 1, rect.height() - 2 }, { rect.width() - 2, rect.height() - 2 }, shadow_color);
  104. }
  105. }
  106. void StylePainter::paint_surface(Painter& painter, const Rect& rect, const Palette& palette, bool paint_vertical_lines, bool paint_top_line)
  107. {
  108. painter.fill_rect({ rect.x(), rect.y() + 1, rect.width(), rect.height() - 2 }, palette.button());
  109. painter.draw_line(rect.top_left(), rect.top_right(), paint_top_line ? palette.threed_highlight() : palette.button());
  110. painter.draw_line(rect.bottom_left(), rect.bottom_right(), palette.threed_shadow1());
  111. if (paint_vertical_lines) {
  112. painter.draw_line(rect.top_left().translated(0, 1), rect.bottom_left().translated(0, -1), palette.threed_highlight());
  113. painter.draw_line(rect.top_right(), rect.bottom_right().translated(0, -1), palette.threed_shadow1());
  114. }
  115. }
  116. void StylePainter::paint_frame(Painter& painter, const Rect& rect, const Palette& palette, FrameShape shape, FrameShadow shadow, int thickness, bool skip_vertical_lines)
  117. {
  118. Color top_left_color;
  119. Color bottom_right_color;
  120. Color dark_shade = palette.threed_shadow1();
  121. Color light_shade = palette.threed_highlight();
  122. if (shape == FrameShape::Container && thickness >= 2) {
  123. if (shadow == FrameShadow::Raised) {
  124. dark_shade = palette.threed_shadow2();
  125. }
  126. }
  127. if (shadow == FrameShadow::Raised) {
  128. top_left_color = light_shade;
  129. bottom_right_color = dark_shade;
  130. } else if (shadow == FrameShadow::Sunken) {
  131. top_left_color = dark_shade;
  132. bottom_right_color = light_shade;
  133. } else if (shadow == FrameShadow::Plain) {
  134. top_left_color = dark_shade;
  135. bottom_right_color = dark_shade;
  136. }
  137. if (thickness >= 1) {
  138. painter.draw_line(rect.top_left(), rect.top_right(), top_left_color);
  139. painter.draw_line(rect.bottom_left(), rect.bottom_right(), bottom_right_color);
  140. if (shape != FrameShape::Panel || !skip_vertical_lines) {
  141. painter.draw_line(rect.top_left().translated(0, 1), rect.bottom_left().translated(0, -1), top_left_color);
  142. painter.draw_line(rect.top_right(), rect.bottom_right().translated(0, -1), bottom_right_color);
  143. }
  144. }
  145. if (shape == FrameShape::Container && thickness >= 2) {
  146. Color top_left_color;
  147. Color bottom_right_color;
  148. Color dark_shade = palette.threed_shadow2();
  149. Color light_shade = palette.button();
  150. if (shadow == FrameShadow::Raised) {
  151. dark_shade = palette.threed_shadow1();
  152. top_left_color = light_shade;
  153. bottom_right_color = dark_shade;
  154. } else if (shadow == FrameShadow::Sunken) {
  155. top_left_color = dark_shade;
  156. bottom_right_color = light_shade;
  157. } else if (shadow == FrameShadow::Plain) {
  158. top_left_color = dark_shade;
  159. bottom_right_color = dark_shade;
  160. }
  161. Rect inner_container_frame_rect = rect.shrunken(2, 2);
  162. painter.draw_line(inner_container_frame_rect.top_left(), inner_container_frame_rect.top_right(), top_left_color);
  163. painter.draw_line(inner_container_frame_rect.bottom_left(), inner_container_frame_rect.bottom_right(), bottom_right_color);
  164. painter.draw_line(inner_container_frame_rect.top_left().translated(0, 1), inner_container_frame_rect.bottom_left().translated(0, -1), top_left_color);
  165. painter.draw_line(inner_container_frame_rect.top_right(), inner_container_frame_rect.bottom_right().translated(0, -1), bottom_right_color);
  166. }
  167. if (shape == FrameShape::Box && thickness >= 2) {
  168. swap(top_left_color, bottom_right_color);
  169. Rect inner_rect = rect.shrunken(2, 2);
  170. painter.draw_line(inner_rect.top_left(), inner_rect.top_right(), top_left_color);
  171. painter.draw_line(inner_rect.bottom_left(), inner_rect.bottom_right(), bottom_right_color);
  172. painter.draw_line(inner_rect.top_left().translated(0, 1), inner_rect.bottom_left().translated(0, -1), top_left_color);
  173. painter.draw_line(inner_rect.top_right(), inner_rect.bottom_right().translated(0, -1), bottom_right_color);
  174. }
  175. }
  176. void StylePainter::paint_window_frame(Painter& painter, const Rect& rect, const Palette& palette)
  177. {
  178. Color base_color = palette.button();
  179. Color dark_shade = palette.threed_shadow2();
  180. Color mid_shade = palette.threed_shadow1();
  181. Color light_shade = palette.threed_highlight();
  182. painter.draw_line(rect.top_left(), rect.top_right(), base_color);
  183. painter.draw_line(rect.top_left().translated(0, 1), rect.bottom_left(), base_color);
  184. painter.draw_line(rect.top_left().translated(1, 1), rect.top_right().translated(-1, 1), light_shade);
  185. painter.draw_line(rect.top_left().translated(1, 1), rect.bottom_left().translated(1, -1), light_shade);
  186. painter.draw_line(rect.top_left().translated(2, 2), rect.top_right().translated(-2, 2), base_color);
  187. painter.draw_line(rect.top_left().translated(2, 2), rect.bottom_left().translated(2, -2), base_color);
  188. painter.draw_line(rect.top_right(), rect.bottom_right(), dark_shade);
  189. painter.draw_line(rect.top_right().translated(-1, 1), rect.bottom_right().translated(-1, -1), mid_shade);
  190. painter.draw_line(rect.top_right().translated(-2, 2), rect.bottom_right().translated(-2, -2), base_color);
  191. painter.draw_line(rect.bottom_left(), rect.bottom_right(), dark_shade);
  192. painter.draw_line(rect.bottom_left().translated(1, -1), rect.bottom_right().translated(-1, -1), mid_shade);
  193. painter.draw_line(rect.bottom_left().translated(2, -2), rect.bottom_right().translated(-2, -2), base_color);
  194. }
  195. void StylePainter::paint_progress_bar(Painter& painter, const Rect& rect, const Palette& palette, int min, int max, int value, const StringView& text)
  196. {
  197. // First we fill the entire widget with the gradient. This incurs a bit of
  198. // overdraw but ensures a consistent look throughout the progression.
  199. Color start_color = palette.active_window_border1();
  200. Color end_color = palette.active_window_border2();
  201. painter.fill_rect_with_gradient(rect, start_color, end_color);
  202. if (!text.is_null()) {
  203. painter.draw_text(rect.translated(1, 1), text, TextAlignment::Center, palette.base_text());
  204. painter.draw_text(rect, text, TextAlignment::Center, palette.base_text().inverted());
  205. }
  206. float range_size = max - min;
  207. float progress = (value - min) / range_size;
  208. // Then we carve out a hole in the remaining part of the widget.
  209. // We draw the text a third time, clipped and inverse, for sharp contrast.
  210. float progress_width = progress * rect.width();
  211. Rect hole_rect { (int)progress_width, 0, (int)(rect.width() - progress_width), rect.height() };
  212. hole_rect.move_by(rect.location());
  213. hole_rect.set_right_without_resize(rect.right());
  214. PainterStateSaver saver(painter);
  215. painter.fill_rect(hole_rect, palette.base());
  216. painter.add_clip_rect(hole_rect);
  217. if (!text.is_null())
  218. painter.draw_text(rect.translated(0, 0), text, TextAlignment::Center, palette.base_text());
  219. }
  220. static RefPtr<GraphicsBitmap> s_unfilled_circle_bitmap;
  221. static RefPtr<GraphicsBitmap> s_filled_circle_bitmap;
  222. static RefPtr<GraphicsBitmap> s_changing_filled_circle_bitmap;
  223. static RefPtr<GraphicsBitmap> s_changing_unfilled_circle_bitmap;
  224. static const GraphicsBitmap& circle_bitmap(bool checked, bool changing)
  225. {
  226. if (changing)
  227. return checked ? *s_changing_filled_circle_bitmap : *s_changing_unfilled_circle_bitmap;
  228. return checked ? *s_filled_circle_bitmap : *s_unfilled_circle_bitmap;
  229. }
  230. void StylePainter::paint_radio_button(Painter& painter, const Rect& rect, const Palette&, bool is_checked, bool is_being_pressed)
  231. {
  232. if (!s_unfilled_circle_bitmap) {
  233. s_unfilled_circle_bitmap = GraphicsBitmap::load_from_file("/res/icons/unfilled-radio-circle.png");
  234. s_filled_circle_bitmap = GraphicsBitmap::load_from_file("/res/icons/filled-radio-circle.png");
  235. s_changing_filled_circle_bitmap = GraphicsBitmap::load_from_file("/res/icons/changing-filled-radio-circle.png");
  236. s_changing_unfilled_circle_bitmap = GraphicsBitmap::load_from_file("/res/icons/changing-unfilled-radio-circle.png");
  237. }
  238. auto& bitmap = circle_bitmap(is_checked, is_being_pressed);
  239. painter.blit(rect.location(), bitmap, bitmap.rect());
  240. }