Преглед на файлове

Add a lifecycle method for manual theme item caching to Control

Yuri Sizov преди 3 години
родител
ревизия
3b1aa240dc
променени са 52 файла, в които са добавени 1560 реда и са изтрити 866 реда
  1. 10 6
      scene/gui/box_container.cpp
  2. 6 1
      scene/gui/box_container.h
  3. 88 44
      scene/gui/button.cpp
  4. 38 0
      scene/gui/button.h
  5. 60 36
      scene/gui/check_box.cpp
  6. 17 0
      scene/gui/check_box.h
  7. 66 24
      scene/gui/check_button.cpp
  8. 17 0
      scene/gui/check_button.h
  9. 10 0
      scene/gui/control.cpp
  10. 4 0
      scene/gui/control.h
  11. 17 13
      scene/gui/flow_container.cpp
  12. 7 1
      scene/gui/flow_container.h
  13. 14 13
      scene/gui/grid_container.cpp
  14. 7 0
      scene/gui/grid_container.h
  15. 76 59
      scene/gui/item_list.cpp
  16. 25 3
      scene/gui/item_list.h
  17. 37 21
      scene/gui/label.cpp
  18. 16 1
      scene/gui/label.h
  19. 67 36
      scene/gui/line_edit.cpp
  20. 26 0
      scene/gui/line_edit.h
  21. 32 12
      scene/gui/link_button.cpp
  22. 19 0
      scene/gui/link_button.h
  23. 14 15
      scene/gui/margin_container.cpp
  24. 9 0
      scene/gui/margin_container.h
  25. 56 32
      scene/gui/menu_bar.cpp
  26. 28 0
      scene/gui/menu_bar.h
  27. 44 25
      scene/gui/option_button.cpp
  28. 18 0
      scene/gui/option_button.h
  29. 7 2
      scene/gui/panel.cpp
  30. 6 0
      scene/gui/panel.h
  31. 12 30
      scene/gui/panel_container.cpp
  32. 5 0
      scene/gui/panel_container.h
  33. 31 29
      scene/gui/progress_bar.cpp
  34. 13 0
      scene/gui/progress_bar.h
  35. 47 29
      scene/gui/scroll_bar.cpp
  36. 18 1
      scene/gui/scroll_bar.h
  37. 11 9
      scene/gui/scroll_container.cpp
  38. 5 0
      scene/gui/scroll_container.h
  39. 12 6
      scene/gui/separator.cpp
  40. 8 0
      scene/gui/separator.h
  41. 45 12
      scene/gui/slider.cpp
  42. 14 1
      scene/gui/slider.h
  43. 10 6
      scene/gui/spin_box.cpp
  44. 5 0
      scene/gui/spin_box.h
  45. 21 21
      scene/gui/split_container.cpp
  46. 8 0
      scene/gui/split_container.h
  47. 92 112
      scene/gui/tab_bar.cpp
  48. 29 0
      scene/gui/tab_bar.h
  49. 80 52
      scene/gui/tab_container.cpp
  50. 35 0
      scene/gui/tab_container.h
  51. 210 210
      scene/gui/tree.cpp
  52. 8 4
      scene/gui/tree.h

+ 10 - 6
scene/gui/box_container.cpp

@@ -44,7 +44,6 @@ void BoxContainer::_resort() {
 
 	Size2i new_size = get_size();
 
-	int sep = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer");
 	bool rtl = is_layout_rtl();
 
 	bool first = true;
@@ -90,7 +89,7 @@ void BoxContainer::_resort() {
 		return;
 	}
 
-	int stretch_max = (vertical ? new_size.height : new_size.width) - (children_count - 1) * sep;
+	int stretch_max = (vertical ? new_size.height : new_size.width) - (children_count - 1) * theme_cache.separation;
 	int stretch_diff = stretch_max - stretch_min;
 	if (stretch_diff < 0) {
 		//avoid negative stretch space
@@ -214,7 +213,7 @@ void BoxContainer::_resort() {
 		if (first) {
 			first = false;
 		} else {
-			ofs += sep;
+			ofs += theme_cache.separation;
 		}
 
 		int from = ofs;
@@ -248,7 +247,6 @@ Size2 BoxContainer::get_minimum_size() const {
 	/* Calculate MINIMUM SIZE */
 
 	Size2i minimum;
-	int sep = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer");
 
 	bool first = true;
 
@@ -273,7 +271,7 @@ Size2 BoxContainer::get_minimum_size() const {
 				minimum.width = size.width;
 			}
 
-			minimum.height += size.height + (first ? 0 : sep);
+			minimum.height += size.height + (first ? 0 : theme_cache.separation);
 
 		} else { /* HORIZONTAL */
 
@@ -281,7 +279,7 @@ Size2 BoxContainer::get_minimum_size() const {
 				minimum.height = size.height;
 			}
 
-			minimum.width += size.width + (first ? 0 : sep);
+			minimum.width += size.width + (first ? 0 : theme_cache.separation);
 		}
 
 		first = false;
@@ -290,6 +288,12 @@ Size2 BoxContainer::get_minimum_size() const {
 	return minimum;
 }
 
+void BoxContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.separation = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer");
+}
+
 void BoxContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_SORT_CHILDREN: {

+ 6 - 1
scene/gui/box_container.h

@@ -47,11 +47,16 @@ private:
 	bool vertical = false;
 	AlignmentMode alignment = ALIGNMENT_BEGIN;
 
+	struct ThemeCache {
+		int separation = 0;
+	} theme_cache;
+
 	void _resort();
 
 protected:
-	void _notification(int p_what);
+	virtual void _update_theme_item_cache() override;
 
+	void _notification(int p_what);
 	static void _bind_methods();
 
 public:

+ 88 - 44
scene/gui/button.cpp

@@ -36,7 +36,7 @@
 Size2 Button::get_minimum_size() const {
 	Ref<Texture2D> _icon = icon;
 	if (_icon.is_null() && has_theme_icon(SNAME("icon"))) {
-		_icon = Control::get_theme_icon(SNAME("icon"));
+		_icon = theme_cache.icon;
 	}
 
 	return get_minimum_size_for_text_and_icon("", _icon);
@@ -46,6 +46,45 @@ void Button::_set_internal_margin(Side p_side, float p_value) {
 	_internal_margin[p_side] = p_value;
 }
 
+void Button::_update_theme_item_cache() {
+	BaseButton::_update_theme_item_cache();
+
+	theme_cache.normal = get_theme_stylebox(SNAME("normal"));
+	theme_cache.normal_mirrored = get_theme_stylebox(SNAME("normal_mirrored"));
+	theme_cache.pressed = get_theme_stylebox(SNAME("pressed"));
+	theme_cache.pressed_mirrored = get_theme_stylebox(SNAME("pressed_mirrored"));
+	theme_cache.hover = get_theme_stylebox(SNAME("hover"));
+	theme_cache.hover_mirrored = get_theme_stylebox(SNAME("hover_mirrored"));
+	theme_cache.hover_pressed = get_theme_stylebox(SNAME("hover_pressed"));
+	theme_cache.hover_pressed_mirrored = get_theme_stylebox(SNAME("hover_pressed_mirrored"));
+	theme_cache.disabled = get_theme_stylebox(SNAME("disabled"));
+	theme_cache.disabled_mirrored = get_theme_stylebox(SNAME("disabled_mirrored"));
+	theme_cache.focus = get_theme_stylebox(SNAME("focus"));
+
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color"));
+	theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color"));
+	theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color"));
+	theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color"));
+	theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
+
+	theme_cache.font = get_theme_font(SNAME("font"));
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+
+	theme_cache.icon_normal_color = get_theme_color(SNAME("icon_normal_color"));
+	theme_cache.icon_focus_color = get_theme_color(SNAME("icon_focus_color"));
+	theme_cache.icon_pressed_color = get_theme_color(SNAME("icon_pressed_color"));
+	theme_cache.icon_hover_color = get_theme_color(SNAME("icon_hover_color"));
+	theme_cache.icon_hover_pressed_color = get_theme_color(SNAME("icon_hover_pressed_color"));
+	theme_cache.icon_disabled_color = get_theme_color(SNAME("icon_disabled_color"));
+
+	theme_cache.icon = get_theme_icon(SNAME("icon"));
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+}
+
 void Button::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
@@ -73,15 +112,15 @@ void Button::_notification(int p_what) {
 			Color color;
 			Color color_icon(1, 1, 1, 1);
 
-			Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+			Ref<StyleBox> style = theme_cache.normal;
 			bool rtl = is_layout_rtl();
 
 			switch (get_draw_mode()) {
 				case DRAW_NORMAL: {
 					if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) {
-						style = get_theme_stylebox(SNAME("normal_mirrored"));
+						style = theme_cache.normal_mirrored;
 					} else {
-						style = get_theme_stylebox(SNAME("normal"));
+						style = theme_cache.normal;
 					}
 
 					if (!flat) {
@@ -90,14 +129,14 @@ void Button::_notification(int p_what) {
 
 					// Focus colors only take precedence over normal state.
 					if (has_focus()) {
-						color = get_theme_color(SNAME("font_focus_color"));
+						color = theme_cache.font_focus_color;
 						if (has_theme_color(SNAME("icon_focus_color"))) {
-							color_icon = get_theme_color(SNAME("icon_focus_color"));
+							color_icon = theme_cache.icon_focus_color;
 						}
 					} else {
-						color = get_theme_color(SNAME("font_color"));
+						color = theme_cache.font_color;
 						if (has_theme_color(SNAME("icon_normal_color"))) {
-							color_icon = get_theme_color(SNAME("icon_normal_color"));
+							color_icon = theme_cache.icon_normal_color;
 						}
 					}
 				} break;
@@ -105,19 +144,19 @@ void Button::_notification(int p_what) {
 					// Edge case for CheckButton and CheckBox.
 					if (has_theme_stylebox("hover_pressed")) {
 						if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) {
-							style = get_theme_stylebox(SNAME("hover_pressed_mirrored"));
+							style = theme_cache.hover_pressed_mirrored;
 						} else {
-							style = get_theme_stylebox(SNAME("hover_pressed"));
+							style = theme_cache.hover_pressed;
 						}
 
 						if (!flat) {
 							style->draw(ci, Rect2(Point2(0, 0), size));
 						}
 						if (has_theme_color(SNAME("font_hover_pressed_color"))) {
-							color = get_theme_color(SNAME("font_hover_pressed_color"));
+							color = theme_cache.font_hover_pressed_color;
 						}
 						if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
-							color_icon = get_theme_color(SNAME("icon_hover_pressed_color"));
+							color_icon = theme_cache.icon_hover_pressed_color;
 						}
 
 						break;
@@ -126,53 +165,53 @@ void Button::_notification(int p_what) {
 				}
 				case DRAW_PRESSED: {
 					if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) {
-						style = get_theme_stylebox(SNAME("pressed_mirrored"));
+						style = theme_cache.pressed_mirrored;
 					} else {
-						style = get_theme_stylebox(SNAME("pressed"));
+						style = theme_cache.pressed;
 					}
 
 					if (!flat) {
 						style->draw(ci, Rect2(Point2(0, 0), size));
 					}
 					if (has_theme_color(SNAME("font_pressed_color"))) {
-						color = get_theme_color(SNAME("font_pressed_color"));
+						color = theme_cache.font_pressed_color;
 					} else {
-						color = get_theme_color(SNAME("font_color"));
+						color = theme_cache.font_color;
 					}
 					if (has_theme_color(SNAME("icon_pressed_color"))) {
-						color_icon = get_theme_color(SNAME("icon_pressed_color"));
+						color_icon = theme_cache.icon_pressed_color;
 					}
 
 				} break;
 				case DRAW_HOVER: {
 					if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) {
-						style = get_theme_stylebox(SNAME("hover_mirrored"));
+						style = theme_cache.hover_mirrored;
 					} else {
-						style = get_theme_stylebox(SNAME("hover"));
+						style = theme_cache.hover;
 					}
 
 					if (!flat) {
 						style->draw(ci, Rect2(Point2(0, 0), size));
 					}
-					color = get_theme_color(SNAME("font_hover_color"));
+					color = theme_cache.font_hover_color;
 					if (has_theme_color(SNAME("icon_hover_color"))) {
-						color_icon = get_theme_color(SNAME("icon_hover_color"));
+						color_icon = theme_cache.icon_hover_color;
 					}
 
 				} break;
 				case DRAW_DISABLED: {
 					if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) {
-						style = get_theme_stylebox(SNAME("disabled_mirrored"));
+						style = theme_cache.disabled_mirrored;
 					} else {
-						style = get_theme_stylebox(SNAME("disabled"));
+						style = theme_cache.disabled;
 					}
 
 					if (!flat) {
 						style->draw(ci, Rect2(Point2(0, 0), size));
 					}
-					color = get_theme_color(SNAME("font_disabled_color"));
+					color = theme_cache.font_disabled_color;
 					if (has_theme_color(SNAME("icon_disabled_color"))) {
-						color_icon = get_theme_color(SNAME("icon_disabled_color"));
+						color_icon = theme_cache.icon_disabled_color;
 					} else {
 						color_icon.a = 0.4;
 					}
@@ -181,13 +220,13 @@ void Button::_notification(int p_what) {
 			}
 
 			if (has_focus()) {
-				Ref<StyleBox> style2 = get_theme_stylebox(SNAME("focus"));
+				Ref<StyleBox> style2 = theme_cache.focus;
 				style2->draw(ci, Rect2(Point2(), size));
 			}
 
 			Ref<Texture2D> _icon;
 			if (icon.is_null() && has_theme_icon(SNAME("icon"))) {
-				_icon = Control::get_theme_icon(SNAME("icon"));
+				_icon = theme_cache.icon;
 			} else {
 				_icon = icon;
 			}
@@ -217,21 +256,21 @@ void Button::_notification(int p_what) {
 				if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
 					style_offset.x = style->get_margin(SIDE_LEFT);
 					if (_internal_margin[SIDE_LEFT] > 0) {
-						icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation"));
+						icon_ofs_region = _internal_margin[SIDE_LEFT] + theme_cache.h_separation;
 					}
 				} else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
 					style_offset.x = 0.0;
 				} else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) {
 					style_offset.x = -style->get_margin(SIDE_RIGHT);
 					if (_internal_margin[SIDE_RIGHT] > 0) {
-						icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation"));
+						icon_ofs_region = -_internal_margin[SIDE_RIGHT] - theme_cache.h_separation;
 					}
 				}
 				style_offset.y = style->get_margin(SIDE_TOP);
 
 				if (expand_icon) {
 					Size2 _size = get_size() - style->get_offset() * 2;
-					int icon_text_separation = text.is_empty() ? 0 : get_theme_constant(SNAME("h_separation"));
+					int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation;
 					_size.width -= icon_text_separation + icon_ofs_region;
 					if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
 						_size.width -= text_buf->get_size().width;
@@ -261,7 +300,7 @@ void Button::_notification(int p_what) {
 				}
 			}
 
-			Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("h_separation")), 0) : Point2();
+			Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + theme_cache.h_separation, 0) : Point2();
 			if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
 				icon_ofs.x = 0.0;
 			}
@@ -271,10 +310,10 @@ void Button::_notification(int p_what) {
 			int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
 
 			if (_internal_margin[SIDE_LEFT] > 0) {
-				text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation"));
+				text_clip -= _internal_margin[SIDE_LEFT] + theme_cache.h_separation;
 			}
 			if (_internal_margin[SIDE_RIGHT] > 0) {
-				text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant(SNAME("h_separation"));
+				text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation;
 			}
 
 			Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0;
@@ -288,7 +327,7 @@ void Button::_notification(int p_what) {
 						icon_ofs.x = 0.0;
 					}
 					if (_internal_margin[SIDE_LEFT] > 0) {
-						text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation"));
+						text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + theme_cache.h_separation;
 					} else {
 						text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x;
 					}
@@ -305,7 +344,7 @@ void Button::_notification(int p_what) {
 				} break;
 				case HORIZONTAL_ALIGNMENT_RIGHT: {
 					if (_internal_margin[SIDE_RIGHT] > 0) {
-						text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation"));
+						text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - theme_cache.h_separation;
 					} else {
 						text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width;
 					}
@@ -316,8 +355,8 @@ void Button::_notification(int p_what) {
 				} break;
 			}
 
-			Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
-			int outline_size = get_theme_constant(SNAME("outline_size"));
+			Color font_outline_color = theme_cache.font_outline_color;
+			int outline_size = theme_cache.outline_size;
 			if (outline_size > 0 && font_outline_color.a > 0) {
 				text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color);
 			}
@@ -346,7 +385,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
 		if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
 			minsize.width += p_icon->get_width();
 			if (!xl_text.is_empty() || !p_text.is_empty()) {
-				minsize.width += MAX(0, get_theme_constant(SNAME("h_separation")));
+				minsize.width += MAX(0, theme_cache.h_separation);
 			}
 		} else {
 			minsize.width = MAX(minsize.width, p_icon->get_width());
@@ -354,12 +393,12 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
 	}
 
 	if (!xl_text.is_empty() || !p_text.is_empty()) {
-		Ref<Font> font = get_theme_font(SNAME("font"));
-		float font_height = font->get_height(get_theme_font_size(SNAME("font_size")));
+		Ref<Font> font = theme_cache.font;
+		float font_height = font->get_height(theme_cache.font_size);
 		minsize.height = MAX(font_height, minsize.height);
 	}
 
-	return get_theme_stylebox(SNAME("normal"))->get_minimum_size() + minsize;
+	return theme_cache.normal->get_minimum_size() + minsize;
 }
 
 void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
@@ -371,10 +410,15 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
 		p_text = xl_text;
 	}
 
-	Ref<Font> font = get_theme_font(SNAME("font"));
-	int font_size = get_theme_font_size(SNAME("font_size"));
-
 	p_paragraph->clear();
+
+	Ref<Font> font = theme_cache.font;
+	int font_size = theme_cache.font_size;
+	if (font.is_null() || font_size == 0) {
+		// Can't shape without a valid font and a non-zero size.
+		return;
+	}
+
 	if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
 		p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
 	} else {

+ 38 - 0
scene/gui/button.h

@@ -54,10 +54,48 @@ private:
 	HorizontalAlignment icon_alignment = HORIZONTAL_ALIGNMENT_LEFT;
 	float _internal_margin[4] = {};
 
+	struct ThemeCache {
+		Ref<StyleBox> normal;
+		Ref<StyleBox> normal_mirrored;
+		Ref<StyleBox> pressed;
+		Ref<StyleBox> pressed_mirrored;
+		Ref<StyleBox> hover;
+		Ref<StyleBox> hover_mirrored;
+		Ref<StyleBox> hover_pressed;
+		Ref<StyleBox> hover_pressed_mirrored;
+		Ref<StyleBox> disabled;
+		Ref<StyleBox> disabled_mirrored;
+		Ref<StyleBox> focus;
+
+		Color font_color;
+		Color font_focus_color;
+		Color font_pressed_color;
+		Color font_hover_color;
+		Color font_hover_pressed_color;
+		Color font_disabled_color;
+
+		Ref<Font> font;
+		int font_size = 0;
+		int outline_size = 0;
+		Color font_outline_color;
+
+		Color icon_normal_color;
+		Color icon_focus_color;
+		Color icon_pressed_color;
+		Color icon_hover_color;
+		Color icon_hover_pressed_color;
+		Color icon_disabled_color;
+
+		Ref<Texture2D> icon;
+
+		int h_separation = 0;
+	} theme_cache;
+
 	void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");
 
 protected:
 	void _set_internal_margin(Side p_side, float p_value);
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 	static void _bind_methods();
 

+ 60 - 36
scene/gui/check_box.cpp

@@ -33,39 +33,30 @@
 #include "servers/rendering_server.h"
 
 Size2 CheckBox::get_icon_size() const {
-	Ref<Texture2D> checked = Control::get_theme_icon(SNAME("checked"));
-	Ref<Texture2D> unchecked = Control::get_theme_icon(SNAME("unchecked"));
-	Ref<Texture2D> radio_checked = Control::get_theme_icon(SNAME("radio_checked"));
-	Ref<Texture2D> radio_unchecked = Control::get_theme_icon(SNAME("radio_unchecked"));
-	Ref<Texture2D> checked_disabled = Control::get_theme_icon(SNAME("checked_disabled"));
-	Ref<Texture2D> unchecked_disabled = Control::get_theme_icon(SNAME("unchecked_disabled"));
-	Ref<Texture2D> radio_checked_disabled = Control::get_theme_icon(SNAME("radio_checked_disabled"));
-	Ref<Texture2D> radio_unchecked_disabled = Control::get_theme_icon(SNAME("radio_unchecked_disabled"));
-
 	Size2 tex_size = Size2(0, 0);
-	if (!checked.is_null()) {
-		tex_size = Size2(checked->get_width(), checked->get_height());
+	if (!theme_cache.checked.is_null()) {
+		tex_size = Size2(theme_cache.checked->get_width(), theme_cache.checked->get_height());
 	}
-	if (!unchecked.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, unchecked->get_width()), MAX(tex_size.height, unchecked->get_height()));
+	if (!theme_cache.unchecked.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, theme_cache.unchecked->get_width()), MAX(tex_size.height, theme_cache.unchecked->get_height()));
 	}
-	if (!radio_checked.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, radio_checked->get_width()), MAX(tex_size.height, radio_checked->get_height()));
+	if (!theme_cache.radio_checked.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, theme_cache.radio_checked->get_width()), MAX(tex_size.height, theme_cache.radio_checked->get_height()));
 	}
-	if (!radio_unchecked.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, radio_unchecked->get_width()), MAX(tex_size.height, radio_unchecked->get_height()));
+	if (!theme_cache.radio_unchecked.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, theme_cache.radio_unchecked->get_width()), MAX(tex_size.height, theme_cache.radio_unchecked->get_height()));
 	}
-	if (!checked_disabled.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, checked_disabled->get_width()), MAX(tex_size.height, checked_disabled->get_height()));
+	if (!theme_cache.checked_disabled.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, theme_cache.checked_disabled->get_width()), MAX(tex_size.height, theme_cache.checked_disabled->get_height()));
 	}
-	if (!unchecked_disabled.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, unchecked_disabled->get_width()), MAX(tex_size.height, unchecked_disabled->get_height()));
+	if (!theme_cache.unchecked_disabled.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, theme_cache.unchecked_disabled->get_width()), MAX(tex_size.height, theme_cache.unchecked_disabled->get_height()));
 	}
-	if (!radio_checked_disabled.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, radio_checked_disabled->get_width()), MAX(tex_size.height, radio_checked_disabled->get_height()));
+	if (!theme_cache.radio_checked_disabled.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, theme_cache.radio_checked_disabled->get_width()), MAX(tex_size.height, theme_cache.radio_checked_disabled->get_height()));
 	}
-	if (!radio_unchecked_disabled.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, radio_unchecked_disabled->get_width()), MAX(tex_size.height, radio_unchecked_disabled->get_height()));
+	if (!theme_cache.radio_unchecked_disabled.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, theme_cache.radio_unchecked_disabled->get_width()), MAX(tex_size.height, theme_cache.radio_unchecked_disabled->get_height()));
 	}
 	return tex_size;
 }
@@ -75,14 +66,30 @@ Size2 CheckBox::get_minimum_size() const {
 	Size2 tex_size = get_icon_size();
 	minsize.width += tex_size.width;
 	if (get_text().length() > 0) {
-		minsize.width += MAX(0, get_theme_constant(SNAME("h_separation")));
+		minsize.width += MAX(0, theme_cache.h_separation);
 	}
-	Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
-	minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM));
+	minsize.height = MAX(minsize.height, tex_size.height + theme_cache.normal_style->get_margin(SIDE_TOP) + theme_cache.normal_style->get_margin(SIDE_BOTTOM));
 
 	return minsize;
 }
 
+void CheckBox::_update_theme_item_cache() {
+	Button::_update_theme_item_cache();
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+	theme_cache.check_v_adjust = get_theme_constant(SNAME("check_v_adjust"));
+	theme_cache.normal_style = get_theme_stylebox(SNAME("normal"));
+
+	theme_cache.checked = get_theme_icon(SNAME("checked"));
+	theme_cache.unchecked = get_theme_icon(SNAME("unchecked"));
+	theme_cache.radio_checked = get_theme_icon(SNAME("radio_checked"));
+	theme_cache.radio_unchecked = get_theme_icon(SNAME("radio_unchecked"));
+	theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled"));
+	theme_cache.unchecked_disabled = get_theme_icon(SNAME("unchecked_disabled"));
+	theme_cache.radio_checked_disabled = get_theme_icon(SNAME("radio_checked_disabled"));
+	theme_cache.radio_unchecked_disabled = get_theme_icon(SNAME("radio_unchecked_disabled"));
+}
+
 void CheckBox::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_THEME_CHANGED:
@@ -100,22 +107,39 @@ void CheckBox::_notification(int p_what) {
 		case NOTIFICATION_DRAW: {
 			RID ci = get_canvas_item();
 
-			Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : ""));
-			Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : ""));
-			Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
+			Ref<Texture2D> on_tex;
+			Ref<Texture2D> off_tex;
+
+			if (is_radio()) {
+				if (is_disabled()) {
+					on_tex = theme_cache.radio_checked_disabled;
+					off_tex = theme_cache.radio_unchecked_disabled;
+				} else {
+					on_tex = theme_cache.radio_checked;
+					off_tex = theme_cache.radio_unchecked;
+				}
+			} else {
+				if (is_disabled()) {
+					on_tex = theme_cache.checked_disabled;
+					off_tex = theme_cache.unchecked_disabled;
+				} else {
+					on_tex = theme_cache.checked;
+					off_tex = theme_cache.unchecked;
+				}
+			}
 
 			Vector2 ofs;
 			if (is_layout_rtl()) {
-				ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width;
+				ofs.x = get_size().x - theme_cache.normal_style->get_margin(SIDE_RIGHT) - get_icon_size().width;
 			} else {
-				ofs.x = sb->get_margin(SIDE_LEFT);
+				ofs.x = theme_cache.normal_style->get_margin(SIDE_LEFT);
 			}
-			ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_v_adjust"));
+			ofs.y = int((get_size().height - get_icon_size().height) / 2) + theme_cache.check_v_adjust;
 
 			if (is_pressed()) {
-				on->draw(ci, ofs);
+				on_tex->draw(ci, ofs);
 			} else {
-				off->draw(ci, ofs);
+				off_tex->draw(ci, ofs);
 			}
 		} break;
 	}

+ 17 - 0
scene/gui/check_box.h

@@ -36,9 +36,26 @@
 class CheckBox : public Button {
 	GDCLASS(CheckBox, Button);
 
+	struct ThemeCache {
+		int h_separation = 0;
+		int check_v_adjust = 0;
+		Ref<StyleBox> normal_style;
+
+		Ref<Texture2D> checked;
+		Ref<Texture2D> unchecked;
+		Ref<Texture2D> radio_checked;
+		Ref<Texture2D> radio_unchecked;
+		Ref<Texture2D> checked_disabled;
+		Ref<Texture2D> unchecked_disabled;
+		Ref<Texture2D> radio_checked_disabled;
+		Ref<Texture2D> radio_unchecked_disabled;
+	} theme_cache;
+
 protected:
 	Size2 get_icon_size() const;
 	Size2 get_minimum_size() const override;
+
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 
 	bool is_radio();

+ 66 - 24
scene/gui/check_button.cpp

@@ -34,14 +34,33 @@
 #include "servers/rendering_server.h"
 
 Size2 CheckButton::get_icon_size() const {
-	Ref<Texture2D> on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on");
-	Ref<Texture2D> off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off");
+	Ref<Texture2D> on_tex;
+	Ref<Texture2D> off_tex;
+
+	if (is_layout_rtl()) {
+		if (is_disabled()) {
+			on_tex = theme_cache.checked_disabled_mirrored;
+			off_tex = theme_cache.unchecked_disabled_mirrored;
+		} else {
+			on_tex = theme_cache.checked_mirrored;
+			off_tex = theme_cache.unchecked_mirrored;
+		}
+	} else {
+		if (is_disabled()) {
+			on_tex = theme_cache.checked_disabled;
+			off_tex = theme_cache.unchecked_disabled;
+		} else {
+			on_tex = theme_cache.checked;
+			off_tex = theme_cache.unchecked;
+		}
+	}
+
 	Size2 tex_size = Size2(0, 0);
-	if (!on.is_null()) {
-		tex_size = Size2(on->get_width(), on->get_height());
+	if (!on_tex.is_null()) {
+		tex_size = Size2(on_tex->get_width(), on_tex->get_height());
 	}
-	if (!off.is_null()) {
-		tex_size = Size2(MAX(tex_size.width, off->get_width()), MAX(tex_size.height, off->get_height()));
+	if (!off_tex.is_null()) {
+		tex_size = Size2(MAX(tex_size.width, off_tex->get_width()), MAX(tex_size.height, off_tex->get_height()));
 	}
 
 	return tex_size;
@@ -52,14 +71,30 @@ Size2 CheckButton::get_minimum_size() const {
 	Size2 tex_size = get_icon_size();
 	minsize.width += tex_size.width;
 	if (get_text().length() > 0) {
-		minsize.width += MAX(0, get_theme_constant(SNAME("h_separation")));
+		minsize.width += MAX(0, theme_cache.h_separation);
 	}
-	Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
-	minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM));
+	minsize.height = MAX(minsize.height, tex_size.height + theme_cache.normal_style->get_margin(SIDE_TOP) + theme_cache.normal_style->get_margin(SIDE_BOTTOM));
 
 	return minsize;
 }
 
+void CheckButton::_update_theme_item_cache() {
+	Button::_update_theme_item_cache();
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+	theme_cache.check_v_adjust = get_theme_constant(SNAME("check_v_adjust"));
+	theme_cache.normal_style = get_theme_stylebox(SNAME("normal"));
+
+	theme_cache.checked = get_theme_icon(SNAME("on"));
+	theme_cache.unchecked = get_theme_icon(SNAME("off"));
+	theme_cache.checked_disabled = get_theme_icon(SNAME("on_disabled"));
+	theme_cache.unchecked_disabled = get_theme_icon(SNAME("off_disabled"));
+	theme_cache.checked_mirrored = get_theme_icon(SNAME("on_mirrored"));
+	theme_cache.unchecked_mirrored = get_theme_icon(SNAME("off_mirrored"));
+	theme_cache.checked_disabled_mirrored = get_theme_icon(SNAME("on_disabled_mirrored"));
+	theme_cache.unchecked_disabled_mirrored = get_theme_icon(SNAME("off_disabled_mirrored"));
+}
+
 void CheckButton::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_THEME_CHANGED:
@@ -78,34 +113,41 @@ void CheckButton::_notification(int p_what) {
 			RID ci = get_canvas_item();
 			bool rtl = is_layout_rtl();
 
-			Ref<Texture2D> on;
-			if (rtl) {
-				on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored");
-			} else {
-				on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on");
-			}
-			Ref<Texture2D> off;
+			Ref<Texture2D> on_tex;
+			Ref<Texture2D> off_tex;
+
 			if (rtl) {
-				off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored");
+				if (is_disabled()) {
+					on_tex = theme_cache.checked_disabled_mirrored;
+					off_tex = theme_cache.unchecked_disabled_mirrored;
+				} else {
+					on_tex = theme_cache.checked_mirrored;
+					off_tex = theme_cache.unchecked_mirrored;
+				}
 			} else {
-				off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off");
+				if (is_disabled()) {
+					on_tex = theme_cache.checked_disabled;
+					off_tex = theme_cache.unchecked_disabled;
+				} else {
+					on_tex = theme_cache.checked;
+					off_tex = theme_cache.unchecked;
+				}
 			}
 
-			Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
 			Vector2 ofs;
 			Size2 tex_size = get_icon_size();
 
 			if (rtl) {
-				ofs.x = sb->get_margin(SIDE_LEFT);
+				ofs.x = theme_cache.normal_style->get_margin(SIDE_LEFT);
 			} else {
-				ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT));
+				ofs.x = get_size().width - (tex_size.width + theme_cache.normal_style->get_margin(SIDE_RIGHT));
 			}
-			ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_v_adjust"));
+			ofs.y = (get_size().height - tex_size.height) / 2 + theme_cache.check_v_adjust;
 
 			if (is_pressed()) {
-				on->draw(ci, ofs);
+				on_tex->draw(ci, ofs);
 			} else {
-				off->draw(ci, ofs);
+				off_tex->draw(ci, ofs);
 			}
 		} break;
 	}

+ 17 - 0
scene/gui/check_button.h

@@ -36,9 +36,26 @@
 class CheckButton : public Button {
 	GDCLASS(CheckButton, Button);
 
+	struct ThemeCache {
+		int h_separation = 0;
+		int check_v_adjust = 0;
+		Ref<StyleBox> normal_style;
+
+		Ref<Texture2D> checked;
+		Ref<Texture2D> unchecked;
+		Ref<Texture2D> checked_disabled;
+		Ref<Texture2D> unchecked_disabled;
+		Ref<Texture2D> checked_mirrored;
+		Ref<Texture2D> unchecked_mirrored;
+		Ref<Texture2D> checked_disabled_mirrored;
+		Ref<Texture2D> unchecked_disabled_mirrored;
+	} theme_cache;
+
 protected:
 	Size2 get_icon_size() const;
 	virtual Size2 get_minimum_size() const override;
+
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 
 public:

+ 10 - 0
scene/gui/control.cpp

@@ -2330,6 +2330,9 @@ void Control::_invalidate_theme_cache() {
 	data.theme_constant_cache.clear();
 }
 
+void Control::_update_theme_item_cache() {
+}
+
 void Control::set_theme(const Ref<Theme> &p_theme) {
 	if (data.theme == p_theme) {
 		return;
@@ -3103,6 +3106,11 @@ void Control::remove_child_notify(Node *p_child) {
 
 void Control::_notification(int p_notification) {
 	switch (p_notification) {
+		case NOTIFICATION_POSTINITIALIZE: {
+			_invalidate_theme_cache();
+			_update_theme_item_cache();
+		} break;
+
 		case NOTIFICATION_ENTER_TREE: {
 			// Need to defer here, because theme owner information might be set in
 			// add_child_notify, which doesn't get called until right after this.
@@ -3236,6 +3244,7 @@ void Control::_notification(int p_notification) {
 		case NOTIFICATION_THEME_CHANGED: {
 			emit_signal(SceneStringNames::get_singleton()->theme_changed);
 			_invalidate_theme_cache();
+			_update_theme_item_cache();
 			update_minimum_size();
 			queue_redraw();
 		} break;
@@ -3257,6 +3266,7 @@ void Control::_notification(int p_notification) {
 			if (is_inside_tree()) {
 				data.is_rtl_dirty = true;
 				_invalidate_theme_cache();
+				_update_theme_item_cache();
 				_size_changed();
 			}
 		} break;

+ 4 - 0
scene/gui/control.h

@@ -325,6 +325,10 @@ protected:
 	bool _property_can_revert(const StringName &p_name) const;
 	bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
 
+	// Theming.
+
+	virtual void _update_theme_item_cache();
+
 	// Internationalization.
 
 	virtual TypedArray<Vector2i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;

+ 17 - 13
scene/gui/flow_container.cpp

@@ -44,9 +44,6 @@ void FlowContainer::_resort() {
 		return;
 	}
 
-	int separation_horizontal = get_theme_constant(SNAME("h_separation"));
-	int separation_vertical = get_theme_constant(SNAME("v_separation"));
-
 	bool rtl = is_layout_rtl();
 
 	HashMap<Control *, Size2i> children_minsize_cache;
@@ -74,14 +71,14 @@ void FlowContainer::_resort() {
 
 		if (vertical) { /* VERTICAL */
 			if (children_in_current_line > 0) {
-				ofs.y += separation_vertical;
+				ofs.y += theme_cache.v_separation;
 			}
 			if (ofs.y + child_msc.y > current_container_size) {
-				line_length = ofs.y - separation_vertical;
+				line_length = ofs.y - theme_cache.v_separation;
 				lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
 
 				// Move in new column (vertical line).
-				ofs.x += line_height + separation_horizontal;
+				ofs.x += line_height + theme_cache.h_separation;
 				ofs.y = 0;
 				line_height = 0;
 				line_stretch_ratio_total = 0;
@@ -96,14 +93,14 @@ void FlowContainer::_resort() {
 
 		} else { /* HORIZONTAL */
 			if (children_in_current_line > 0) {
-				ofs.x += separation_horizontal;
+				ofs.x += theme_cache.h_separation;
 			}
 			if (ofs.x + child_msc.x > current_container_size) {
-				line_length = ofs.x - separation_horizontal;
+				line_length = ofs.x - theme_cache.h_separation;
 				lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
 
 				// Move in new line.
-				ofs.y += line_height + separation_vertical;
+				ofs.y += line_height + theme_cache.v_separation;
 				ofs.x = 0;
 				line_height = 0;
 				line_stretch_ratio_total = 0;
@@ -146,11 +143,11 @@ void FlowContainer::_resort() {
 			current_line_idx++;
 			child_idx_in_line = 0;
 			if (vertical) {
-				ofs.x += line_data.min_line_height + separation_horizontal;
+				ofs.x += line_data.min_line_height + theme_cache.h_separation;
 				ofs.y = 0;
 			} else {
 				ofs.x = 0;
-				ofs.y += line_data.min_line_height + separation_vertical;
+				ofs.y += line_data.min_line_height + theme_cache.v_separation;
 			}
 			line_data = lines_data[current_line_idx];
 		}
@@ -184,9 +181,9 @@ void FlowContainer::_resort() {
 		fit_child_in_rect(child, child_rect);
 
 		if (vertical) { /* VERTICAL */
-			ofs.y += child_size.height + separation_vertical;
+			ofs.y += child_size.height + theme_cache.v_separation;
 		} else { /* HORIZONTAL */
-			ofs.x += child_size.width + separation_horizontal;
+			ofs.x += child_size.width + theme_cache.h_separation;
 		}
 
 		child_idx_in_line++;
@@ -250,6 +247,13 @@ Vector<int> FlowContainer::get_allowed_size_flags_vertical() const {
 	return flags;
 }
 
+void FlowContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+	theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
+}
+
 void FlowContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_SORT_CHILDREN: {

+ 7 - 1
scene/gui/flow_container.h

@@ -42,11 +42,17 @@ private:
 
 	bool vertical = false;
 
+	struct ThemeCache {
+		int h_separation = 0;
+		int v_separation = 0;
+	} theme_cache;
+
 	void _resort();
 
 protected:
-	void _notification(int p_what);
+	virtual void _update_theme_item_cache() override;
 
+	void _notification(int p_what);
 	static void _bind_methods();
 
 public:

+ 14 - 13
scene/gui/grid_container.cpp

@@ -31,6 +31,13 @@
 #include "grid_container.h"
 #include "core/templates/rb_set.h"
 
+void GridContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+	theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
+}
+
 void GridContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_SORT_CHILDREN: {
@@ -39,9 +46,6 @@ void GridContainer::_notification(int p_what) {
 			RBSet<int> col_expanded; // Columns which have the SIZE_EXPAND flag set.
 			RBSet<int> row_expanded; // Rows which have the SIZE_EXPAND flag set.
 
-			int hsep = get_theme_constant(SNAME("h_separation"));
-			int vsep = get_theme_constant(SNAME("v_separation"));
-
 			// Compute the per-column/per-row data.
 			int valid_controls_index = 0;
 			for (int i = 0; i < get_child_count(); i++) {
@@ -98,8 +102,8 @@ void GridContainer::_notification(int p_what) {
 					remaining_space.height -= E.value;
 				}
 			}
-			remaining_space.height -= vsep * MAX(max_row - 1, 0);
-			remaining_space.width -= hsep * MAX(max_col - 1, 0);
+			remaining_space.height -= theme_cache.v_separation * MAX(max_row - 1, 0);
+			remaining_space.width -= theme_cache.h_separation * MAX(max_col - 1, 0);
 
 			bool can_fit = false;
 			while (!can_fit && col_expanded.size() > 0) {
@@ -202,7 +206,7 @@ void GridContainer::_notification(int p_what) {
 						col_ofs = 0;
 					}
 					if (row > 0) {
-						row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep;
+						row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + theme_cache.v_separation;
 
 						if (row_expanded.has(row - 1) && row - 1 < row_remaining_pixel_index) {
 							// Apply the remaining pixel of the previous row.
@@ -224,11 +228,11 @@ void GridContainer::_notification(int p_what) {
 				if (rtl) {
 					Point2 p(col_ofs - s.width, row_ofs);
 					fit_child_in_rect(c, Rect2(p, s));
-					col_ofs -= s.width + hsep;
+					col_ofs -= s.width + theme_cache.h_separation;
 				} else {
 					Point2 p(col_ofs, row_ofs);
 					fit_child_in_rect(c, Rect2(p, s));
-					col_ofs += s.width + hsep;
+					col_ofs += s.width + theme_cache.h_separation;
 				}
 			}
 		} break;
@@ -271,9 +275,6 @@ Size2 GridContainer::get_minimum_size() const {
 	RBMap<int, int> col_minw;
 	RBMap<int, int> row_minh;
 
-	int hsep = get_theme_constant(SNAME("h_separation"));
-	int vsep = get_theme_constant(SNAME("v_separation"));
-
 	int max_row = 0;
 	int max_col = 0;
 
@@ -313,8 +314,8 @@ Size2 GridContainer::get_minimum_size() const {
 		ms.height += E.value;
 	}
 
-	ms.height += vsep * max_row;
-	ms.width += hsep * max_col;
+	ms.height += theme_cache.v_separation * max_row;
+	ms.width += theme_cache.h_separation * max_col;
 
 	return ms;
 }

+ 7 - 0
scene/gui/grid_container.h

@@ -38,7 +38,14 @@ class GridContainer : public Container {
 
 	int columns = 1;
 
+	struct ThemeCache {
+		int h_separation = 0;
+		int v_separation = 0;
+	} theme_cache;
+
 protected:
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 	static void _bind_methods();
 

+ 76 - 59
scene/gui/item_list.cpp

@@ -43,7 +43,7 @@ void ItemList::_shape(int p_idx) {
 	} else {
 		item.text_buf->set_direction((TextServer::Direction)item.text_direction);
 	}
-	item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.language);
+	item.text_buf->add_string(item.text, theme_cache.font, theme_cache.font_size, item.language);
 	if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
 		item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
 	} else {
@@ -655,8 +655,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
 	if (mb.is_valid() && mb->is_pressed()) {
 		search_string = ""; //any mousepress cancels
 		Vector2 pos = mb->get_position();
-		Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
-		pos -= bg->get_offset();
+		pos -= theme_cache.bg_style->get_offset();
 		pos.y += scroll_bar->get_value();
 
 		if (is_layout_rtl()) {
@@ -980,6 +979,31 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) {
 	return Rect2(ofs_x, ofs_y, tex_width, tex_height);
 }
 
+void ItemList::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+	theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
+
+	theme_cache.bg_style = get_theme_stylebox(SNAME("bg"));
+	theme_cache.bg_focus_style = get_theme_stylebox(SNAME("bg_focus"));
+
+	theme_cache.font = get_theme_font(SNAME("font"));
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color"));
+	theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+
+	theme_cache.line_separation = get_theme_constant(SNAME("line_separation"));
+	theme_cache.icon_margin = get_theme_constant(SNAME("icon_margin"));
+	theme_cache.selected_style = get_theme_stylebox(SNAME("selected"));
+	theme_cache.selected_focus_style = get_theme_stylebox(SNAME("selected_focus"));
+	theme_cache.cursor_style = get_theme_stylebox(SNAME("cursor_unfocused"));
+	theme_cache.cursor_focus_style = get_theme_stylebox(SNAME("cursor"));
+	theme_cache.guide_color = get_theme_color(SNAME("guide_color"));
+}
+
 void ItemList::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_RESIZED: {
@@ -998,37 +1022,32 @@ void ItemList::_notification(int p_what) {
 		} break;
 
 		case NOTIFICATION_DRAW: {
-			Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
-
 			int mw = scroll_bar->get_minimum_size().x;
 			scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw);
 			scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
-			scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP));
-			scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM));
+			scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, theme_cache.bg_style->get_margin(SIDE_TOP));
+			scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -theme_cache.bg_style->get_margin(SIDE_BOTTOM));
 
 			Size2 size = get_size();
-			int width = size.width - bg->get_minimum_size().width;
+			int width = size.width - theme_cache.bg_style->get_minimum_size().width;
 
-			draw_style_box(bg, Rect2(Point2(), size));
+			draw_style_box(theme_cache.bg_style, Rect2(Point2(), size));
 
-			int hseparation = get_theme_constant(SNAME("h_separation"));
-			int vseparation = get_theme_constant(SNAME("v_separation"));
-			int icon_margin = get_theme_constant(SNAME("icon_margin"));
-			int line_separation = get_theme_constant(SNAME("line_separation"));
-			Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
-			int outline_size = get_theme_constant(SNAME("outline_size"));
+			Ref<StyleBox> sbsel;
+			Ref<StyleBox> cursor;
 
-			Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected"));
-			Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused"));
+			if (has_focus()) {
+				sbsel = theme_cache.selected_focus_style;
+				cursor = theme_cache.cursor_focus_style;
+			} else {
+				sbsel = theme_cache.selected_style;
+				cursor = theme_cache.cursor_style;
+			}
 			bool rtl = is_layout_rtl();
 
-			Color guide_color = get_theme_color(SNAME("guide_color"));
-			Color font_color = get_theme_color(SNAME("font_color"));
-			Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
-
 			if (has_focus()) {
 				RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
-				draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size));
+				draw_style_box(theme_cache.bg_focus_style, Rect2(Point2(), size));
 				RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
 			}
 
@@ -1047,9 +1066,9 @@ void ItemList::_notification(int p_what) {
 
 						if (!items[i].text.is_empty()) {
 							if (icon_mode == ICON_MODE_TOP) {
-								minsize.y += icon_margin;
+								minsize.y += theme_cache.icon_margin;
 							} else {
-								minsize.x += icon_margin;
+								minsize.x += theme_cache.icon_margin;
 							}
 						}
 					}
@@ -1067,7 +1086,7 @@ void ItemList::_notification(int p_what) {
 						if (icon_mode == ICON_MODE_TOP) {
 							minsize.x = MAX(minsize.x, s.width);
 							if (max_text_lines > 0) {
-								minsize.y += s.height + line_separation * max_text_lines;
+								minsize.y += s.height + theme_cache.line_separation * max_text_lines;
 							} else {
 								minsize.y += s.height;
 							}
@@ -1084,13 +1103,13 @@ void ItemList::_notification(int p_what) {
 					max_column_width = MAX(max_column_width, minsize.x);
 
 					// elements need to adapt to the selected size
-					minsize.y += vseparation;
-					minsize.x += hseparation;
+					minsize.y += theme_cache.v_separation;
+					minsize.x += theme_cache.h_separation;
 					items.write[i].rect_cache.size = minsize;
 					items.write[i].min_rect_cache.size = minsize;
 				}
 
-				int fit_size = size.x - bg->get_minimum_size().width - mw;
+				int fit_size = size.x - theme_cache.bg_style->get_minimum_size().width - mw;
 
 				//2-attempt best fit
 				current_columns = 0x7FFFFFFF;
@@ -1118,11 +1137,11 @@ void ItemList::_notification(int p_what) {
 						}
 						items.write[i].rect_cache.position = ofs;
 						max_h = MAX(max_h, items[i].rect_cache.size.y);
-						ofs.x += items[i].rect_cache.size.x + hseparation;
+						ofs.x += items[i].rect_cache.size.x + theme_cache.h_separation;
 						col++;
 						if (col == current_columns) {
 							if (i < items.size() - 1) {
-								separators.push_back(ofs.y + max_h + vseparation / 2);
+								separators.push_back(ofs.y + max_h + theme_cache.v_separation / 2);
 							}
 
 							for (int j = i; j >= 0 && col > 0; j--, col--) {
@@ -1130,7 +1149,7 @@ void ItemList::_notification(int p_what) {
 							}
 
 							ofs.x = 0;
-							ofs.y += max_h + vseparation;
+							ofs.y += max_h + theme_cache.v_separation;
 							col = 0;
 							max_h = 0;
 						}
@@ -1141,10 +1160,10 @@ void ItemList::_notification(int p_what) {
 					}
 
 					if (all_fit) {
-						float page = MAX(0, size.height - bg->get_minimum_size().height);
+						float page = MAX(0, size.height - theme_cache.bg_style->get_minimum_size().height);
 						float max = MAX(page, ofs.y + max_h);
 						if (auto_height) {
-							auto_height_value = ofs.y + max_h + bg->get_minimum_size().height;
+							auto_height_value = ofs.y + max_h + theme_cache.bg_style->get_minimum_size().height;
 						}
 						scroll_bar->set_max(max);
 						scroll_bar->set_page(page);
@@ -1185,7 +1204,7 @@ void ItemList::_notification(int p_what) {
 
 			ensure_selected_visible = false;
 
-			Vector2 base_ofs = bg->get_offset();
+			Vector2 base_ofs = theme_cache.bg_style->get_offset();
 			base_ofs.y -= int(scroll_bar->get_value());
 
 			const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there
@@ -1229,10 +1248,10 @@ void ItemList::_notification(int p_what) {
 				if (items[i].selected) {
 					Rect2 r = rcache;
 					r.position += base_ofs;
-					r.position.y -= vseparation / 2;
-					r.size.y += vseparation;
-					r.position.x -= hseparation / 2;
-					r.size.x += hseparation;
+					r.position.y -= theme_cache.v_separation / 2;
+					r.size.y += theme_cache.v_separation;
+					r.position.x -= theme_cache.h_separation / 2;
+					r.size.x += theme_cache.h_separation;
 
 					if (rtl) {
 						r.position.x = size.width - r.position.x - r.size.x;
@@ -1245,10 +1264,10 @@ void ItemList::_notification(int p_what) {
 					r.position += base_ofs;
 
 					// Size rect to make the align the temperature colors
-					r.position.y -= vseparation / 2;
-					r.size.y += vseparation;
-					r.position.x -= hseparation / 2;
-					r.size.x += hseparation;
+					r.position.y -= theme_cache.v_separation / 2;
+					r.size.y += theme_cache.v_separation;
+					r.position.x -= theme_cache.h_separation / 2;
+					r.size.x += theme_cache.h_separation;
 
 					if (rtl) {
 						r.position.x = size.width - r.position.x - r.size.x;
@@ -1274,11 +1293,11 @@ void ItemList::_notification(int p_what) {
 
 					if (icon_mode == ICON_MODE_TOP) {
 						pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2);
-						pos.y += icon_margin;
-						text_ofs.y = icon_size.height + icon_margin * 2;
+						pos.y += theme_cache.icon_margin;
+						text_ofs.y = icon_size.height + theme_cache.icon_margin * 2;
 					} else {
 						pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2);
-						text_ofs.x = icon_size.width + icon_margin;
+						text_ofs.x = icon_size.width + theme_cache.icon_margin;
 					}
 
 					Rect2 draw_rect = Rect2(pos, icon_size);
@@ -1329,7 +1348,7 @@ void ItemList::_notification(int p_what) {
 						max_len = size2.x;
 					}
 
-					Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
+					Color modulate = items[i].selected ? theme_cache.font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : theme_cache.font_color);
 					if (items[i].disabled) {
 						modulate.a *= 0.5;
 					}
@@ -1344,8 +1363,8 @@ void ItemList::_notification(int p_what) {
 
 						items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
 
-						if (outline_size > 0 && font_outline_color.a > 0) {
-							items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+						if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
+							items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color);
 						}
 
 						items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
@@ -1375,8 +1394,8 @@ void ItemList::_notification(int p_what) {
 							items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
 						}
 
-						if (outline_size > 0 && font_outline_color.a > 0) {
-							items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+						if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
+							items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color);
 						}
 
 						if (width - text_ofs.x > 0) {
@@ -1388,10 +1407,10 @@ void ItemList::_notification(int p_what) {
 				if (select_mode == SELECT_MULTI && i == current) {
 					Rect2 r = rcache;
 					r.position += base_ofs;
-					r.position.y -= vseparation / 2;
-					r.size.y += vseparation;
-					r.position.x -= hseparation / 2;
-					r.size.x += hseparation;
+					r.position.y -= theme_cache.v_separation / 2;
+					r.size.y += theme_cache.v_separation;
+					r.position.x -= theme_cache.h_separation / 2;
+					r.size.x += theme_cache.h_separation;
 
 					if (rtl) {
 						r.position.x = size.width - r.position.x - r.size.x;
@@ -1423,7 +1442,7 @@ void ItemList::_notification(int p_what) {
 				}
 
 				const int y = base_ofs.y + separators[i];
-				draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color);
+				draw_line(Vector2(theme_cache.bg_style->get_margin(SIDE_LEFT), y), Vector2(width, y), theme_cache.guide_color);
 			}
 		} break;
 	}
@@ -1435,8 +1454,7 @@ void ItemList::_scroll_changed(double) {
 
 int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
 	Vector2 pos = p_pos;
-	Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
-	pos -= bg->get_offset();
+	pos -= theme_cache.bg_style->get_offset();
 	pos.y += scroll_bar->get_value();
 
 	if (is_layout_rtl()) {
@@ -1473,8 +1491,7 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const {
 	}
 
 	Vector2 pos = p_pos;
-	Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
-	pos -= bg->get_offset();
+	pos -= theme_cache.bg_style->get_offset();
 	pos.y += scroll_bar->get_value();
 
 	if (is_layout_rtl()) {

+ 25 - 3
scene/gui/item_list.h

@@ -109,23 +109,45 @@ private:
 	int max_columns = 1;
 
 	Size2 fixed_icon_size;
-
 	Size2 max_item_size_cache;
 
 	int defer_select_single = -1;
-
 	bool allow_rmb_select = false;
-
 	bool allow_reselect = false;
 
 	real_t icon_scale = 1.0;
 
 	bool do_autoscroll_to_bottom = false;
 
+	struct ThemeCache {
+		int h_separation = 0;
+		int v_separation = 0;
+
+		Ref<StyleBox> bg_style;
+		Ref<StyleBox> bg_focus_style;
+
+		Ref<Font> font;
+		int font_size = 0;
+		Color font_color;
+		Color font_selected_color;
+		int font_outline_size = 0;
+		Color font_outline_color;
+
+		int line_separation = 0;
+		int icon_margin = 0;
+		Ref<StyleBox> selected_style;
+		Ref<StyleBox> selected_focus_style;
+		Ref<StyleBox> cursor_style;
+		Ref<StyleBox> cursor_focus_style;
+		Color guide_color;
+	} theme_cache;
+
 	void _scroll_changed(double);
 	void _shape(int p_idx);
 
 protected:
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;

+ 37 - 21
scene/gui/label.cpp

@@ -71,7 +71,7 @@ bool Label::is_uppercase() const {
 }
 
 int Label::get_line_height(int p_line) const {
-	Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
+	Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
 	if (p_line >= 0 && p_line < lines_rid.size()) {
 		return TS->shaped_text_get_size(lines_rid[p_line]).y;
 	} else if (lines_rid.size() > 0) {
@@ -81,13 +81,13 @@ int Label::get_line_height(int p_line) const {
 		}
 		return h;
 	} else {
-		int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size"));
+		int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size;
 		return font->get_height(font_size);
 	}
 }
 
 void Label::_shape() {
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label"));
+	Ref<StyleBox> style = theme_cache.normal_style;
 	int width = (get_size().width - style->get_minimum_size().width);
 
 	if (dirty || font_dirty) {
@@ -99,8 +99,8 @@ void Label::_shape() {
 		} else {
 			TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction);
 		}
-		const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
-		int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size"));
+		const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
+		int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size;
 		ERR_FAIL_COND(font.is_null());
 		String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
 		if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
@@ -232,8 +232,8 @@ void Label::_shape() {
 }
 
 void Label::_update_visible() {
-	int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"), SNAME("Label"));
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label"));
+	int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing;
+	Ref<StyleBox> style = theme_cache.normal_style;
 	int lines_visible = lines_rid.size();
 
 	if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
@@ -272,6 +272,22 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col
 	}
 }
 
+void Label::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.normal_style = get_theme_stylebox(SNAME("normal"));
+	theme_cache.font = get_theme_font(SNAME("font"));
+
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.line_spacing = get_theme_constant(SNAME("line_spacing"));
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
+	theme_cache.font_shadow_offset = Point2(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+	theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size"));
+	theme_cache.font_shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
+}
+
 void Label::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_TRANSLATION_CHANGED: {
@@ -307,15 +323,15 @@ void Label::_notification(int p_what) {
 
 			Size2 string_size;
 			Size2 size = get_size();
-			Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
-			Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
-			Color font_color = has_settings ? settings->get_font_color() : get_theme_color(SNAME("font_color"));
-			Color font_shadow_color = has_settings ? settings->get_shadow_color() : get_theme_color(SNAME("font_shadow_color"));
-			Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : Point2(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
-			int line_spacing = has_settings ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"));
-			Color font_outline_color = has_settings ? settings->get_outline_color() : get_theme_color(SNAME("font_outline_color"));
-			int outline_size = has_settings ? settings->get_outline_size() : get_theme_constant(SNAME("outline_size"));
-			int shadow_outline_size = has_settings ? settings->get_shadow_size() : get_theme_constant(SNAME("shadow_outline_size"));
+			Ref<StyleBox> style = theme_cache.normal_style;
+			Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
+			Color font_color = has_settings ? settings->get_font_color() : theme_cache.font_color;
+			Color font_shadow_color = has_settings ? settings->get_shadow_color() : theme_cache.font_shadow_color;
+			Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : theme_cache.font_shadow_offset;
+			int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing;
+			Color font_outline_color = has_settings ? settings->get_outline_color() : theme_cache.font_outline_color;
+			int outline_size = has_settings ? settings->get_outline_size() : theme_cache.font_outline_size;
+			int shadow_outline_size = has_settings ? settings->get_shadow_size() : theme_cache.font_shadow_outline_size;
 			bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL);
 			bool rtl_layout = is_layout_rtl();
 
@@ -562,12 +578,12 @@ Size2 Label::get_minimum_size() const {
 
 	Size2 min_size = minsize;
 
-	const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
-	int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size"));
+	const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
+	int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size;
 
 	min_size.height = MAX(min_size.height, font->get_height(font_size) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM));
 
-	Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size();
+	Size2 min_style = theme_cache.normal_style->get_minimum_size();
 	if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
 		return Size2(1, (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? 1 : min_size.height) + min_style;
 	} else {
@@ -590,8 +606,8 @@ int Label::get_line_count() const {
 }
 
 int Label::get_visible_line_count() const {
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
-	int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"));
+	Ref<StyleBox> style = theme_cache.normal_style;
+	int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing;
 	int lines_visible = 0;
 	float total_h = 0.0;
 	for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {

+ 16 - 1
scene/gui/label.h

@@ -67,13 +67,28 @@ private:
 
 	Ref<LabelSettings> settings;
 
+	struct ThemeCache {
+		Ref<StyleBox> normal_style;
+		Ref<Font> font;
+
+		int font_size = 0;
+		int line_spacing = 0;
+		Color font_color;
+		Color font_shadow_color;
+		Point2 font_shadow_offset;
+		Color font_outline_color;
+		int font_outline_size;
+		int font_shadow_outline_size;
+	} theme_cache;
+
 	void _update_visible();
 	void _shape();
 	void _invalidate();
 
 protected:
-	void _notification(int p_what);
+	virtual void _update_theme_item_cache() override;
 
+	void _notification(int p_what);
 	static void _bind_methods();
 
 public:

+ 67 - 36
scene/gui/line_edit.cpp

@@ -448,7 +448,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
 		if (context_menu_enabled) {
 			if (k->is_action("ui_menu", true)) {
 				_ensure_menu();
-				Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")))) / 2);
+				Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2);
 				menu->set_position(get_screen_position() + pos);
 				menu->reset_size();
 				menu->popup();
@@ -696,11 +696,38 @@ bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const {
 	if (!clear_button_enabled || !has_point(p_pos)) {
 		return false;
 	}
-	Ref<Texture2D> icon = Control::get_theme_icon(SNAME("clear"));
-	int x_ofs = get_theme_stylebox(SNAME("normal"))->get_margin(SIDE_RIGHT);
+	Ref<Texture2D> icon = theme_cache.clear_icon;
+	int x_ofs = theme_cache.normal->get_margin(SIDE_RIGHT);
 	return p_pos.x > get_size().width - icon->get_width() - x_ofs;
 }
 
+void LineEdit::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.normal = get_theme_stylebox(SNAME("normal"));
+	theme_cache.read_only = get_theme_stylebox(SNAME("read_only"));
+	theme_cache.focus = get_theme_stylebox(SNAME("focus"));
+
+	theme_cache.font = get_theme_font(SNAME("font"));
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_uneditable_color = get_theme_color(SNAME("font_uneditable_color"));
+	theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color"));
+	theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+	theme_cache.font_placeholder_color = get_theme_color(SNAME("font_placeholder_color"));
+	theme_cache.caret_width = get_theme_constant(SNAME("caret_width"));
+	theme_cache.caret_color = get_theme_color(SNAME("caret_color"));
+	theme_cache.minimum_character_width = get_theme_constant(SNAME("minimum_character_width"));
+	theme_cache.selection_color = get_theme_color(SNAME("selection_color"));
+
+	theme_cache.clear_icon = get_theme_icon(SNAME("clear"));
+	theme_cache.clear_button_color = get_theme_color(SNAME("clear_button_color"));
+	theme_cache.clear_button_color_pressed = get_theme_color(SNAME("clear_button_color_pressed"));
+
+	theme_cache.base_scale = get_theme_default_base_scale();
+}
+
 void LineEdit::_notification(int p_what) {
 	switch (p_what) {
 #ifdef TOOLS_ENABLED
@@ -771,19 +798,19 @@ void LineEdit::_notification(int p_what) {
 
 			RID ci = get_canvas_item();
 
-			Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+			Ref<StyleBox> style = theme_cache.normal;
 			if (!is_editable()) {
-				style = get_theme_stylebox(SNAME("read_only"));
+				style = theme_cache.read_only;
 				draw_caret = false;
 			}
-			Ref<Font> font = get_theme_font(SNAME("font"));
+			Ref<Font> font = theme_cache.font;
 
 			if (!flat) {
 				style->draw(ci, Rect2(Point2(), size));
 			}
 
 			if (has_focus()) {
-				get_theme_stylebox(SNAME("focus"))->draw(ci, Rect2(Point2(), size));
+				theme_cache.focus->draw(ci, Rect2(Point2(), size));
 			}
 
 			int x_ofs = 0;
@@ -821,25 +848,30 @@ void LineEdit::_notification(int p_what) {
 			int y_area = height - style->get_minimum_size().height;
 			int y_ofs = style->get_offset().y + (y_area - text_height) / 2;
 
-			Color selection_color = get_theme_color(SNAME("selection_color"));
-			Color font_color = get_theme_color(is_editable() ? SNAME("font_color") : SNAME("font_uneditable_color"));
-			Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
-			Color caret_color = get_theme_color(SNAME("caret_color"));
+			Color selection_color = theme_cache.selection_color;
+			Color font_color;
+			if (is_editable()) {
+				font_color = theme_cache.font_color;
+			} else {
+				font_color = theme_cache.font_uneditable_color;
+			}
+			Color font_selected_color = theme_cache.font_selected_color;
+			Color caret_color = theme_cache.caret_color;
 
 			// Draw placeholder color.
 			if (using_placeholder) {
-				font_color = get_theme_color(SNAME("font_placeholder_color"));
+				font_color = theme_cache.font_placeholder_color;
 			}
 
 			bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
 			if (right_icon.is_valid() || display_clear_icon) {
-				Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
+				Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
 				Color color_icon(1, 1, 1, !is_editable() ? .5 * .9 : .9);
 				if (display_clear_icon) {
 					if (clear_button_status.press_attempt && clear_button_status.pressing_inside) {
-						color_icon = get_theme_color(SNAME("clear_button_color_pressed"));
+						color_icon = theme_cache.clear_button_color_pressed;
 					} else {
-						color_icon = get_theme_color(SNAME("clear_button_color"));
+						color_icon = theme_cache.clear_button_color;
 					}
 				}
 
@@ -879,8 +911,8 @@ void LineEdit::_notification(int p_what) {
 
 			// Draw text.
 			ofs.y += TS->shaped_text_get_ascent(text_rid);
-			Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
-			int outline_size = get_theme_constant(SNAME("outline_size"));
+			Color font_outline_color = theme_cache.font_outline_color;
+			int outline_size = theme_cache.font_outline_size;
 			if (outline_size > 0 && font_outline_color.a > 0) {
 				Vector2 oofs = ofs;
 				for (int i = 0; i < gl_size; i++) {
@@ -918,7 +950,7 @@ void LineEdit::_notification(int p_what) {
 			ofs.x = x_ofs + scroll_offset;
 			if (draw_caret || drag_caret_force_displayed) {
 				// Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1).
-				const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale());
+				const int caret_width = theme_cache.caret_width * MAX(1, theme_cache.base_scale);
 
 				if (ime_text.length() == 0) {
 					// Normal caret.
@@ -926,7 +958,7 @@ void LineEdit::_notification(int p_what) {
 
 					if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) {
 						// No carets, add one at the start.
-						int h = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")));
+						int h = theme_cache.font->get_height(theme_cache.font_size);
 						int y = style->get_offset().y + (y_area - h) / 2;
 						if (rtl) {
 							caret.l_dir = TextServer::DIRECTION_RTL;
@@ -1193,7 +1225,7 @@ void LineEdit::shift_selection_check_post(bool p_shift) {
 }
 
 void LineEdit::set_caret_at_pixel_pos(int p_x) {
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+	Ref<StyleBox> style = theme_cache.normal;
 	bool rtl = is_layout_rtl();
 
 	int x_ofs = 0;
@@ -1226,7 +1258,7 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) {
 	bool using_placeholder = text.is_empty() && ime_text.is_empty();
 	bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
 	if (right_icon.is_valid() || display_clear_icon) {
-		Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
+		Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
 		if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
 			if (Math::is_zero_approx(scroll_offset)) {
 				x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
@@ -1241,7 +1273,7 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) {
 }
 
 Vector2 LineEdit::get_caret_pixel_pos() {
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+	Ref<StyleBox> style = theme_cache.normal;
 	bool rtl = is_layout_rtl();
 
 	int x_ofs = 0;
@@ -1274,7 +1306,7 @@ Vector2 LineEdit::get_caret_pixel_pos() {
 	bool using_placeholder = text.is_empty() && ime_text.is_empty();
 	bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
 	if (right_icon.is_valid() || display_clear_icon) {
-		Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
+		Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
 		if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
 			if (Math::is_zero_approx(scroll_offset)) {
 				x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
@@ -1559,7 +1591,7 @@ void LineEdit::set_caret_column(int p_column) {
 		return;
 	}
 
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+	Ref<StyleBox> style = theme_cache.normal;
 	bool rtl = is_layout_rtl();
 
 	int x_ofs = 0;
@@ -1593,7 +1625,7 @@ void LineEdit::set_caret_column(int p_column) {
 	bool using_placeholder = text.is_empty() && ime_text.is_empty();
 	bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
 	if (right_icon.is_valid() || display_clear_icon) {
-		Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
+		Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
 		if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
 			if (Math::is_zero_approx(scroll_offset)) {
 				x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
@@ -1664,15 +1696,15 @@ void LineEdit::clear_internal() {
 }
 
 Size2 LineEdit::get_minimum_size() const {
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
-	Ref<Font> font = get_theme_font(SNAME("font"));
-	int font_size = get_theme_font_size(SNAME("font_size"));
+	Ref<StyleBox> style = theme_cache.normal;
+	Ref<Font> font = theme_cache.font;
+	int font_size = theme_cache.font_size;
 
 	Size2 min_size;
 
 	// Minimum size of text.
 	float em_space_size = font->get_char_size('M', font_size).x;
-	min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size;
+	min_size.width = theme_cache.minimum_character_width * em_space_size;
 
 	if (expand_to_text_length) {
 		// Add a space because some fonts are too exact, and because caret needs a bit more when at the end.
@@ -1688,9 +1720,8 @@ Size2 LineEdit::get_minimum_size() const {
 		icon_max_width = right_icon->get_width();
 	}
 	if (clear_button_enabled) {
-		Ref<Texture2D> clear_icon = Control::get_theme_icon(SNAME("clear"));
-		min_size.height = MAX(min_size.height, clear_icon->get_height());
-		icon_max_width = MAX(icon_max_width, clear_icon->get_width());
+		min_size.height = MAX(min_size.height, theme_cache.clear_icon->get_height());
+		icon_max_width = MAX(icon_max_width, theme_cache.clear_icon->get_width());
 	}
 	min_size.width += icon_max_width;
 
@@ -2155,8 +2186,8 @@ void LineEdit::_shape() {
 	}
 	TS->shaped_text_set_preserve_control(text_rid, draw_control_chars);
 
-	const Ref<Font> &font = get_theme_font(SNAME("font"));
-	int font_size = get_theme_font_size(SNAME("font_size"));
+	const Ref<Font> &font = theme_cache.font;
+	int font_size = theme_cache.font_size;
 	ERR_FAIL_COND(font.is_null());
 	TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language);
 	for (int i = 0; i < TextServer::SPACING_MAX; i++) {
@@ -2176,12 +2207,12 @@ void LineEdit::_shape() {
 
 void LineEdit::_fit_to_width() {
 	if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
-		Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+		Ref<StyleBox> style = theme_cache.normal;
 		int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT);
 		bool using_placeholder = text.is_empty() && ime_text.is_empty();
 		bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
 		if (right_icon.is_valid() || display_clear_icon) {
-			Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
+			Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
 			t_width -= r_icon->get_width();
 		}
 		TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width));

+ 26 - 0
scene/gui/line_edit.h

@@ -174,6 +174,31 @@ private:
 	double caret_blink_timer = 0.0;
 	bool caret_blinking = false;
 
+	struct ThemeCache {
+		Ref<StyleBox> normal;
+		Ref<StyleBox> read_only;
+		Ref<StyleBox> focus;
+
+		Ref<Font> font;
+		int font_size = 0;
+		Color font_color;
+		Color font_uneditable_color;
+		Color font_selected_color;
+		int font_outline_size;
+		Color font_outline_color;
+		Color font_placeholder_color;
+		int caret_width = 0;
+		Color caret_color;
+		int minimum_character_width = 0;
+		Color selection_color;
+
+		Ref<Texture2D> clear_icon;
+		Color clear_button_color;
+		Color clear_button_color_pressed;
+
+		int base_scale = 0;
+	} theme_cache;
+
 	bool _is_over_clear_button(const Point2 &p_pos) const;
 
 	void _clear_undo_stack();
@@ -215,6 +240,7 @@ private:
 	void _ensure_menu();
 
 protected:
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 	static void _bind_methods();
 	virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;

+ 32 - 12
scene/gui/link_button.cpp

@@ -33,8 +33,8 @@
 #include "core/string/translation.h"
 
 void LinkButton::_shape() {
-	Ref<Font> font = get_theme_font(SNAME("font"));
-	int font_size = get_theme_font_size(SNAME("font_size"));
+	Ref<Font> font = theme_cache.font;
+	int font_size = theme_cache.font_size;
 
 	text_buf->clear();
 	if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
@@ -125,6 +125,26 @@ Size2 LinkButton::get_minimum_size() const {
 	return text_buf->get_size();
 }
 
+void LinkButton::_update_theme_item_cache() {
+	BaseButton::_update_theme_item_cache();
+
+	theme_cache.focus = get_theme_stylebox(SNAME("focus"));
+
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color"));
+	theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color"));
+	theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color"));
+	theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color"));
+	theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
+
+	theme_cache.font = get_theme_font(SNAME("font"));
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+
+	theme_cache.underline_spacing = get_theme_constant(SNAME("underline_spacing"));
+}
+
 void LinkButton::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_TRANSLATION_CHANGED: {
@@ -153,9 +173,9 @@ void LinkButton::_notification(int p_what) {
 			switch (get_draw_mode()) {
 				case DRAW_NORMAL: {
 					if (has_focus()) {
-						color = get_theme_color(SNAME("font_focus_color"));
+						color = theme_cache.font_focus_color;
 					} else {
-						color = get_theme_color(SNAME("font_color"));
+						color = theme_cache.font_color;
 					}
 
 					do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
@@ -163,35 +183,35 @@ void LinkButton::_notification(int p_what) {
 				case DRAW_HOVER_PRESSED:
 				case DRAW_PRESSED: {
 					if (has_theme_color(SNAME("font_pressed_color"))) {
-						color = get_theme_color(SNAME("font_pressed_color"));
+						color = theme_cache.font_pressed_color;
 					} else {
-						color = get_theme_color(SNAME("font_color"));
+						color = theme_cache.font_color;
 					}
 
 					do_underline = underline_mode != UNDERLINE_MODE_NEVER;
 
 				} break;
 				case DRAW_HOVER: {
-					color = get_theme_color(SNAME("font_hover_color"));
+					color = theme_cache.font_hover_color;
 					do_underline = underline_mode != UNDERLINE_MODE_NEVER;
 
 				} break;
 				case DRAW_DISABLED: {
-					color = get_theme_color(SNAME("font_disabled_color"));
+					color = theme_cache.font_disabled_color;
 					do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
 
 				} break;
 			}
 
 			if (has_focus()) {
-				Ref<StyleBox> style = get_theme_stylebox(SNAME("focus"));
+				Ref<StyleBox> style = theme_cache.focus;
 				style->draw(ci, Rect2(Point2(), size));
 			}
 
 			int width = text_buf->get_line_width();
 
-			Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
-			int outline_size = get_theme_constant(SNAME("outline_size"));
+			Color font_outline_color = theme_cache.font_outline_color;
+			int outline_size = theme_cache.outline_size;
 			if (is_layout_rtl()) {
 				if (outline_size > 0 && font_outline_color.a > 0) {
 					text_buf->draw_outline(get_canvas_item(), Vector2(size.width - width, 0), outline_size, font_outline_color);
@@ -205,7 +225,7 @@ void LinkButton::_notification(int p_what) {
 			}
 
 			if (do_underline) {
-				int underline_spacing = get_theme_constant(SNAME("underline_spacing")) + text_buf->get_line_underline_position();
+				int underline_spacing = theme_cache.underline_spacing + text_buf->get_line_underline_position();
 				int y = text_buf->get_line_ascent() + underline_spacing;
 
 				if (is_layout_rtl()) {

+ 19 - 0
scene/gui/link_button.h

@@ -55,10 +55,29 @@ private:
 	TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
 	Array st_args;
 
+	struct ThemeCache {
+		Ref<StyleBox> focus;
+
+		Color font_color;
+		Color font_focus_color;
+		Color font_pressed_color;
+		Color font_hover_color;
+		Color font_hover_pressed_color;
+		Color font_disabled_color;
+
+		Ref<Font> font;
+		int font_size = 0;
+		int outline_size = 0;
+		Color font_outline_color;
+
+		int underline_spacing = 0;
+	} theme_cache;
+
 	void _shape();
 
 protected:
 	virtual Size2 get_minimum_size() const override;
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 	static void _bind_methods();
 

+ 14 - 15
scene/gui/margin_container.cpp

@@ -30,12 +30,16 @@
 
 #include "margin_container.h"
 
-Size2 MarginContainer::get_minimum_size() const {
-	int margin_left = get_theme_constant(SNAME("margin_left"));
-	int margin_top = get_theme_constant(SNAME("margin_top"));
-	int margin_right = get_theme_constant(SNAME("margin_right"));
-	int margin_bottom = get_theme_constant(SNAME("margin_bottom"));
+void MarginContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.margin_left = get_theme_constant(SNAME("margin_left"));
+	theme_cache.margin_top = get_theme_constant(SNAME("margin_top"));
+	theme_cache.margin_right = get_theme_constant(SNAME("margin_right"));
+	theme_cache.margin_bottom = get_theme_constant(SNAME("margin_bottom"));
+}
 
+Size2 MarginContainer::get_minimum_size() const {
 	Size2 max;
 
 	for (int i = 0; i < get_child_count(); i++) {
@@ -59,8 +63,8 @@ Size2 MarginContainer::get_minimum_size() const {
 		}
 	}
 
-	max.width += (margin_left + margin_right);
-	max.height += (margin_top + margin_bottom);
+	max.width += (theme_cache.margin_left + theme_cache.margin_right);
+	max.height += (theme_cache.margin_top + theme_cache.margin_bottom);
 
 	return max;
 }
@@ -86,11 +90,6 @@ Vector<int> MarginContainer::get_allowed_size_flags_vertical() const {
 void MarginContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_SORT_CHILDREN: {
-			int margin_left = get_theme_constant(SNAME("margin_left"));
-			int margin_top = get_theme_constant(SNAME("margin_top"));
-			int margin_right = get_theme_constant(SNAME("margin_right"));
-			int margin_bottom = get_theme_constant(SNAME("margin_bottom"));
-
 			Size2 s = get_size();
 
 			for (int i = 0; i < get_child_count(); i++) {
@@ -102,9 +101,9 @@ void MarginContainer::_notification(int p_what) {
 					continue;
 				}
 
-				int w = s.width - margin_left - margin_right;
-				int h = s.height - margin_top - margin_bottom;
-				fit_child_in_rect(c, Rect2(margin_left, margin_top, w, h));
+				int w = s.width - theme_cache.margin_left - theme_cache.margin_right;
+				int h = s.height - theme_cache.margin_top - theme_cache.margin_bottom;
+				fit_child_in_rect(c, Rect2(theme_cache.margin_left, theme_cache.margin_top, w, h));
 			}
 		} break;
 

+ 9 - 0
scene/gui/margin_container.h

@@ -36,7 +36,16 @@
 class MarginContainer : public Container {
 	GDCLASS(MarginContainer, Container);
 
+	struct ThemeCache {
+		int margin_left = 0;
+		int margin_top = 0;
+		int margin_right = 0;
+		int margin_bottom = 0;
+	} theme_cache;
+
 protected:
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 
 public:

+ 56 - 32
scene/gui/menu_bar.cpp

@@ -340,6 +340,35 @@ void MenuBar::_update_menu() {
 	queue_redraw();
 }
 
+void MenuBar::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.normal = get_theme_stylebox(SNAME("normal"));
+	theme_cache.normal_mirrored = get_theme_stylebox(SNAME("normal_mirrored"));
+	theme_cache.disabled = get_theme_stylebox(SNAME("disabled"));
+	theme_cache.disabled_mirrored = get_theme_stylebox(SNAME("disabled_mirrored"));
+	theme_cache.pressed = get_theme_stylebox(SNAME("pressed"));
+	theme_cache.pressed_mirrored = get_theme_stylebox(SNAME("pressed_mirrored"));
+	theme_cache.hover = get_theme_stylebox(SNAME("hover"));
+	theme_cache.hover_mirrored = get_theme_stylebox(SNAME("hover_mirrored"));
+	theme_cache.hover_pressed = get_theme_stylebox(SNAME("hover_pressed"));
+	theme_cache.hover_pressed_mirrored = get_theme_stylebox(SNAME("hover_pressed_mirrored"));
+
+	theme_cache.font = get_theme_font(SNAME("font"));
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
+	theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color"));
+	theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color"));
+	theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color"));
+	theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color"));
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+}
+
 void MenuBar::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE: {
@@ -394,8 +423,7 @@ void MenuBar::_notification(int p_what) {
 }
 
 int MenuBar::_get_index_at_point(const Point2 &p_point) const {
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
-	int hsep = get_theme_constant(SNAME("h_separation"));
+	Ref<StyleBox> style = theme_cache.normal;
 	int offset = 0;
 	for (int i = 0; i < menu_cache.size(); i++) {
 		if (menu_cache[i].hidden) {
@@ -407,7 +435,7 @@ int MenuBar::_get_index_at_point(const Point2 &p_point) const {
 				return i;
 			}
 		}
-		offset += size.x + hsep;
+		offset += size.x + theme_cache.h_separation;
 	}
 	return -1;
 }
@@ -415,8 +443,7 @@ int MenuBar::_get_index_at_point(const Point2 &p_point) const {
 Rect2 MenuBar::_get_menu_item_rect(int p_index) const {
 	ERR_FAIL_INDEX_V(p_index, menu_cache.size(), Rect2());
 
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
-	int hsep = get_theme_constant(SNAME("h_separation"));
+	Ref<StyleBox> style = theme_cache.normal;
 
 	int offset = 0;
 	for (int i = 0; i < p_index; i++) {
@@ -424,7 +451,7 @@ Rect2 MenuBar::_get_menu_item_rect(int p_index) const {
 			continue;
 		}
 		Size2 size = menu_cache[i].text_buf->get_size() + style->get_minimum_size();
-		offset += size.x + hsep;
+		offset += size.x + theme_cache.h_separation;
 	}
 
 	return Rect2(Point2(offset, 0), menu_cache[p_index].text_buf->get_size() + style->get_minimum_size());
@@ -443,76 +470,76 @@ void MenuBar::_draw_menu_item(int p_index) {
 	}
 
 	Color color;
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+	Ref<StyleBox> style = theme_cache.normal;
 	Rect2 item_rect = _get_menu_item_rect(p_index);
 
 	if (menu_cache[p_index].disabled) {
 		if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) {
-			style = get_theme_stylebox(SNAME("disabled_mirrored"));
+			style = theme_cache.disabled_mirrored;
 		} else {
-			style = get_theme_stylebox(SNAME("disabled"));
+			style = theme_cache.disabled;
 		}
 		if (!flat) {
 			style->draw(ci, item_rect);
 		}
-		color = get_theme_color(SNAME("font_disabled_color"));
+		color = theme_cache.font_disabled_color;
 	} else if (hovered && pressed && has_theme_stylebox("hover_pressed")) {
 		if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) {
-			style = get_theme_stylebox(SNAME("hover_pressed_mirrored"));
+			style = theme_cache.hover_pressed_mirrored;
 		} else {
-			style = get_theme_stylebox(SNAME("hover_pressed"));
+			style = theme_cache.hover_pressed;
 		}
 		if (!flat) {
 			style->draw(ci, item_rect);
 		}
 		if (has_theme_color(SNAME("font_hover_pressed_color"))) {
-			color = get_theme_color(SNAME("font_hover_pressed_color"));
+			color = theme_cache.font_hover_pressed_color;
 		}
 	} else if (pressed) {
 		if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) {
-			style = get_theme_stylebox(SNAME("pressed_mirrored"));
+			style = theme_cache.pressed_mirrored;
 		} else {
-			style = get_theme_stylebox(SNAME("pressed"));
+			style = theme_cache.pressed;
 		}
 		if (!flat) {
 			style->draw(ci, item_rect);
 		}
 		if (has_theme_color(SNAME("font_pressed_color"))) {
-			color = get_theme_color(SNAME("font_pressed_color"));
+			color = theme_cache.font_pressed_color;
 		} else {
-			color = get_theme_color(SNAME("font_color"));
+			color = theme_cache.font_color;
 		}
 	} else if (hovered) {
 		if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) {
-			style = get_theme_stylebox(SNAME("hover_mirrored"));
+			style = theme_cache.hover_mirrored;
 		} else {
-			style = get_theme_stylebox(SNAME("hover"));
+			style = theme_cache.hover;
 		}
 		if (!flat) {
 			style->draw(ci, item_rect);
 		}
-		color = get_theme_color(SNAME("font_hover_color"));
+		color = theme_cache.font_hover_color;
 	} else {
 		if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) {
-			style = get_theme_stylebox(SNAME("normal_mirrored"));
+			style = theme_cache.normal_mirrored;
 		} else {
-			style = get_theme_stylebox(SNAME("normal"));
+			style = theme_cache.normal;
 		}
 		if (!flat) {
 			style->draw(ci, item_rect);
 		}
 		// Focus colors only take precedence over normal state.
 		if (has_focus()) {
-			color = get_theme_color(SNAME("font_focus_color"));
+			color = theme_cache.font_focus_color;
 		} else {
-			color = get_theme_color(SNAME("font_color"));
+			color = theme_cache.font_color;
 		}
 	}
 
 	Point2 text_ofs = item_rect.position + Point2(style->get_margin(SIDE_LEFT), style->get_margin(SIDE_TOP));
 
-	Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
-	int outline_size = get_theme_constant(SNAME("outline_size"));
+	Color font_outline_color = theme_cache.font_outline_color;
+	int outline_size = theme_cache.outline_size;
 	if (outline_size > 0 && font_outline_color.a > 0) {
 		menu_cache[p_index].text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color);
 	}
@@ -520,16 +547,13 @@ void MenuBar::_draw_menu_item(int p_index) {
 }
 
 void MenuBar::shape(Menu &p_menu) {
-	Ref<Font> font = get_theme_font(SNAME("font"));
-	int font_size = get_theme_font_size(SNAME("font_size"));
-
 	p_menu.text_buf->clear();
 	if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
 		p_menu.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
 	} else {
 		p_menu.text_buf->set_direction((TextServer::Direction)text_direction);
 	}
-	p_menu.text_buf->add_string(p_menu.name, font, font_size, language);
+	p_menu.text_buf->add_string(p_menu.name, theme_cache.font, theme_cache.font_size, language);
 }
 
 void MenuBar::_refresh_menu_names() {
@@ -759,7 +783,7 @@ Size2 MenuBar::get_minimum_size() const {
 		return Size2();
 	}
 
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+	Ref<StyleBox> style = theme_cache.normal;
 
 	Vector2 size;
 	for (int i = 0; i < menu_cache.size(); i++) {
@@ -771,7 +795,7 @@ Size2 MenuBar::get_minimum_size() const {
 		size.x += sz.x;
 	}
 	if (menu_cache.size() > 1) {
-		size.x += get_theme_constant(SNAME("h_separation")) * (menu_cache.size() - 1);
+		size.x += theme_cache.h_separation * (menu_cache.size() - 1);
 	}
 	return size;
 }

+ 28 - 0
scene/gui/menu_bar.h

@@ -76,6 +76,33 @@ class MenuBar : public Control {
 	Vector2i old_mouse_pos;
 	ObjectID shortcut_context;
 
+	struct ThemeCache {
+		Ref<StyleBox> normal;
+		Ref<StyleBox> normal_mirrored;
+		Ref<StyleBox> disabled;
+		Ref<StyleBox> disabled_mirrored;
+		Ref<StyleBox> pressed;
+		Ref<StyleBox> pressed_mirrored;
+		Ref<StyleBox> hover;
+		Ref<StyleBox> hover_mirrored;
+		Ref<StyleBox> hover_pressed;
+		Ref<StyleBox> hover_pressed_mirrored;
+
+		Ref<Font> font;
+		int font_size = 0;
+		int outline_size = 0;
+		Color font_outline_color;
+
+		Color font_color;
+		Color font_disabled_color;
+		Color font_pressed_color;
+		Color font_hover_color;
+		Color font_hover_pressed_color;
+		Color font_focus_color;
+
+		int h_separation = 0;
+	} theme_cache;
+
 	int _get_index_at_point(const Point2 &p_point) const;
 	Rect2 _get_menu_item_rect(int p_index) const;
 	void _draw_menu_item(int p_index);
@@ -96,6 +123,7 @@ class MenuBar : public Control {
 protected:
 	virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
 
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 	virtual void add_child_notify(Node *p_child) override;
 	virtual void move_child_notify(Node *p_child) override;

+ 44 - 25
scene/gui/option_button.cpp

@@ -43,11 +43,11 @@ Size2 OptionButton::get_minimum_size() const {
 	}
 
 	if (has_theme_icon(SNAME("arrow"))) {
-		const Size2 padding = get_theme_stylebox(SNAME("normal"))->get_minimum_size();
-		const Size2 arrow_size = Control::get_theme_icon(SNAME("arrow"))->get_size();
+		const Size2 padding = theme_cache.normal->get_minimum_size();
+		const Size2 arrow_size = theme_cache.arrow_icon->get_size();
 
 		Size2 content_size = minsize - padding;
-		content_size.width += arrow_size.width + MAX(0, get_theme_constant(SNAME("h_separation")));
+		content_size.width += arrow_size.width + MAX(0, theme_cache.h_separation);
 		content_size.height = MAX(content_size.height, arrow_size.height);
 
 		minsize = content_size + padding;
@@ -56,35 +56,63 @@ Size2 OptionButton::get_minimum_size() const {
 	return minsize;
 }
 
+void OptionButton::_update_theme_item_cache() {
+	Button::_update_theme_item_cache();
+
+	theme_cache.normal = get_theme_stylebox(SNAME("normal"));
+
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color"));
+	theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color"));
+	theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color"));
+	theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color"));
+	theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+
+	theme_cache.arrow_icon = get_theme_icon(SNAME("arrow"));
+	theme_cache.arrow_margin = get_theme_constant(SNAME("arrow_margin"));
+	theme_cache.modulate_arrow = get_theme_constant(SNAME("modulate_arrow"));
+}
+
 void OptionButton::_notification(int p_what) {
 	switch (p_what) {
+		case NOTIFICATION_POSTINITIALIZE: {
+			if (has_theme_icon(SNAME("arrow"))) {
+				if (is_layout_rtl()) {
+					_set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width());
+				} else {
+					_set_internal_margin(SIDE_RIGHT, theme_cache.arrow_icon->get_width());
+				}
+			}
+		} break;
+
 		case NOTIFICATION_DRAW: {
 			if (!has_theme_icon(SNAME("arrow"))) {
 				return;
 			}
 
 			RID ci = get_canvas_item();
-			Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"));
 			Color clr = Color(1, 1, 1);
-			if (get_theme_constant(SNAME("modulate_arrow"))) {
+			if (theme_cache.modulate_arrow) {
 				switch (get_draw_mode()) {
 					case DRAW_PRESSED:
-						clr = get_theme_color(SNAME("font_pressed_color"));
+						clr = theme_cache.font_pressed_color;
 						break;
 					case DRAW_HOVER:
-						clr = get_theme_color(SNAME("font_hover_color"));
+						clr = theme_cache.font_hover_color;
 						break;
 					case DRAW_HOVER_PRESSED:
-						clr = get_theme_color(SNAME("font_hover_pressed_color"));
+						clr = theme_cache.font_hover_pressed_color;
 						break;
 					case DRAW_DISABLED:
-						clr = get_theme_color(SNAME("font_disabled_color"));
+						clr = theme_cache.font_disabled_color;
 						break;
 					default:
 						if (has_focus()) {
-							clr = get_theme_color(SNAME("font_focus_color"));
+							clr = theme_cache.font_focus_color;
 						} else {
-							clr = get_theme_color(SNAME("font_color"));
+							clr = theme_cache.font_color;
 						}
 				}
 			}
@@ -93,11 +121,11 @@ void OptionButton::_notification(int p_what) {
 
 			Point2 ofs;
 			if (is_layout_rtl()) {
-				ofs = Point2(get_theme_constant(SNAME("arrow_margin")), int(Math::abs((size.height - arrow->get_height()) / 2)));
+				ofs = Point2(theme_cache.arrow_margin, int(Math::abs((size.height - theme_cache.arrow_icon->get_height()) / 2)));
 			} else {
-				ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin")), int(Math::abs((size.height - arrow->get_height()) / 2)));
+				ofs = Point2(size.width - theme_cache.arrow_icon->get_width() - theme_cache.arrow_margin, int(Math::abs((size.height - theme_cache.arrow_icon->get_height()) / 2)));
 			}
-			arrow->draw(ci, ofs, clr);
+			theme_cache.arrow_icon->draw(ci, ofs, clr);
 		} break;
 
 		case NOTIFICATION_TRANSLATION_CHANGED:
@@ -108,11 +136,11 @@ void OptionButton::_notification(int p_what) {
 		case NOTIFICATION_THEME_CHANGED: {
 			if (has_theme_icon(SNAME("arrow"))) {
 				if (is_layout_rtl()) {
-					_set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width());
+					_set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width());
 					_set_internal_margin(SIDE_RIGHT, 0.f);
 				} else {
 					_set_internal_margin(SIDE_LEFT, 0.f);
-					_set_internal_margin(SIDE_RIGHT, Control::get_theme_icon(SNAME("arrow"))->get_width());
+					_set_internal_margin(SIDE_RIGHT, theme_cache.arrow_icon->get_width());
 				}
 			}
 			_refresh_size_cache();
@@ -540,15 +568,6 @@ OptionButton::OptionButton(const String &p_text) :
 		Button(p_text) {
 	set_toggle_mode(true);
 	set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
-	if (is_layout_rtl()) {
-		if (has_theme_icon(SNAME("arrow"))) {
-			_set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width());
-		}
-	} else {
-		if (has_theme_icon(SNAME("arrow"))) {
-			_set_internal_margin(SIDE_RIGHT, Control::get_theme_icon(SNAME("arrow"))->get_width());
-		}
-	}
 	set_action_mode(ACTION_MODE_BUTTON_PRESS);
 
 	popup = memnew(PopupMenu);

+ 18 - 0
scene/gui/option_button.h

@@ -43,6 +43,23 @@ class OptionButton : public Button {
 	Vector2 _cached_size;
 	bool cache_refresh_pending = false;
 
+	struct ThemeCache {
+		Ref<StyleBox> normal;
+
+		Color font_color;
+		Color font_focus_color;
+		Color font_pressed_color;
+		Color font_hover_color;
+		Color font_hover_pressed_color;
+		Color font_disabled_color;
+
+		int h_separation = 0;
+
+		Ref<Texture2D> arrow_icon;
+		int arrow_margin = 0;
+		int modulate_arrow = 0;
+	} theme_cache;
+
 	void _focused(int p_which);
 	void _selected(int p_which);
 	void _select(int p_which, bool p_emit = false);
@@ -54,6 +71,7 @@ class OptionButton : public Button {
 
 protected:
 	Size2 get_minimum_size() const override;
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;

+ 7 - 2
scene/gui/panel.cpp

@@ -30,12 +30,17 @@
 
 #include "panel.h"
 
+void Panel::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.panel_style = get_theme_stylebox(SNAME("panel"));
+}
+
 void Panel::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
 			RID ci = get_canvas_item();
-			Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
-			style->draw(ci, Rect2(Point2(), get_size()));
+			theme_cache.panel_style->draw(ci, Rect2(Point2(), get_size()));
 		} break;
 	}
 }

+ 6 - 0
scene/gui/panel.h

@@ -36,7 +36,13 @@
 class Panel : public Control {
 	GDCLASS(Panel, Control);
 
+	struct ThemeCache {
+		Ref<StyleBox> panel_style;
+	} theme_cache;
+
 protected:
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 
 public:

+ 12 - 30
scene/gui/panel_container.cpp

@@ -31,14 +31,6 @@
 #include "panel_container.h"
 
 Size2 PanelContainer::get_minimum_size() const {
-	Ref<StyleBox> style;
-
-	if (has_theme_stylebox(SNAME("panel"))) {
-		style = get_theme_stylebox(SNAME("panel"));
-	} else {
-		style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
-	}
-
 	Size2 ms;
 	for (int i = 0; i < get_child_count(); i++) {
 		Control *c = Object::cast_to<Control>(get_child(i));
@@ -54,8 +46,8 @@ Size2 PanelContainer::get_minimum_size() const {
 		ms.height = MAX(ms.height, minsize.height);
 	}
 
-	if (style.is_valid()) {
-		ms += style->get_minimum_size();
+	if (theme_cache.panel_style.is_valid()) {
+		ms += theme_cache.panel_style->get_minimum_size();
 	}
 	return ms;
 }
@@ -78,35 +70,25 @@ Vector<int> PanelContainer::get_allowed_size_flags_vertical() const {
 	return flags;
 }
 
+void PanelContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.panel_style = get_theme_stylebox(SNAME("panel"));
+}
+
 void PanelContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
 			RID ci = get_canvas_item();
-			Ref<StyleBox> style;
-
-			if (has_theme_stylebox(SNAME("panel"))) {
-				style = get_theme_stylebox(SNAME("panel"));
-			} else {
-				style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
-			}
-
-			style->draw(ci, Rect2(Point2(), get_size()));
+			theme_cache.panel_style->draw(ci, Rect2(Point2(), get_size()));
 		} break;
 
 		case NOTIFICATION_SORT_CHILDREN: {
-			Ref<StyleBox> style;
-
-			if (has_theme_stylebox(SNAME("panel"))) {
-				style = get_theme_stylebox(SNAME("panel"));
-			} else {
-				style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
-			}
-
 			Size2 size = get_size();
 			Point2 ofs;
-			if (style.is_valid()) {
-				size -= style->get_minimum_size();
-				ofs += style->get_offset();
+			if (theme_cache.panel_style.is_valid()) {
+				size -= theme_cache.panel_style->get_minimum_size();
+				ofs += theme_cache.panel_style->get_offset();
 			}
 
 			for (int i = 0; i < get_child_count(); i++) {

+ 5 - 0
scene/gui/panel_container.h

@@ -36,7 +36,12 @@
 class PanelContainer : public Container {
 	GDCLASS(PanelContainer, Container);
 
+	struct ThemeCache {
+		Ref<StyleBox> panel_style;
+	} theme_cache;
+
 protected:
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 
 public:

+ 31 - 29
scene/gui/progress_bar.cpp

@@ -33,18 +33,13 @@
 #include "scene/resources/text_line.h"
 
 Size2 ProgressBar::get_minimum_size() const {
-	Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
-	Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"));
-	Ref<Font> font = get_theme_font(SNAME("font"));
-	int font_size = get_theme_font_size(SNAME("font_size"));
-
-	Size2 minimum_size = bg->get_minimum_size();
-	minimum_size.height = MAX(minimum_size.height, fg->get_minimum_size().height);
-	minimum_size.width = MAX(minimum_size.width, fg->get_minimum_size().width);
+	Size2 minimum_size = theme_cache.bg_style->get_minimum_size();
+	minimum_size.height = MAX(minimum_size.height, theme_cache.fg_style->get_minimum_size().height);
+	minimum_size.width = MAX(minimum_size.width, theme_cache.fg_style->get_minimum_size().width);
 	if (percent_visible) {
 		String txt = "100%";
-		TextLine tl = TextLine(txt, font, font_size);
-		minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + tl.get_size().y);
+		TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size);
+		minimum_size.height = MAX(minimum_size.height, theme_cache.bg_style->get_minimum_size().height + tl.get_size().y);
 	} else { // this is needed, else the progressbar will collapse
 		minimum_size.width = MAX(minimum_size.width, 1);
 		minimum_size.height = MAX(minimum_size.height, 1);
@@ -52,23 +47,30 @@ Size2 ProgressBar::get_minimum_size() const {
 	return minimum_size;
 }
 
+void ProgressBar::_update_theme_item_cache() {
+	Range::_update_theme_item_cache();
+
+	theme_cache.bg_style = get_theme_stylebox(SNAME("bg"));
+	theme_cache.fg_style = get_theme_stylebox(SNAME("fg"));
+
+	theme_cache.font = get_theme_font(SNAME("font"));
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.font_color = get_theme_color(SNAME("font_color"));
+	theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+}
+
 void ProgressBar::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
-			Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
-			Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"));
-			Ref<Font> font = get_theme_font(SNAME("font"));
-			int font_size = get_theme_font_size(SNAME("font_size"));
-			Color font_color = get_theme_color(SNAME("font_color"));
-
-			draw_style_box(bg, Rect2(Point2(), get_size()));
+			draw_style_box(theme_cache.bg_style, Rect2(Point2(), get_size()));
 
 			float r = get_as_ratio();
 
 			switch (mode) {
 				case FILL_BEGIN_TO_END:
 				case FILL_END_TO_BEGIN: {
-					int mp = fg->get_minimum_size().width;
+					int mp = theme_cache.fg_style->get_minimum_size().width;
 					int p = round(r * (get_size().width - mp));
 					// We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL,
 					// and left to right otherwise. And likewise for FILL_END_TO_BEGIN.
@@ -76,23 +78,23 @@ void ProgressBar::_notification(int p_what) {
 					if (p > 0) {
 						if (right_to_left) {
 							int p_remaining = round((1.0 - r) * (get_size().width - mp));
-							draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+							draw_style_box(theme_cache.fg_style, Rect2(Point2(p_remaining, 0), Size2(p + theme_cache.fg_style->get_minimum_size().width, get_size().height)));
 						} else {
-							draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+							draw_style_box(theme_cache.fg_style, Rect2(Point2(0, 0), Size2(p + theme_cache.fg_style->get_minimum_size().width, get_size().height)));
 						}
 					}
 				} break;
 				case FILL_TOP_TO_BOTTOM:
 				case FILL_BOTTOM_TO_TOP: {
-					int mp = fg->get_minimum_size().height;
+					int mp = theme_cache.fg_style->get_minimum_size().height;
 					int p = round(r * (get_size().height - mp));
 
 					if (p > 0) {
 						if (mode == FILL_TOP_TO_BOTTOM) {
-							draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height)));
+							draw_style_box(theme_cache.fg_style, Rect2(Point2(0, 0), Size2(get_size().width, p + theme_cache.fg_style->get_minimum_size().height)));
 						} else {
 							int p_remaining = round((1.0 - r) * (get_size().height - mp));
-							draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height)));
+							draw_style_box(theme_cache.fg_style, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + theme_cache.fg_style->get_minimum_size().height)));
 						}
 					}
 				} break;
@@ -102,14 +104,14 @@ void ProgressBar::_notification(int p_what) {
 
 			if (percent_visible) {
 				String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign();
-				TextLine tl = TextLine(txt, font, font_size);
+				TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size);
 				Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round();
-				Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
-				int outline_size = get_theme_constant(SNAME("outline_size"));
-				if (outline_size > 0 && font_outline_color.a > 0) {
-					tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color);
+
+				if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
+					tl.draw_outline(get_canvas_item(), text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color);
 				}
-				tl.draw(get_canvas_item(), text_pos, font_color);
+
+				tl.draw(get_canvas_item(), text_pos, theme_cache.font_color);
 			}
 		} break;
 	}

+ 13 - 0
scene/gui/progress_bar.h

@@ -38,7 +38,20 @@ class ProgressBar : public Range {
 
 	bool percent_visible = true;
 
+	struct ThemeCache {
+		Ref<StyleBox> bg_style;
+		Ref<StyleBox> fg_style;
+
+		Ref<Font> font;
+		int font_size = 0;
+		Color font_color;
+		int font_outline_size = 0;
+		Color font_outline_color;
+	} theme_cache;
+
 protected:
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 	static void _bind_methods();
 

+ 47 - 29
scene/gui/scroll_bar.cpp

@@ -70,8 +70,8 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
 
 		if (b->is_pressed()) {
 			double ofs = orientation == VERTICAL ? b->get_position().y : b->get_position().x;
-			Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-			Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
+			Ref<Texture2D> decr = theme_cache.decrement_icon;
+			Ref<Texture2D> incr = theme_cache.increment_icon;
 
 			double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width();
 			double incr_size = orientation == VERTICAL ? incr->get_height() : incr->get_width();
@@ -146,7 +146,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
 
 		if (drag.active) {
 			double ofs = orientation == VERTICAL ? m->get_position().y : m->get_position().x;
-			Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
+			Ref<Texture2D> decr = theme_cache.decrement_icon;
 
 			double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width();
 			ofs -= decr_size;
@@ -156,8 +156,8 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
 			set_as_ratio(drag.value_at_click + diff);
 		} else {
 			double ofs = orientation == VERTICAL ? m->get_position().y : m->get_position().x;
-			Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-			Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
+			Ref<Texture2D> decr = theme_cache.decrement_icon;
+			Ref<Texture2D> incr = theme_cache.increment_icon;
 
 			double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width();
 			double incr_size = orientation == VERTICAL ? incr->get_height() : incr->get_width();
@@ -217,6 +217,24 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
 	}
 }
 
+void ScrollBar::_update_theme_item_cache() {
+	Range::_update_theme_item_cache();
+
+	theme_cache.scroll_style = get_theme_stylebox(SNAME("scroll"));
+	theme_cache.scroll_focus_style = get_theme_stylebox(SNAME("scroll_focus"));
+	theme_cache.scroll_offset_style = get_theme_stylebox(SNAME("hscroll"));
+	theme_cache.grabber_style = get_theme_stylebox(SNAME("grabber"));
+	theme_cache.grabber_hl_style = get_theme_stylebox(SNAME("grabber_highlight"));
+	theme_cache.grabber_pressed_style = get_theme_stylebox(SNAME("grabber_pressed"));
+
+	theme_cache.increment_icon = get_theme_icon(SNAME("increment"));
+	theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight"));
+	theme_cache.increment_pressed_icon = get_theme_icon(SNAME("increment_pressed"));
+	theme_cache.decrement_icon = get_theme_icon(SNAME("decrement"));
+	theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight"));
+	theme_cache.decrement_pressed_icon = get_theme_icon(SNAME("decrement_pressed"));
+}
+
 void ScrollBar::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
@@ -225,30 +243,30 @@ void ScrollBar::_notification(int p_what) {
 			Ref<Texture2D> decr, incr;
 
 			if (decr_active) {
-				decr = get_theme_icon(SNAME("decrement_pressed"));
+				decr = theme_cache.decrement_pressed_icon;
 			} else if (highlight == HIGHLIGHT_DECR) {
-				decr = get_theme_icon(SNAME("decrement_highlight"));
+				decr = theme_cache.decrement_hl_icon;
 			} else {
-				decr = get_theme_icon(SNAME("decrement"));
+				decr = theme_cache.decrement_icon;
 			}
 
 			if (incr_active) {
-				incr = get_theme_icon(SNAME("increment_pressed"));
+				incr = theme_cache.increment_pressed_icon;
 			} else if (highlight == HIGHLIGHT_INCR) {
-				incr = get_theme_icon(SNAME("increment_highlight"));
+				incr = theme_cache.increment_hl_icon;
 			} else {
-				incr = get_theme_icon(SNAME("increment"));
+				incr = theme_cache.increment_icon;
 			}
 
-			Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll"));
+			Ref<StyleBox> bg = has_focus() ? theme_cache.scroll_focus_style : theme_cache.scroll_style;
 
 			Ref<StyleBox> grabber;
 			if (drag.active) {
-				grabber = get_theme_stylebox(SNAME("grabber_pressed"));
+				grabber = theme_cache.grabber_pressed_style;
 			} else if (highlight == HIGHLIGHT_RANGE) {
-				grabber = get_theme_stylebox(SNAME("grabber_highlight"));
+				grabber = theme_cache.grabber_hl_style;
 			} else {
-				grabber = get_theme_stylebox(SNAME("grabber"));
+				grabber = theme_cache.grabber_style;
 			}
 
 			Point2 ofs;
@@ -414,7 +432,7 @@ void ScrollBar::_notification(int p_what) {
 }
 
 double ScrollBar::get_grabber_min_size() const {
-	Ref<StyleBox> grabber = get_theme_stylebox(SNAME("grabber"));
+	Ref<StyleBox> grabber = theme_cache.grabber_style;
 	Size2 gminsize = grabber->get_minimum_size() + grabber->get_center_size();
 	return (orientation == VERTICAL) ? gminsize.height : gminsize.width;
 }
@@ -435,17 +453,17 @@ double ScrollBar::get_area_size() const {
 	switch (orientation) {
 		case VERTICAL: {
 			double area = get_size().height;
-			area -= get_theme_stylebox(SNAME("scroll"))->get_minimum_size().height;
-			area -= get_theme_icon(SNAME("increment"))->get_height();
-			area -= get_theme_icon(SNAME("decrement"))->get_height();
+			area -= theme_cache.scroll_style->get_minimum_size().height;
+			area -= theme_cache.increment_icon->get_height();
+			area -= theme_cache.decrement_icon->get_height();
 			area -= get_grabber_min_size();
 			return area;
 		} break;
 		case HORIZONTAL: {
 			double area = get_size().width;
-			area -= get_theme_stylebox(SNAME("scroll"))->get_minimum_size().width;
-			area -= get_theme_icon(SNAME("increment"))->get_width();
-			area -= get_theme_icon(SNAME("decrement"))->get_width();
+			area -= theme_cache.scroll_style->get_minimum_size().width;
+			area -= theme_cache.increment_icon->get_width();
+			area -= theme_cache.decrement_icon->get_width();
 			area -= get_grabber_min_size();
 			return area;
 		} break;
@@ -459,13 +477,13 @@ double ScrollBar::get_area_offset() const {
 	double ofs = 0.0;
 
 	if (orientation == VERTICAL) {
-		ofs += get_theme_stylebox(SNAME("hscroll"))->get_margin(SIDE_TOP);
-		ofs += get_theme_icon(SNAME("decrement"))->get_height();
+		ofs += theme_cache.scroll_offset_style->get_margin(SIDE_TOP);
+		ofs += theme_cache.decrement_icon->get_height();
 	}
 
 	if (orientation == HORIZONTAL) {
-		ofs += get_theme_stylebox(SNAME("hscroll"))->get_margin(SIDE_LEFT);
-		ofs += get_theme_icon(SNAME("decrement"))->get_width();
+		ofs += theme_cache.scroll_offset_style->get_margin(SIDE_LEFT);
+		ofs += theme_cache.decrement_icon->get_width();
 	}
 
 	return ofs;
@@ -476,9 +494,9 @@ double ScrollBar::get_grabber_offset() const {
 }
 
 Size2 ScrollBar::get_minimum_size() const {
-	Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
-	Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-	Ref<StyleBox> bg = get_theme_stylebox(SNAME("scroll"));
+	Ref<Texture2D> incr = theme_cache.increment_icon;
+	Ref<Texture2D> decr = theme_cache.decrement_icon;
+	Ref<StyleBox> bg = theme_cache.scroll_style;
 	Size2 minsize;
 
 	if (orientation == VERTICAL) {

+ 18 - 1
scene/gui/scroll_bar.h

@@ -86,14 +86,31 @@ class ScrollBar : public Range {
 	double target_scroll = 0.0;
 	bool smooth_scroll_enabled = false;
 
+	struct ThemeCache {
+		Ref<StyleBox> scroll_style;
+		Ref<StyleBox> scroll_focus_style;
+		Ref<StyleBox> scroll_offset_style;
+		Ref<StyleBox> grabber_style;
+		Ref<StyleBox> grabber_hl_style;
+		Ref<StyleBox> grabber_pressed_style;
+
+		Ref<Texture2D> increment_icon;
+		Ref<Texture2D> increment_hl_icon;
+		Ref<Texture2D> increment_pressed_icon;
+		Ref<Texture2D> decrement_icon;
+		Ref<Texture2D> decrement_hl_icon;
+		Ref<Texture2D> decrement_pressed_icon;
+	} theme_cache;
+
 	void _drag_node_exit();
 	void _drag_node_input(const Ref<InputEvent> &p_input);
 
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
 
 protected:
-	void _notification(int p_what);
+	virtual void _update_theme_item_cache() override;
 
+	void _notification(int p_what);
 	static void _bind_methods();
 
 public:

+ 11 - 9
scene/gui/scroll_container.cpp

@@ -35,7 +35,6 @@
 #include "scene/main/window.h"
 
 Size2 ScrollContainer::get_minimum_size() const {
-	Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
 	Size2 min_size;
 
 	// Calculated in this function, as it needs to traverse all child controls once to calculate;
@@ -77,10 +76,16 @@ Size2 ScrollContainer::get_minimum_size() const {
 		min_size.x += v_scroll->get_minimum_size().x;
 	}
 
-	min_size += sb->get_minimum_size();
+	min_size += theme_cache.bg_style->get_minimum_size();
 	return min_size;
 }
 
+void ScrollContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.bg_style = get_theme_stylebox(SNAME("bg"));
+}
+
 void ScrollContainer::_cancel_drag() {
 	set_physics_process_internal(false);
 	drag_touching_deaccel = false;
@@ -271,9 +276,8 @@ void ScrollContainer::_reposition_children() {
 	Size2 size = get_size();
 	Point2 ofs;
 
-	Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
-	size -= sb->get_minimum_size();
-	ofs += sb->get_offset();
+	size -= theme_cache.bg_style->get_minimum_size();
+	ofs += theme_cache.bg_style->get_offset();
 	bool rtl = is_layout_rtl();
 
 	if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons
@@ -337,8 +341,7 @@ void ScrollContainer::_notification(int p_what) {
 		} break;
 
 		case NOTIFICATION_DRAW: {
-			Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
-			draw_style_box(sb, Rect2(Vector2(), get_size()));
+			draw_style_box(theme_cache.bg_style, Rect2(Vector2(), get_size()));
 		} break;
 
 		case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
@@ -413,8 +416,7 @@ void ScrollContainer::_notification(int p_what) {
 
 void ScrollContainer::update_scrollbars() {
 	Size2 size = get_size();
-	Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
-	size -= sb->get_minimum_size();
+	size -= theme_cache.bg_style->get_minimum_size();
 
 	Size2 hmin = h_scroll->get_combined_minimum_size();
 	Size2 vmin = v_scroll->get_combined_minimum_size();

+ 5 - 0
scene/gui/scroll_container.h

@@ -69,9 +69,14 @@ private:
 	int deadzone = 0;
 	bool follow_focus = false;
 
+	struct ThemeCache {
+		Ref<StyleBox> bg_style;
+	} theme_cache;
+
 	void _cancel_drag();
 
 protected:
+	virtual void _update_theme_item_cache() override;
 	Size2 get_minimum_size() const override;
 
 	void _gui_focus_changed(Control *p_control);

+ 12 - 6
scene/gui/separator.cpp

@@ -33,24 +33,30 @@
 Size2 Separator::get_minimum_size() const {
 	Size2 ms(3, 3);
 	if (orientation == VERTICAL) {
-		ms.x = get_theme_constant(SNAME("separation"));
+		ms.x = theme_cache.separation;
 	} else { // HORIZONTAL
-		ms.y = get_theme_constant(SNAME("separation"));
+		ms.y = theme_cache.separation;
 	}
 	return ms;
 }
 
+void Separator::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.separation = get_theme_constant(SNAME("separation"));
+	theme_cache.separator_style = get_theme_stylebox(SNAME("separator"));
+}
+
 void Separator::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
 			Size2i size = get_size();
-			Ref<StyleBox> style = get_theme_stylebox(SNAME("separator"));
-			Size2i ssize = style->get_minimum_size() + style->get_center_size();
+			Size2i ssize = theme_cache.separator_style->get_minimum_size() + theme_cache.separator_style->get_center_size();
 
 			if (orientation == VERTICAL) {
-				style->draw(get_canvas_item(), Rect2((size.x - ssize.x) / 2, 0, ssize.x, size.y));
+				theme_cache.separator_style->draw(get_canvas_item(), Rect2((size.x - ssize.x) / 2, 0, ssize.x, size.y));
 			} else {
-				style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y));
+				theme_cache.separator_style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y));
 			}
 		} break;
 	}

+ 8 - 0
scene/gui/separator.h

@@ -35,8 +35,16 @@
 class Separator : public Control {
 	GDCLASS(Separator, Control);
 
+	struct ThemeCache {
+		int separation = 0;
+		Ref<StyleBox> separator_style;
+	} theme_cache;
+
 protected:
 	Orientation orientation = Orientation::HORIZONTAL;
+
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 
 public:

+ 45 - 12
scene/gui/slider.cpp

@@ -33,11 +33,8 @@
 #include "core/os/keyboard.h"
 
 Size2 Slider::get_minimum_size() const {
-	Ref<StyleBox> style = get_theme_stylebox(SNAME("slider"));
-	Size2i ss = style->get_minimum_size() + style->get_center_size();
-
-	Ref<Texture2D> grabber = get_theme_icon(SNAME("grabber"));
-	Size2i rs = grabber->get_size();
+	Size2i ss = theme_cache.slider_style->get_minimum_size() + theme_cache.slider_style->get_center_size();
+	Size2i rs = theme_cache.grabber_icon->get_size();
 
 	if (orientation == HORIZONTAL) {
 		return Size2i(ss.width, MAX(ss.height, rs.height));
@@ -58,7 +55,13 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
 	if (mb.is_valid()) {
 		if (mb->get_button_index() == MouseButton::LEFT) {
 			if (mb->is_pressed()) {
-				Ref<Texture2D> grabber = get_theme_icon(mouse_inside || has_focus() ? "grabber_highlight" : "grabber");
+				Ref<Texture2D> grabber;
+				if (mouse_inside || has_focus()) {
+					grabber = theme_cache.grabber_hl_icon;
+				} else {
+					grabber = theme_cache.grabber_icon;
+				}
+
 				grab.pos = orientation == VERTICAL ? mb->get_position().y : mb->get_position().x;
 
 				double grab_width = (double)grabber->get_size().width;
@@ -95,7 +98,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
 	if (mm.is_valid()) {
 		if (grab.active) {
 			Size2i size = get_size();
-			Ref<Texture2D> grabber = get_theme_icon(SNAME("grabber"));
+			Ref<Texture2D> grabber = theme_cache.grabber_icon;
 			double motion = (orientation == VERTICAL ? mm->get_position().y : mm->get_position().x) - grab.pos;
 			if (orientation == VERTICAL) {
 				motion = -motion;
@@ -145,6 +148,19 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
 	}
 }
 
+void Slider::_update_theme_item_cache() {
+	Range::_update_theme_item_cache();
+
+	theme_cache.slider_style = get_theme_stylebox(SNAME("slider"));
+	theme_cache.grabber_area_style = get_theme_stylebox(SNAME("grabber_area"));
+	theme_cache.grabber_area_hl_style = get_theme_stylebox(SNAME("grabber_area_highlight"));
+
+	theme_cache.grabber_icon = get_theme_icon(SNAME("grabber"));
+	theme_cache.grabber_hl_icon = get_theme_icon(SNAME("grabber_highlight"));
+	theme_cache.grabber_disabled_icon = get_theme_icon(SNAME("grabber_disabled"));
+	theme_cache.tick_icon = get_theme_icon(SNAME("tick"));
+}
+
 void Slider::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_THEME_CHANGED: {
@@ -171,13 +187,30 @@ void Slider::_notification(int p_what) {
 		case NOTIFICATION_DRAW: {
 			RID ci = get_canvas_item();
 			Size2i size = get_size();
-			Ref<StyleBox> style = get_theme_stylebox(SNAME("slider"));
-			bool highlighted = mouse_inside || has_focus();
-			Ref<StyleBox> grabber_area = get_theme_stylebox(highlighted ? "grabber_area_highlight" : "grabber_area");
-			Ref<Texture2D> grabber = get_theme_icon(editable ? (highlighted ? "grabber_highlight" : "grabber") : "grabber_disabled");
-			Ref<Texture2D> tick = get_theme_icon(SNAME("tick"));
 			double ratio = Math::is_nan(get_as_ratio()) ? 0 : get_as_ratio();
 
+			Ref<StyleBox> style = theme_cache.slider_style;
+			Ref<Texture2D> tick = theme_cache.tick_icon;
+
+			bool highlighted = mouse_inside || has_focus();
+			Ref<Texture2D> grabber;
+			if (editable) {
+				if (highlighted) {
+					grabber = theme_cache.grabber_hl_icon;
+				} else {
+					grabber = theme_cache.grabber_icon;
+				}
+			} else {
+				grabber = theme_cache.grabber_disabled_icon;
+			}
+
+			Ref<StyleBox> grabber_area;
+			if (highlighted) {
+				grabber_area = theme_cache.grabber_area_hl_style;
+			} else {
+				grabber_area = theme_cache.grabber_area_style;
+			}
+
 			if (orientation == VERTICAL) {
 				int widget_width = style->get_minimum_size().width + style->get_center_size().width;
 				double areasize = size.height - grabber->get_size().height;

+ 14 - 1
scene/gui/slider.h

@@ -49,11 +49,24 @@ class Slider : public Range {
 	bool editable = true;
 	bool scrollable = true;
 
+	struct ThemeCache {
+		Ref<StyleBox> slider_style;
+		Ref<StyleBox> grabber_area_style;
+		Ref<StyleBox> grabber_area_hl_style;
+
+		Ref<Texture2D> grabber_icon;
+		Ref<Texture2D> grabber_hl_icon;
+		Ref<Texture2D> grabber_disabled_icon;
+		Ref<Texture2D> tick_icon;
+	} theme_cache;
+
 protected:
+	bool ticks_on_borders = false;
+
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 	static void _bind_methods();
-	bool ticks_on_borders = false;
 
 public:
 	virtual Size2 get_minimum_size() const override;

+ 10 - 6
scene/gui/spin_box.cpp

@@ -210,25 +210,29 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) {
 	}
 }
 
+void SpinBox::_update_theme_item_cache() {
+	Range::_update_theme_item_cache();
+
+	theme_cache.updown_icon = get_theme_icon(SNAME("updown"));
+}
+
 void SpinBox::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
-			Ref<Texture2D> updown = get_theme_icon(SNAME("updown"));
-
-			_adjust_width_for_icon(updown);
+			_adjust_width_for_icon(theme_cache.updown_icon);
 
 			RID ci = get_canvas_item();
 			Size2i size = get_size();
 
 			if (is_layout_rtl()) {
-				updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2));
+				theme_cache.updown_icon->draw(ci, Point2i(0, (size.height - theme_cache.updown_icon->get_height()) / 2));
 			} else {
-				updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2));
+				theme_cache.updown_icon->draw(ci, Point2i(size.width - theme_cache.updown_icon->get_width(), (size.height - theme_cache.updown_icon->get_height()) / 2));
 			}
 		} break;
 
 		case NOTIFICATION_ENTER_TREE: {
-			_adjust_width_for_icon(get_theme_icon(SNAME("updown")));
+			_adjust_width_for_icon(theme_cache.updown_icon);
 			_value_changed(0);
 		} break;
 

+ 5 - 0
scene/gui/spin_box.h

@@ -69,9 +69,14 @@ class SpinBox : public Range {
 
 	inline void _adjust_width_for_icon(const Ref<Texture2D> &icon);
 
+	struct ThemeCache {
+		Ref<Texture2D> updown_icon;
+	} theme_cache;
+
 protected:
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
 
+	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 
 	static void _bind_methods();

+ 21 - 21
scene/gui/split_container.cpp

@@ -76,9 +76,7 @@ void SplitContainer::_resort() {
 	bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
 
 	// Determine the separation between items
-	Ref<Texture2D> g = get_theme_icon(SNAME("grabber"));
-	int sep = get_theme_constant(SNAME("separation"));
-	sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0;
+	int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? theme_cache.grabber_icon->get_height() : theme_cache.grabber_icon->get_width()) : 0;
 
 	// Compute the minimum size
 	Size2 ms_first = first->get_combined_minimum_size();
@@ -131,9 +129,7 @@ Size2 SplitContainer::get_minimum_size() const {
 	/* Calculate MINIMUM SIZE */
 
 	Size2i minimum;
-	Ref<Texture2D> g = get_theme_icon(SNAME("grabber"));
-	int sep = get_theme_constant(SNAME("separation"));
-	sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0;
+	int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? theme_cache.grabber_icon->get_height() : theme_cache.grabber_icon->get_width()) : 0;
 
 	for (int i = 0; i < 2; i++) {
 		if (!_getch(i)) {
@@ -162,6 +158,14 @@ Size2 SplitContainer::get_minimum_size() const {
 	return minimum;
 }
 
+void SplitContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.separation = get_theme_constant(SNAME("separation"));
+	theme_cache.autohide = get_theme_constant(SNAME("autohide"));
+	theme_cache.grabber_icon = get_theme_icon(SNAME("grabber"));
+}
+
 void SplitContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_TRANSLATION_CHANGED:
@@ -175,7 +179,7 @@ void SplitContainer::_notification(int p_what) {
 
 		case NOTIFICATION_MOUSE_EXIT: {
 			mouse_inside = false;
-			if (get_theme_constant(SNAME("autohide"))) {
+			if (theme_cache.autohide) {
 				queue_redraw();
 			}
 		} break;
@@ -185,7 +189,7 @@ void SplitContainer::_notification(int p_what) {
 				return;
 			}
 
-			if (collapsed || (!dragging && !mouse_inside && get_theme_constant(SNAME("autohide")))) {
+			if (collapsed || (!dragging && !mouse_inside && theme_cache.autohide)) {
 				return;
 			}
 
@@ -193,8 +197,8 @@ void SplitContainer::_notification(int p_what) {
 				return;
 			}
 
-			int sep = dragger_visibility != DRAGGER_HIDDEN_COLLAPSED ? get_theme_constant(SNAME("separation")) : 0;
-			Ref<Texture2D> tex = get_theme_icon(SNAME("grabber"));
+			int sep = dragger_visibility != DRAGGER_HIDDEN_COLLAPSED ? theme_cache.separation : 0;
+			Ref<Texture2D> tex = theme_cache.grabber_icon;
 			Size2 size = get_size();
 
 			if (vertical) {
@@ -222,16 +226,14 @@ void SplitContainer::gui_input(const Ref<InputEvent> &p_event) {
 	if (mb.is_valid()) {
 		if (mb->get_button_index() == MouseButton::LEFT) {
 			if (mb->is_pressed()) {
-				int sep = get_theme_constant(SNAME("separation"));
-
 				if (vertical) {
-					if (mb->get_position().y > middle_sep && mb->get_position().y < middle_sep + sep) {
+					if (mb->get_position().y > middle_sep && mb->get_position().y < middle_sep + theme_cache.separation) {
 						dragging = true;
 						drag_from = mb->get_position().y;
 						drag_ofs = split_offset;
 					}
 				} else {
-					if (mb->get_position().x > middle_sep && mb->get_position().x < middle_sep + sep) {
+					if (mb->get_position().x > middle_sep && mb->get_position().x < middle_sep + theme_cache.separation) {
 						dragging = true;
 						drag_from = mb->get_position().x;
 						drag_ofs = split_offset;
@@ -248,14 +250,14 @@ void SplitContainer::gui_input(const Ref<InputEvent> &p_event) {
 	if (mm.is_valid()) {
 		bool mouse_inside_state = false;
 		if (vertical) {
-			mouse_inside_state = mm->get_position().y > middle_sep && mm->get_position().y < middle_sep + get_theme_constant(SNAME("separation"));
+			mouse_inside_state = mm->get_position().y > middle_sep && mm->get_position().y < middle_sep + theme_cache.separation;
 		} else {
-			mouse_inside_state = mm->get_position().x > middle_sep && mm->get_position().x < middle_sep + get_theme_constant(SNAME("separation"));
+			mouse_inside_state = mm->get_position().x > middle_sep && mm->get_position().x < middle_sep + theme_cache.separation;
 		}
 
 		if (mouse_inside != mouse_inside_state) {
 			mouse_inside = mouse_inside_state;
-			if (get_theme_constant(SNAME("autohide"))) {
+			if (theme_cache.autohide) {
 				queue_redraw();
 			}
 		}
@@ -281,14 +283,12 @@ Control::CursorShape SplitContainer::get_cursor_shape(const Point2 &p_pos) const
 	}
 
 	if (!collapsed && _getch(0) && _getch(1) && dragger_visibility == DRAGGER_VISIBLE) {
-		int sep = get_theme_constant(SNAME("separation"));
-
 		if (vertical) {
-			if (p_pos.y > middle_sep && p_pos.y < middle_sep + sep) {
+			if (p_pos.y > middle_sep && p_pos.y < middle_sep + theme_cache.separation) {
 				return CURSOR_VSPLIT;
 			}
 		} else {
-			if (p_pos.x > middle_sep && p_pos.x < middle_sep + sep) {
+			if (p_pos.x > middle_sep && p_pos.x < middle_sep + theme_cache.separation) {
 				return CURSOR_HSPLIT;
 			}
 		}

+ 8 - 0
scene/gui/split_container.h

@@ -55,12 +55,20 @@ private:
 	DraggerVisibility dragger_visibility = DRAGGER_VISIBLE;
 	bool mouse_inside = false;
 
+	struct ThemeCache {
+		int separation = 0;
+		int autohide = 0;
+		Ref<Texture2D> grabber_icon;
+	} theme_cache;
+
 	Control *_getch(int p_idx) const;
 
 	void _resort();
 
 protected:
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 	static void _bind_methods();
 

+ 92 - 112
scene/gui/tab_bar.cpp

@@ -44,14 +44,7 @@ Size2 TabBar::get_minimum_size() const {
 		return ms;
 	}
 
-	Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
-	Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
-	Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-	Ref<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight"));
-	Ref<Texture2D> close = get_theme_icon(SNAME("close"));
-	int hseparation = get_theme_constant(SNAME("h_separation"));
-
-	int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
+	int y_margin = MAX(MAX(theme_cache.tab_unselected_style->get_minimum_size().height, theme_cache.tab_selected_style->get_minimum_size().height), theme_cache.tab_disabled_style->get_minimum_size().height);
 
 	for (int i = 0; i < tabs.size(); i++) {
 		if (tabs[i].hidden) {
@@ -62,22 +55,22 @@ Size2 TabBar::get_minimum_size() const {
 
 		Ref<StyleBox> style;
 		if (tabs[i].disabled) {
-			style = tab_disabled;
+			style = theme_cache.tab_disabled_style;
 		} else if (current == i) {
-			style = tab_selected;
+			style = theme_cache.tab_selected_style;
 		} else {
-			style = tab_unselected;
+			style = theme_cache.tab_unselected_style;
 		}
 		ms.width += style->get_minimum_size().width;
 
 		Ref<Texture2D> tex = tabs[i].icon;
 		if (tex.is_valid()) {
 			ms.height = MAX(ms.height, tex->get_size().height + y_margin);
-			ms.width += tex->get_size().width + hseparation;
+			ms.width += tex->get_size().width + theme_cache.h_separation;
 		}
 
 		if (!tabs[i].text.is_empty()) {
-			ms.width += tabs[i].size_text + hseparation;
+			ms.width += tabs[i].size_text + theme_cache.h_separation;
 		}
 		ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
 
@@ -87,22 +80,22 @@ Size2 TabBar::get_minimum_size() const {
 			Ref<Texture2D> rb = tabs[i].right_button;
 
 			if (close_visible) {
-				ms.width += button_highlight->get_minimum_size().width + rb->get_width();
+				ms.width += theme_cache.button_hl_style->get_minimum_size().width + rb->get_width();
 			} else {
-				ms.width += button_highlight->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+				ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation;
 			}
 
 			ms.height = MAX(ms.height, rb->get_height() + y_margin);
 		}
 
 		if (close_visible) {
-			ms.width += button_highlight->get_margin(SIDE_LEFT) + close->get_width() + hseparation;
+			ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + theme_cache.close_icon->get_width() + theme_cache.h_separation;
 
-			ms.height = MAX(ms.height, close->get_height() + y_margin);
+			ms.height = MAX(ms.height, theme_cache.close_icon->get_height() + y_margin);
 		}
 
 		if (ms.width - ofs > style->get_minimum_size().width) {
-			ms.width -= hseparation;
+			ms.width -= theme_cache.h_separation;
 		}
 	}
 
@@ -122,16 +115,13 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
 		Point2 pos = mm->get_position();
 
 		if (buttons_visible) {
-			Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
-			Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-
 			if (is_layout_rtl()) {
-				if (pos.x < decr->get_width()) {
+				if (pos.x < theme_cache.decrement_icon->get_width()) {
 					if (highlight_arrow != 1) {
 						highlight_arrow = 1;
 						queue_redraw();
 					}
-				} else if (pos.x < incr->get_width() + decr->get_width()) {
+				} else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) {
 					if (highlight_arrow != 0) {
 						highlight_arrow = 0;
 						queue_redraw();
@@ -141,8 +131,8 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
 					queue_redraw();
 				}
 			} else {
-				int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
-				if (pos.x > limit_minus_buttons + decr->get_width()) {
+				int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
+				if (pos.x > limit_minus_buttons + theme_cache.decrement_icon->get_width()) {
 					if (highlight_arrow != 1) {
 						highlight_arrow = 1;
 						queue_redraw();
@@ -214,18 +204,15 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
 			Point2 pos = mb->get_position();
 
 			if (buttons_visible) {
-				Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
-				Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-
 				if (is_layout_rtl()) {
-					if (pos.x < decr->get_width()) {
+					if (pos.x < theme_cache.decrement_icon->get_width()) {
 						if (missing_right) {
 							offset++;
 							_update_cache();
 							queue_redraw();
 						}
 						return;
-					} else if (pos.x < incr->get_width() + decr->get_width()) {
+					} else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) {
 						if (offset > 0) {
 							offset--;
 							_update_cache();
@@ -234,8 +221,8 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
 						return;
 					}
 				} else {
-					int limit = get_size().width - incr->get_width() - decr->get_width();
-					if (pos.x > limit + decr->get_width()) {
+					int limit = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
+					if (pos.x > limit + theme_cache.decrement_icon->get_width()) {
 						if (missing_right) {
 							offset++;
 							_update_cache();
@@ -299,9 +286,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
 }
 
 void TabBar::_shape(int p_tab) {
-	Ref<Font> font = get_theme_font(SNAME("font"));
-	int font_size = get_theme_font_size(SNAME("font_size"));
-
 	tabs.write[p_tab].xl_text = atr(tabs[p_tab].text);
 	tabs.write[p_tab].text_buf->clear();
 	tabs.write[p_tab].text_buf->set_width(-1);
@@ -311,7 +295,37 @@ void TabBar::_shape(int p_tab) {
 		tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction);
 	}
 
-	tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].language);
+	tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, theme_cache.font, theme_cache.font_size, tabs[p_tab].language);
+}
+
+void TabBar::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+
+	theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected"));
+	theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected"));
+	theme_cache.tab_disabled_style = get_theme_stylebox(SNAME("tab_disabled"));
+
+	theme_cache.increment_icon = get_theme_icon(SNAME("increment"));
+	theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight"));
+	theme_cache.decrement_icon = get_theme_icon(SNAME("decrement"));
+	theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight"));
+	theme_cache.drop_mark_icon = get_theme_icon(SNAME("drop_mark"));
+	theme_cache.drop_mark_color = get_theme_color(SNAME("drop_mark_color"));
+
+	theme_cache.font = get_theme_font(SNAME("font"));
+	theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+	theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
+
+	theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color"));
+	theme_cache.font_unselected_color = get_theme_color(SNAME("font_unselected_color"));
+	theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+
+	theme_cache.close_icon = get_theme_icon(SNAME("close"));
+	theme_cache.button_pressed_style = get_theme_stylebox(SNAME("button_pressed"));
+	theme_cache.button_hl_style = get_theme_stylebox(SNAME("button_highlight"));
 }
 
 void TabBar::_notification(int p_what) {
@@ -352,18 +366,9 @@ void TabBar::_notification(int p_what) {
 				return;
 			}
 
-			Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
-			Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
-			Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-			Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
-			Color font_unselected_color = get_theme_color(SNAME("font_unselected_color"));
-			Color font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
-			Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
-			Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-
 			bool rtl = is_layout_rtl();
 			Vector2 size = get_size();
-			int limit_minus_buttons = size.width - incr->get_width() - decr->get_width();
+			int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
 
 			int ofs = tabs[offset].ofs_cache;
 
@@ -378,14 +383,14 @@ void TabBar::_notification(int p_what) {
 					Color col;
 
 					if (tabs[i].disabled) {
-						sb = tab_disabled;
-						col = font_disabled_color;
+						sb = theme_cache.tab_disabled_style;
+						col = theme_cache.font_disabled_color;
 					} else if (i == current) {
-						sb = tab_selected;
-						col = font_selected_color;
+						sb = theme_cache.tab_selected_style;
+						col = theme_cache.font_selected_color;
 					} else {
-						sb = tab_unselected;
-						col = font_unselected_color;
+						sb = theme_cache.tab_unselected_style;
+						col = theme_cache.font_unselected_color;
 					}
 
 					_draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs);
@@ -396,41 +401,38 @@ void TabBar::_notification(int p_what) {
 
 			// Draw selected tab in the front, but only if it's visible.
 			if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) {
-				Ref<StyleBox> sb = tabs[current].disabled ? tab_disabled : tab_selected;
+				Ref<StyleBox> sb = tabs[current].disabled ? theme_cache.tab_disabled_style : theme_cache.tab_selected_style;
 				float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache;
 
-				_draw_tab(sb, font_selected_color, current, x);
+				_draw_tab(sb, theme_cache.font_selected_color, current, x);
 			}
 
 			if (buttons_visible) {
-				Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight"));
-				Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight"));
-
-				int vofs = (size.height - incr->get_size().height) / 2;
+				int vofs = (size.height - theme_cache.increment_icon->get_size().height) / 2;
 
 				if (rtl) {
 					if (missing_right) {
-						draw_texture(highlight_arrow == 1 ? decr_hl : decr, Point2(0, vofs));
+						draw_texture(highlight_arrow == 1 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(0, vofs));
 					} else {
-						draw_texture(decr, Point2(0, vofs), Color(1, 1, 1, 0.5));
+						draw_texture(theme_cache.decrement_icon, Point2(0, vofs), Color(1, 1, 1, 0.5));
 					}
 
 					if (offset > 0) {
-						draw_texture(highlight_arrow == 0 ? incr_hl : incr, Point2(incr->get_size().width, vofs));
+						draw_texture(highlight_arrow == 0 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs));
 					} else {
-						draw_texture(incr, Point2(incr->get_size().width, vofs), Color(1, 1, 1, 0.5));
+						draw_texture(theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs), Color(1, 1, 1, 0.5));
 					}
 				} else {
 					if (offset > 0) {
-						draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit_minus_buttons, vofs));
+						draw_texture(highlight_arrow == 0 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs));
 					} else {
-						draw_texture(decr, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5));
+						draw_texture(theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5));
 					}
 
 					if (missing_right) {
-						draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit_minus_buttons + decr->get_size().width, vofs));
+						draw_texture(highlight_arrow == 1 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs));
 					} else {
-						draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5));
+						draw_texture(theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs), Color(1, 1, 1, 0.5));
 					}
 				}
 			}
@@ -462,10 +464,7 @@ void TabBar::_notification(int p_what) {
 					}
 				}
 
-				Ref<Texture2D> drop_mark = get_theme_icon(SNAME("drop_mark"));
-				Color drop_mark_color = get_theme_color(SNAME("drop_mark_color"));
-
-				drop_mark->draw(get_canvas_item(), Point2(x - drop_mark->get_width() / 2, (size.height - drop_mark->get_height()) / 2), drop_mark_color);
+				theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - theme_cache.drop_mark_icon->get_width() / 2, (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color);
 			}
 		} break;
 	}
@@ -475,10 +474,6 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
 	RID ci = get_canvas_item();
 	bool rtl = is_layout_rtl();
 
-	Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
-	int outline_size = get_theme_constant(SNAME("outline_size"));
-	int hseparation = get_theme_constant(SNAME("h_separation"));
-
 	Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height);
 	p_tab_style->draw(ci, sb_rect);
 
@@ -491,7 +486,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
 	if (icon.is_valid()) {
 		icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
 
-		p_x = rtl ? p_x - icon->get_width() - hseparation : p_x + icon->get_width() + hseparation;
+		p_x = rtl ? p_x - icon->get_width() - theme_cache.h_separation : p_x + icon->get_width() + theme_cache.h_separation;
 	}
 
 	// Draw the text.
@@ -499,17 +494,17 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
 		Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x,
 				p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2);
 
-		if (outline_size > 0 && font_outline_color.a > 0) {
-			tabs[p_index].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+		if (theme_cache.outline_size > 0 && theme_cache.font_outline_color.a > 0) {
+			tabs[p_index].text_buf->draw_outline(ci, text_pos, theme_cache.outline_size, theme_cache.font_outline_color);
 		}
 		tabs[p_index].text_buf->draw(ci, text_pos, p_font_color);
 
-		p_x = rtl ? p_x - tabs[p_index].size_text - hseparation : p_x + tabs[p_index].size_text + hseparation;
+		p_x = rtl ? p_x - tabs[p_index].size_text - theme_cache.h_separation : p_x + tabs[p_index].size_text + theme_cache.h_separation;
 	}
 
 	// Draw and calculate rect of the right button.
 	if (tabs[p_index].right_button.is_valid()) {
-		Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
+		Ref<StyleBox> style = theme_cache.button_hl_style;
 		Ref<Texture2D> rb = tabs[p_index].right_button;
 
 		Rect2 rb_rect;
@@ -521,7 +516,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
 
 		if (rb_hover == p_index) {
 			if (rb_pressing) {
-				get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
+				theme_cache.button_pressed_style->draw(ci, rb_rect);
 			} else {
 				style->draw(ci, rb_rect);
 			}
@@ -534,8 +529,8 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
 
 	// Draw and calculate rect of the close button.
 	if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) {
-		Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
-		Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
+		Ref<StyleBox> style = theme_cache.button_hl_style;
+		Ref<Texture2D> cb = theme_cache.close_icon;
 
 		Rect2 cb_rect;
 		cb_rect.size = style->get_minimum_size() + cb->get_size();
@@ -546,7 +541,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
 
 		if (!tabs[p_index].disabled && cb_hover == p_index) {
 			if (cb_pressing) {
-				get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect);
+				theme_cache.button_pressed_style->draw(ci, cb_rect);
 			} else {
 				style->draw(ci, cb_rect);
 			}
@@ -849,14 +844,8 @@ void TabBar::_update_cache() {
 		return;
 	}
 
-	Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-	Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
-	Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
-	Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
-	Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-
 	int limit = get_size().width;
-	int limit_minus_buttons = limit - incr->get_width() - decr->get_width();
+	int limit_minus_buttons = limit - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
 
 	int w = 0;
 
@@ -1258,52 +1247,47 @@ void TabBar::move_tab(int p_from, int p_to) {
 int TabBar::get_tab_width(int p_idx) const {
 	ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0);
 
-	Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
-	Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
-	Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-	int hseparation = get_theme_constant(SNAME("h_separation"));
-
 	Ref<StyleBox> style;
 
 	if (tabs[p_idx].disabled) {
-		style = tab_disabled;
+		style = theme_cache.tab_disabled_style;
 	} else if (current == p_idx) {
-		style = tab_selected;
+		style = theme_cache.tab_selected_style;
 	} else {
-		style = tab_unselected;
+		style = theme_cache.tab_unselected_style;
 	}
 	int x = style->get_minimum_size().width;
 
 	Ref<Texture2D> tex = tabs[p_idx].icon;
 	if (tex.is_valid()) {
-		x += tex->get_width() + hseparation;
+		x += tex->get_width() + theme_cache.h_separation;
 	}
 
 	if (!tabs[p_idx].text.is_empty()) {
-		x += tabs[p_idx].size_text + hseparation;
+		x += tabs[p_idx].size_text + theme_cache.h_separation;
 	}
 
 	bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current);
 
 	if (tabs[p_idx].right_button.is_valid()) {
-		Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
+		Ref<StyleBox> btn_style = theme_cache.button_hl_style;
 		Ref<Texture2D> rb = tabs[p_idx].right_button;
 
 		if (close_visible) {
 			x += btn_style->get_minimum_size().width + rb->get_width();
 		} else {
-			x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+			x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation;
 		}
 	}
 
 	if (close_visible) {
-		Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
-		Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
-		x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + hseparation;
+		Ref<StyleBox> btn_style = theme_cache.button_hl_style;
+		Ref<Texture2D> cb = theme_cache.close_icon;
+		x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + theme_cache.h_separation;
 	}
 
 	if (x > style->get_minimum_size().width) {
-		x -= hseparation;
+		x -= theme_cache.h_separation;
 	}
 
 	return x;
@@ -1314,9 +1298,7 @@ void TabBar::_ensure_no_over_offset() {
 		return;
 	}
 
-	Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
-	Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-	int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
+	int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
 
 	int prev_offset = offset;
 
@@ -1359,9 +1341,7 @@ void TabBar::ensure_tab_visible(int p_idx) {
 		return;
 	}
 
-	Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
-	Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-	int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
+	int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
 
 	int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache;
 	for (int i = max_drawn_tab; i <= p_idx; i++) {

+ 29 - 0
scene/gui/tab_bar.h

@@ -104,6 +104,34 @@ private:
 	bool scroll_to_selected = true;
 	int tabs_rearrange_group = -1;
 
+	struct ThemeCache {
+		int h_separation = 0;
+
+		Ref<StyleBox> tab_unselected_style;
+		Ref<StyleBox> tab_selected_style;
+		Ref<StyleBox> tab_disabled_style;
+
+		Ref<Texture2D> increment_icon;
+		Ref<Texture2D> increment_hl_icon;
+		Ref<Texture2D> decrement_icon;
+		Ref<Texture2D> decrement_hl_icon;
+		Ref<Texture2D> drop_mark_icon;
+		Color drop_mark_color;
+
+		Ref<Font> font;
+		int font_size;
+		int outline_size = 0;
+
+		Color font_selected_color;
+		Color font_unselected_color;
+		Color font_disabled_color;
+		Color font_outline_color;
+
+		Ref<Texture2D> close_icon;
+		Ref<StyleBox> button_pressed_style;
+		Ref<StyleBox> button_hl_style;
+	} theme_cache;
+
 	int get_tab_width(int p_idx) const;
 	void _ensure_no_over_offset();
 
@@ -117,6 +145,7 @@ private:
 
 protected:
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
+	virtual void _update_theme_item_cache() override;
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;

+ 80 - 52
scene/gui/tab_container.cpp

@@ -60,26 +60,24 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
 		}
 
 		// Handle menu button.
-		Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
-
 		if (is_layout_rtl()) {
-			if (popup && pos.x < menu->get_width()) {
+			if (popup && pos.x < theme_cache.menu_icon->get_width()) {
 				emit_signal(SNAME("pre_popup_pressed"));
 
 				Vector2 popup_pos = get_screen_position();
-				popup_pos.y += menu->get_height();
+				popup_pos.y += theme_cache.menu_icon->get_height();
 
 				popup->set_position(popup_pos);
 				popup->popup();
 				return;
 			}
 		} else {
-			if (popup && pos.x > size.width - menu->get_width()) {
+			if (popup && pos.x > size.width - theme_cache.menu_icon->get_width()) {
 				emit_signal(SNAME("pre_popup_pressed"));
 
 				Vector2 popup_pos = get_screen_position();
 				popup_pos.x += size.width - popup->get_size().width;
-				popup_pos.y += menu->get_height();
+				popup_pos.y += theme_cache.menu_icon->get_height();
 
 				popup->set_position(popup_pos);
 				popup->popup();
@@ -103,10 +101,9 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
 			return;
 		}
 
-		Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
 		if (popup) {
 			if (is_layout_rtl()) {
-				if (pos.x <= menu->get_width()) {
+				if (pos.x <= theme_cache.menu_icon->get_width()) {
 					if (!menu_hovered) {
 						menu_hovered = true;
 						queue_redraw();
@@ -117,7 +114,7 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
 					queue_redraw();
 				}
 			} else {
-				if (pos.x >= size.width - menu->get_width()) {
+				if (pos.x >= size.width - theme_cache.menu_icon->get_width()) {
 					if (!menu_hovered) {
 						menu_hovered = true;
 						queue_redraw();
@@ -136,6 +133,41 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
 	}
 }
 
+void TabContainer::_update_theme_item_cache() {
+	Container::_update_theme_item_cache();
+
+	theme_cache.side_margin = get_theme_constant(SNAME("side_margin"));
+
+	theme_cache.panel_style = get_theme_stylebox(SNAME("panel"));
+	theme_cache.tabbar_style = get_theme_stylebox(SNAME("tabbar_background"));
+
+	theme_cache.menu_icon = get_theme_icon(SNAME("menu"));
+	theme_cache.menu_hl_icon = get_theme_icon(SNAME("menu_highlight"));
+
+	// TabBar overrides.
+	theme_cache.icon_separation = get_theme_constant(SNAME("icon_separation"));
+	theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
+
+	theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected"));
+	theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected"));
+	theme_cache.tab_disabled_style = get_theme_stylebox(SNAME("tab_disabled"));
+
+	theme_cache.increment_icon = get_theme_icon(SNAME("increment"));
+	theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight"));
+	theme_cache.decrement_icon = get_theme_icon(SNAME("decrement"));
+	theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight"));
+	theme_cache.drop_mark_icon = get_theme_icon(SNAME("drop_mark"));
+	theme_cache.drop_mark_color = get_theme_color(SNAME("drop_mark_color"));
+
+	theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color"));
+	theme_cache.font_unselected_color = get_theme_color(SNAME("font_unselected_color"));
+	theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
+	theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+
+	theme_cache.tab_font = get_theme_font(SNAME("font"));
+	theme_cache.tab_font_size = get_theme_font_size(SNAME("font_size"));
+}
+
 void TabContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE: {
@@ -155,31 +187,26 @@ void TabContainer::_notification(int p_what) {
 			Size2 size = get_size();
 
 			// Draw only the tab area if the header is hidden.
-			Ref<StyleBox> panel = get_theme_stylebox(SNAME("panel"));
 			if (!tabs_visible) {
-				panel->draw(canvas, Rect2(0, 0, size.width, size.height));
+				theme_cache.panel_style->draw(canvas, Rect2(0, 0, size.width, size.height));
 				return;
 			}
 
 			int header_height = _get_top_margin();
 
 			// Draw background for the tabbar.
-			Ref<StyleBox> tabbar_background = get_theme_stylebox(SNAME("tabbar_background"));
-			tabbar_background->draw(canvas, Rect2(0, 0, size.width, header_height));
+			theme_cache.tabbar_style->draw(canvas, Rect2(0, 0, size.width, header_height));
 			// Draw the background for the tab's content.
-			panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
+			theme_cache.panel_style->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
 
 			// Draw the popup menu.
 			if (get_popup()) {
-				Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
-				Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight"));
-
-				int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width();
+				int x = is_layout_rtl() ? 0 : get_size().width - theme_cache.menu_icon->get_width();
 
 				if (menu_hovered) {
-					menu_hl->draw(get_canvas_item(), Point2(x, (header_height - menu_hl->get_height()) / 2));
+					theme_cache.menu_hl_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_hl_icon->get_height()) / 2));
 				} else {
-					menu->draw(get_canvas_item(), Point2(x, (header_height - menu->get_height()) / 2));
+					theme_cache.menu_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_icon->get_height()) / 2));
 				}
 			}
 		} break;
@@ -198,23 +225,27 @@ void TabContainer::_on_theme_changed() {
 		return;
 	}
 
-	tab_bar->add_theme_style_override(SNAME("tab_unselected"), get_theme_stylebox(SNAME("tab_unselected")));
-	tab_bar->add_theme_style_override(SNAME("tab_selected"), get_theme_stylebox(SNAME("tab_selected")));
-	tab_bar->add_theme_style_override(SNAME("tab_disabled"), get_theme_stylebox(SNAME("tab_disabled")));
-	tab_bar->add_theme_icon_override(SNAME("increment"), get_theme_icon(SNAME("increment")));
-	tab_bar->add_theme_icon_override(SNAME("increment_highlight"), get_theme_icon(SNAME("increment_highlight")));
-	tab_bar->add_theme_icon_override(SNAME("decrement"), get_theme_icon(SNAME("decrement")));
-	tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), get_theme_icon(SNAME("decrement_highlight")));
-	tab_bar->add_theme_icon_override(SNAME("drop_mark"), get_theme_icon(SNAME("drop_mark")));
-	tab_bar->add_theme_color_override(SNAME("drop_mark_color"), get_theme_color(SNAME("drop_mark_color")));
-	tab_bar->add_theme_color_override(SNAME("font_selected_color"), get_theme_color(SNAME("font_selected_color")));
-	tab_bar->add_theme_color_override(SNAME("font_unselected_color"), get_theme_color(SNAME("font_unselected_color")));
-	tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color")));
-	tab_bar->add_theme_color_override(SNAME("font_outline_color"), get_theme_color(SNAME("font_outline_color")));
-	tab_bar->add_theme_font_override(SNAME("font"), get_theme_font(SNAME("font")));
-	tab_bar->add_theme_font_size_override(SNAME("font_size"), get_theme_font_size(SNAME("font_size")));
-	tab_bar->add_theme_constant_override(SNAME("h_separation"), get_theme_constant(SNAME("icon_separation")));
-	tab_bar->add_theme_constant_override(SNAME("outline_size"), get_theme_constant(SNAME("outline_size")));
+	tab_bar->add_theme_style_override(SNAME("tab_unselected"), theme_cache.tab_unselected_style);
+	tab_bar->add_theme_style_override(SNAME("tab_selected"), theme_cache.tab_selected_style);
+	tab_bar->add_theme_style_override(SNAME("tab_disabled"), theme_cache.tab_disabled_style);
+
+	tab_bar->add_theme_icon_override(SNAME("increment"), theme_cache.increment_icon);
+	tab_bar->add_theme_icon_override(SNAME("increment_highlight"), theme_cache.increment_hl_icon);
+	tab_bar->add_theme_icon_override(SNAME("decrement"), theme_cache.decrement_icon);
+	tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), theme_cache.decrement_hl_icon);
+	tab_bar->add_theme_icon_override(SNAME("drop_mark"), theme_cache.drop_mark_icon);
+	tab_bar->add_theme_color_override(SNAME("drop_mark_color"), theme_cache.drop_mark_color);
+
+	tab_bar->add_theme_color_override(SNAME("font_selected_color"), theme_cache.font_selected_color);
+	tab_bar->add_theme_color_override(SNAME("font_unselected_color"), theme_cache.font_unselected_color);
+	tab_bar->add_theme_color_override(SNAME("font_disabled_color"), theme_cache.font_disabled_color);
+	tab_bar->add_theme_color_override(SNAME("font_outline_color"), theme_cache.font_outline_color);
+
+	tab_bar->add_theme_font_override(SNAME("font"), theme_cache.tab_font);
+	tab_bar->add_theme_font_size_override(SNAME("font_size"), theme_cache.tab_font_size);
+
+	tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation);
+	tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size);
 
 	_update_margins();
 	if (get_tab_count() > 0) {
@@ -228,7 +259,6 @@ void TabContainer::_on_theme_changed() {
 }
 
 void TabContainer::_repaint() {
-	Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
 	Vector<Control *> controls = _get_tab_controls();
 	int current = get_current_tab();
 
@@ -243,10 +273,10 @@ void TabContainer::_repaint() {
 				c->set_offset(SIDE_TOP, _get_top_margin());
 			}
 
-			c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP));
-			c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT));
-			c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT));
-			c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM));
+			c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_TOP));
+			c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_LEFT));
+			c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - theme_cache.panel_style->get_margin(SIDE_RIGHT));
+			c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - theme_cache.panel_style->get_margin(SIDE_BOTTOM));
 		} else {
 			c->hide();
 		}
@@ -256,8 +286,7 @@ void TabContainer::_repaint() {
 }
 
 void TabContainer::_update_margins() {
-	int menu_width = get_theme_icon(SNAME("menu"))->get_width();
-	int side_margin = get_theme_constant(SNAME("side_margin"));
+	int menu_width = theme_cache.menu_icon->get_width();
 
 	// Directly check for validity, to avoid errors when quitting.
 	bool has_popup = popup_obj_id.is_valid();
@@ -271,7 +300,7 @@ void TabContainer::_update_margins() {
 
 	switch (get_tab_alignment()) {
 		case TabBar::ALIGNMENT_LEFT: {
-			tab_bar->set_offset(SIDE_LEFT, side_margin);
+			tab_bar->set_offset(SIDE_LEFT, theme_cache.side_margin);
 			tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
 		} break;
 
@@ -293,10 +322,10 @@ void TabContainer::_update_margins() {
 			int total_tabs_width = last_tab_rect.position.x - first_tab_pos + last_tab_rect.size.width;
 
 			// Calculate if all the tabs would still fit if the margin was present.
-			if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + side_margin) > get_size().width))) {
+			if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + theme_cache.side_margin) > get_size().width))) {
 				tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
 			} else {
-				tab_bar->set_offset(SIDE_RIGHT, -side_margin);
+				tab_bar->set_offset(SIDE_RIGHT, -theme_cache.side_margin);
 			}
 		} break;
 
@@ -798,13 +827,12 @@ Size2 TabContainer::get_minimum_size() const {
 
 		if (!get_clip_tabs()) {
 			if (get_popup()) {
-				ms.x += get_theme_icon(SNAME("menu"))->get_width();
+				ms.x += theme_cache.menu_icon->get_width();
 			}
 
-			int side_margin = get_theme_constant(SNAME("side_margin"));
-			if (side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER &&
+			if (theme_cache.side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER &&
 					(get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) {
-				ms.x += side_margin;
+				ms.x += theme_cache.side_margin;
 			}
 		}
 	}
@@ -824,7 +852,7 @@ Size2 TabContainer::get_minimum_size() const {
 	}
 	ms.y += max_control_height;
 
-	Size2 panel_ms = get_theme_stylebox(SNAME("panel"))->get_minimum_size();
+	Size2 panel_ms = theme_cache.panel_style->get_minimum_size();
 	ms.x = MAX(ms.x, panel_ms.x);
 	ms.y += panel_ms.y;
 

+ 35 - 0
scene/gui/tab_container.h

@@ -48,6 +48,39 @@ class TabContainer : public Container {
 	bool theme_changing = false;
 	Node *child_removing = nullptr;
 
+	struct ThemeCache {
+		int side_margin = 0;
+
+		Ref<StyleBox> panel_style;
+		Ref<StyleBox> tabbar_style;
+
+		Ref<Texture2D> menu_icon;
+		Ref<Texture2D> menu_hl_icon;
+
+		// TabBar overrides.
+		int icon_separation = 0;
+		int outline_size = 0;
+
+		Ref<StyleBox> tab_unselected_style;
+		Ref<StyleBox> tab_selected_style;
+		Ref<StyleBox> tab_disabled_style;
+
+		Ref<Texture2D> increment_icon;
+		Ref<Texture2D> increment_hl_icon;
+		Ref<Texture2D> decrement_icon;
+		Ref<Texture2D> decrement_hl_icon;
+		Ref<Texture2D> drop_mark_icon;
+		Color drop_mark_color;
+
+		Color font_selected_color;
+		Color font_unselected_color;
+		Color font_disabled_color;
+		Color font_outline_color;
+
+		Ref<Font> tab_font;
+		int tab_font_size;
+	} theme_cache;
+
 	int _get_top_margin() const;
 	Vector<Control *> _get_tab_controls() const;
 	void _on_theme_changed();
@@ -65,6 +98,8 @@ class TabContainer : public Container {
 
 protected:
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
+	virtual void _update_theme_item_cache() override;
+
 	void _notification(int p_what);
 	virtual void add_child_notify(Node *p_child) override;
 	virtual void move_child_notify(Node *p_child) override;

Файловите разлики са ограничени, защото са твърде много
+ 210 - 210
scene/gui/tree.cpp


+ 8 - 4
scene/gui/tree.h

@@ -482,12 +482,13 @@ private:
 
 	void propagate_set_columns(TreeItem *p_item);
 
-	struct Cache {
+	struct ThemeCache {
 		Ref<Font> font;
 		Ref<Font> tb_font;
 		int font_size = 0;
 		int tb_font_size = 0;
 		Ref<StyleBox> bg;
+		Ref<StyleBox> bg_focus;
 		Ref<StyleBox> selected;
 		Ref<StyleBox> selected_focus;
 		Ref<StyleBox> cursor;
@@ -505,8 +506,9 @@ private:
 		Ref<Texture2D> checked;
 		Ref<Texture2D> unchecked;
 		Ref<Texture2D> indeterminate;
-		Ref<Texture2D> arrow_collapsed;
 		Ref<Texture2D> arrow;
+		Ref<Texture2D> arrow_collapsed;
+		Ref<Texture2D> arrow_collapsed_mirrored;
 		Ref<Texture2D> select_arrow;
 		Ref<Texture2D> updown;
 
@@ -536,7 +538,9 @@ private:
 		int scroll_border = 0;
 		int scroll_speed = 0;
 		int font_outline_size = 0;
+	} theme_cache;
 
+	struct Cache {
 		enum ClickType {
 			CLICK_NONE,
 			CLICK_TITLE,
@@ -559,7 +563,6 @@ private:
 		Point2i text_editor_position;
 
 		bool rtl = false;
-
 	} cache;
 
 	int _get_title_button_height() const;
@@ -572,7 +575,6 @@ private:
 	bool v_scroll_enabled = true;
 
 	Size2 get_internal_min_size() const;
-	void update_cache();
 	void update_scrollbars();
 
 	Rect2 search_item_rect(TreeItem *p_from, TreeItem *p_item);
@@ -620,6 +622,8 @@ private:
 	bool _scroll(bool p_horizontal, float p_pages);
 
 protected:
+	virtual void _update_theme_item_cache() override;
+
 	static void _bind_methods();
 
 public:

Някои файлове не бяха показани, защото твърде много файлове са промени