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

Bind remaining theme properties to their respective classes

This adds binds for GraphEdit/GraphElement/GraphNode, which were
skipped before due to a rework. This also adds binds for Window,
which was skipped before due to a complicated code organization.

Also adds theme cache entries/direct cache access to a few places
that previously missed it. Some theme properties are now exposed
to other classes via friendships or public getters for convenience.

This removes all string-based theme access from scene/ classes.
Yuri Sizov преди 1 година
родител
ревизия
fe000277ea

+ 16 - 1
scene/gui/code_edit.cpp

@@ -2399,6 +2399,19 @@ void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
 	}
 }
 
+/* Visual */
+Color CodeEdit::_get_brace_mismatch_color() const {
+	return theme_cache.brace_mismatch_color;
+}
+
+Color CodeEdit::_get_code_folding_color() const {
+	return theme_cache.code_folding_color;
+}
+
+Ref<Texture2D> CodeEdit::_get_folded_eol_icon() const {
+	return theme_cache.folded_eol_icon;
+}
+
 void CodeEdit::_bind_methods() {
 	/* Indent management */
 	ClassDB::bind_method(D_METHOD("set_indent_size", "size"), &CodeEdit::set_indent_size);
@@ -2644,7 +2657,7 @@ void CodeEdit::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, folded_eol_icon);
 
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, breakpoint_color);
-	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, CodeEdit, breakpoint_icon, "breakpoint");
+	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, breakpoint_icon, "breakpoint");
 
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, bookmark_color);
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, bookmark_icon, "bookmark");
@@ -2677,6 +2690,8 @@ void CodeEdit::_bind_methods() {
 	/* Other visuals */
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, CodeEdit, style_normal, "normal");
 
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, brace_mismatch_color);
+
 	BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, CodeEdit, font);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, CodeEdit, font_size);
 

+ 6 - 0
scene/gui/code_edit.h

@@ -278,11 +278,17 @@ private:
 		/* Other visuals */
 		Ref<StyleBox> style_normal;
 
+		Color brace_mismatch_color;
+
 		Ref<Font> font;
 		int font_size = 16;
 		int line_spacing = 1;
 	} theme_cache;
 
+	virtual Color _get_brace_mismatch_color() const override;
+	virtual Color _get_code_folding_color() const override;
+	virtual Ref<Texture2D> _get_folded_eol_icon() const override;
+
 	/* Callbacks */
 	int lines_edited_changed = 0;
 	int lines_edited_from = -1;

+ 10 - 10
scene/gui/color_mode.cpp

@@ -73,10 +73,10 @@ void ColorModeRGB::slider_draw(int p_which) {
 	Color left_color;
 	Color right_color;
 	Color color = color_picker->get_pick_color();
-	const real_t margin = 16 * color_picker->get_theme_default_base_scale();
+	const real_t margin = 16 * color_picker->theme_cache.base_scale;
 
 	if (p_which == ColorPicker::SLIDER_COUNT) {
-		slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+		slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
 
 		left_color = color;
 		left_color.a = 0;
@@ -168,10 +168,10 @@ void ColorModeHSV::slider_draw(int p_which) {
 	Color left_color;
 	Color right_color;
 	Color color = color_picker->get_pick_color();
-	const real_t margin = 16 * color_picker->get_theme_default_base_scale();
+	const real_t margin = 16 * color_picker->theme_cache.base_scale;
 
 	if (p_which == ColorPicker::SLIDER_COUNT) {
-		slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+		slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
 
 		left_color = color;
 		left_color.a = 0;
@@ -204,7 +204,7 @@ void ColorModeHSV::slider_draw(int p_which) {
 	slider->draw_polygon(pos, col);
 
 	if (p_which == 0) { // H
-		Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
+		Ref<Texture2D> hue = color_picker->theme_cache.color_hue;
 		slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false, Color::from_hsv(0, 0, color.get_v(), color.get_s()));
 	}
 }
@@ -243,10 +243,10 @@ void ColorModeRAW::slider_draw(int p_which) {
 	Color left_color;
 	Color right_color;
 	Color color = color_picker->get_pick_color();
-	const real_t margin = 16 * color_picker->get_theme_default_base_scale();
+	const real_t margin = 16 * color_picker->theme_cache.base_scale;
 
 	if (p_which == ColorPicker::SLIDER_COUNT) {
-		slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+		slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
 
 		left_color = color;
 		left_color.a = 0;
@@ -334,7 +334,7 @@ Color ColorModeOKHSL::get_color() const {
 void ColorModeOKHSL::slider_draw(int p_which) {
 	HSlider *slider = color_picker->get_slider(p_which);
 	Size2 size = slider->get_size();
-	const real_t margin = 16 * color_picker->get_theme_default_base_scale();
+	const real_t margin = 16 * color_picker->theme_cache.base_scale;
 
 	Vector<Vector2> pos;
 	Vector<Color> col;
@@ -370,7 +370,7 @@ void ColorModeOKHSL::slider_draw(int p_which) {
 		col.resize(4);
 
 		if (p_which == ColorPicker::SLIDER_COUNT) {
-			slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+			slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
 
 			left_color = color;
 			left_color.a = 0;
@@ -399,7 +399,7 @@ void ColorModeOKHSL::slider_draw(int p_which) {
 	slider->draw_polygon(pos, col);
 
 	if (p_which == 0) { // H
-		Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_okhsl_hue"), SNAME("ColorPicker"));
+		Ref<Texture2D> hue = color_picker->theme_cache.color_okhsl_hue;
 		slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false, Color::from_hsv(0, 0, color.get_ok_hsl_l() * 2.0, color.get_ok_hsl_s()));
 		return;
 	}

+ 7 - 6
scene/gui/color_picker.cpp

@@ -673,7 +673,7 @@ ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const {
 }
 
 inline int ColorPicker::_get_preset_size() {
-	return (int(get_minimum_size().width) - (preset_container->get_theme_constant(SNAME("h_separation")) * (PRESET_COLUMN_COUNT - 1))) / PRESET_COLUMN_COUNT;
+	return (int(get_minimum_size().width) - (preset_container->get_h_separation() * (PRESET_COLUMN_COUNT - 1))) / PRESET_COLUMN_COUNT;
 }
 
 void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
@@ -997,7 +997,7 @@ void ColorPicker::_sample_draw() {
 		const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
 
 		if (old_color.a < 1.0) {
-			sample->draw_texture_rect(theme_cache.sample_background_icon, rect_old, true);
+			sample->draw_texture_rect(theme_cache.sample_bg, rect_old, true);
 		}
 
 		sample->draw_rect(rect_old, old_color);
@@ -1011,7 +1011,7 @@ void ColorPicker::_sample_draw() {
 	}
 
 	if (color.a < 1.0) {
-		sample->draw_texture_rect(theme_cache.sample_background_icon, rect_new, true);
+		sample->draw_texture_rect(theme_cache.sample_bg, rect_new, true);
 	}
 
 	sample->draw_rect(rect_new, color);
@@ -1125,7 +1125,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
 	} else if (p_which == 1) {
 		if (actual_shape == SHAPE_HSV_RECTANGLE) {
 			c->draw_set_transform(Point2(), -Math_PI / 2, Size2(c->get_size().x, -c->get_size().y));
-			c->draw_texture_rect(theme_cache.color_hue_icon, Rect2(Point2(), Size2(1, 1)));
+			c->draw_texture_rect(theme_cache.color_hue, Rect2(Point2(), Size2(1, 1)));
 			c->draw_set_transform(Point2(), 0, Size2(1, 1));
 			int y = c->get_size().y - c->get_size().y * (1.0 - h);
 			Color col;
@@ -1703,10 +1703,11 @@ void ColorPicker::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, shape_circle);
 
 	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, bar_arrow);
-	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, ColorPicker, sample_background_icon, "sample_bg");
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, sample_bg);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, overbright_indicator);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, picker_cursor);
-	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, ColorPicker, color_hue_icon, "color_hue");
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, color_hue);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, color_okhsl_hue);
 
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_normal, "tab_unselected", "TabContainer");
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_pressed, "tab_selected", "TabContainer");

+ 9 - 2
scene/gui/color_picker.h

@@ -81,6 +81,12 @@ public:
 class ColorPicker : public VBoxContainer {
 	GDCLASS(ColorPicker, VBoxContainer);
 
+	// These classes poke into theme items for their internal logic.
+	friend class ColorModeRGB;
+	friend class ColorModeHSV;
+	friend class ColorModeRAW;
+	friend class ColorModeOKHSL;
+
 public:
 	enum ColorModeType {
 		MODE_RGB,
@@ -229,10 +235,11 @@ private:
 		Ref<Texture2D> shape_circle;
 
 		Ref<Texture2D> bar_arrow;
-		Ref<Texture2D> sample_background_icon;
+		Ref<Texture2D> sample_bg;
 		Ref<Texture2D> overbright_indicator;
 		Ref<Texture2D> picker_cursor;
-		Ref<Texture2D> color_hue_icon;
+		Ref<Texture2D> color_hue;
+		Ref<Texture2D> color_okhsl_hue;
 
 		/* Mode buttons */
 		Ref<StyleBox> mode_button_normal;

+ 79 - 61
scene/gui/graph_edit.cpp

@@ -38,6 +38,7 @@
 #include "scene/gui/graph_edit_arranger.h"
 #include "scene/gui/view_panner.h"
 #include "scene/resources/style_box_flat.h"
+#include "scene/theme/theme_db.h"
 
 constexpr int MINIMAP_OFFSET = 12;
 constexpr int MINIMAP_PADDING = 5;
@@ -56,23 +57,8 @@ GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) {
 	ge = p_edit;
 }
 
-GraphEditMinimap::GraphEditMinimap(GraphEdit *p_edit) {
-	ge = p_edit;
-
-	graph_proportions = Vector2(1, 1);
-	graph_padding = Vector2(0, 0);
-	camera_position = Vector2(100, 50);
-	camera_size = Vector2(200, 200);
-	minimap_padding = Vector2(MINIMAP_PADDING, MINIMAP_PADDING);
-	minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
-
-	is_pressing = false;
-	is_resizing = false;
-}
-
 Control::CursorShape GraphEditMinimap::get_cursor_shape(const Point2 &p_pos) const {
-	Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
-	if (is_resizing || (p_pos.x < resizer->get_width() && p_pos.y < resizer->get_height())) {
+	if (is_resizing || (p_pos.x < theme_cache.resizer->get_width() && p_pos.y < theme_cache.resizer->get_height())) {
 		return CURSOR_FDIAGSIZE;
 	}
 
@@ -172,8 +158,7 @@ void GraphEditMinimap::gui_input(const Ref<InputEvent> &p_ev) {
 		if (mb->is_pressed()) {
 			is_pressing = true;
 
-			Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
-			Rect2 resizer_hitbox = Rect2(Point2(), resizer->get_size());
+			Rect2 resizer_hitbox = Rect2(Point2(), theme_cache.resizer->get_size());
 			if (resizer_hitbox.has_point(mb->get_position())) {
 				is_resizing = true;
 			} else {
@@ -207,6 +192,21 @@ void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) {
 	ge->set_scroll_offset(p_offset + graph_offset - camera_size / 2);
 }
 
+void GraphEditMinimap::_bind_methods() {
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEditMinimap, panel);
+	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, GraphEditMinimap, node_style, "node");
+	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, GraphEditMinimap, camera_style, "camera");
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEditMinimap, resizer);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEditMinimap, resizer_color);
+}
+
+GraphEditMinimap::GraphEditMinimap(GraphEdit *p_edit) {
+	ge = p_edit;
+
+	minimap_padding = Vector2(MINIMAP_PADDING, MINIMAP_PADDING);
+	minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
+}
+
 Control::CursorShape GraphEdit::get_cursor_shape(const Point2 &p_pos) const {
 	if (moving_selection) {
 		return CURSOR_MOVE;
@@ -493,22 +493,25 @@ void GraphEdit::remove_child_notify(Node *p_child) {
 	}
 }
 
+void GraphEdit::_update_theme_item_cache() {
+	Control::_update_theme_item_cache();
+
+	theme_cache.base_scale = get_theme_default_base_scale();
+}
+
 void GraphEdit::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_THEME_CHANGED: {
-			port_hotzone_inner_extent = get_theme_constant("port_hotzone_inner_extent");
-			port_hotzone_outer_extent = get_theme_constant("port_hotzone_outer_extent");
-
-			zoom_minus_button->set_icon(get_theme_icon(SNAME("zoom_out")));
-			zoom_reset_button->set_icon(get_theme_icon(SNAME("zoom_reset")));
-			zoom_plus_button->set_icon(get_theme_icon(SNAME("zoom_in")));
+			zoom_minus_button->set_icon(theme_cache.zoom_out);
+			zoom_reset_button->set_icon(theme_cache.zoom_reset);
+			zoom_plus_button->set_icon(theme_cache.zoom_in);
 
-			toggle_snapping_button->set_icon(get_theme_icon(SNAME("snapping_toggle")));
-			show_grid_button->set_icon(get_theme_icon(SNAME("grid_toggle")));
-			minimap_button->set_icon(get_theme_icon(SNAME("minimap_toggle")));
-			layout_button->set_icon(get_theme_icon(SNAME("layout")));
+			toggle_snapping_button->set_icon(theme_cache.snapping_toggle);
+			show_grid_button->set_icon(theme_cache.grid_toggle);
+			minimap_button->set_icon(theme_cache.minimap_toggle);
+			layout_button->set_icon(theme_cache.layout);
 
-			zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
+			zoom_label->set_custom_minimum_size(Size2(48, 0) * theme_cache.base_scale);
 		} break;
 
 		case NOTIFICATION_READY: {
@@ -528,7 +531,7 @@ void GraphEdit::_notification(int p_what) {
 
 		case NOTIFICATION_DRAW: {
 			// Draw background fill.
-			draw_style_box(get_theme_stylebox(SNAME("panel")), Rect2(Point2(), get_size()));
+			draw_style_box(theme_cache.panel, Rect2(Point2(), get_size()));
 
 			// Draw background grid.
 			if (show_grid) {
@@ -538,16 +541,13 @@ void GraphEdit::_notification(int p_what) {
 				Point2i from_pos = (offset / float(snapping_distance)).floor();
 				Point2i len = (size / float(snapping_distance)).floor() + Vector2(1, 1);
 
-				Color grid_minor = get_theme_color(SNAME("grid_minor"));
-				Color grid_major = get_theme_color(SNAME("grid_major"));
-
 				for (int i = from_pos.x; i < from_pos.x + len.x; i++) {
 					Color color;
 
 					if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
-						color = grid_major;
+						color = theme_cache.grid_major;
 					} else {
-						color = grid_minor;
+						color = theme_cache.grid_minor;
 					}
 
 					float base_offset = i * snapping_distance * zoom - offset.x * zoom;
@@ -558,9 +558,9 @@ void GraphEdit::_notification(int p_what) {
 					Color color;
 
 					if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
-						color = grid_major;
+						color = theme_cache.grid_major;
 					} else {
-						color = grid_minor;
+						color = theme_cache.grid_minor;
 					}
 
 					float base_offset = i * snapping_distance * zoom - offset.y * zoom;
@@ -578,14 +578,14 @@ void GraphEdit::_notification(int p_what) {
 }
 
 bool GraphEdit::_filter_input(const Point2 &p_point) {
-	Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
-
 	for (int i = get_child_count() - 1; i >= 0; i--) {
 		GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
 		if (!graph_node || !graph_node->is_visible_in_tree()) {
 			continue;
 		}
 
+		Ref<Texture2D> port_icon = graph_node->theme_cache.port;
+
 		for (int j = 0; j < graph_node->get_input_port_count(); j++) {
 			Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
 
@@ -620,8 +620,6 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
 void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
 	Ref<InputEventMouseButton> mb = p_ev;
 	if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
-		Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
-
 		connecting_valid = false;
 		click_pos = mb->get_position() / zoom;
 		for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -630,6 +628,8 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
 				continue;
 			}
 
+			Ref<Texture2D> port_icon = graph_node->theme_cache.port;
+
 			for (int j = 0; j < graph_node->get_output_port_count(); j++) {
 				Vector2 pos = graph_node->get_output_port_position(j) * zoom + graph_node->get_position();
 				Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
@@ -756,12 +756,13 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
 		if (connecting_valid) {
 			Vector2 mpos = mm->get_position() / zoom;
 			for (int i = get_child_count() - 1; i >= 0; i--) {
-				Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
 				GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
 				if (!graph_node || !graph_node->is_visible_in_tree()) {
 					continue;
 				}
 
+				Ref<Texture2D> port_icon = graph_node->theme_cache.port;
+
 				if (!connecting_out) {
 					for (int j = 0; j < graph_node->get_output_port_count(); j++) {
 						Vector2 pos = graph_node->get_output_port_position(j) * zoom + graph_node->get_position();
@@ -877,7 +878,7 @@ bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_port_idx, con
 
 bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
 	if (p_graph_node->is_resizable()) {
-		Ref<Texture2D> resizer = p_graph_node->get_theme_icon(SNAME("resizer"));
+		Ref<Texture2D> resizer = p_graph_node->theme_cache.resizer;
 		Rect2 resizer_rect = Rect2(p_graph_node->get_position() / zoom + p_graph_node->get_size() - resizer->get_size(), resizer->get_size());
 		if (resizer_rect.has_point(p_mouse_pos)) {
 			return false;
@@ -895,9 +896,9 @@ bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, co
 
 bool GraphEdit::is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
 	Rect2 hotzone = Rect2(
-			p_pos.x - (p_left ? port_hotzone_outer_extent : port_hotzone_inner_extent),
+			p_pos.x - (p_left ? theme_cache.port_hotzone_outer_extent : theme_cache.port_hotzone_inner_extent),
 			p_pos.y - p_port_size.height / 2.0,
-			port_hotzone_inner_extent + port_hotzone_outer_extent,
+			theme_cache.port_hotzone_inner_extent + theme_cache.port_hotzone_outer_extent,
 			p_port_size.height);
 
 	if (!hotzone.has_point(p_mouse_pos)) {
@@ -965,12 +966,10 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
 	}
 
 	// Thickness below 0.5 doesn't look good on the graph or its minimap.
-	p_where->draw_polyline_colors(scaled_points, colors, MAX(0.5, Math::floor(p_width * get_theme_default_base_scale())), lines_antialiased);
+	p_where->draw_polyline_colors(scaled_points, colors, MAX(0.5, Math::floor(p_width * theme_cache.base_scale)), lines_antialiased);
 }
 
 void GraphEdit::_connections_layer_draw() {
-	Color activity_color = get_theme_color(SNAME("activity"));
-
 	// Draw connections.
 	List<List<Connection>::Element *> to_erase;
 	for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -998,8 +997,8 @@ void GraphEdit::_connections_layer_draw() {
 		Color tocolor = gnode_to->get_input_port_color(c.to_port);
 
 		if (c.activity > 0) {
-			color = color.lerp(activity_color, c.activity);
-			tocolor = tocolor.lerp(activity_color, c.activity);
+			color = color.lerp(theme_cache.activity_color, c.activity);
+			tocolor = tocolor.lerp(theme_cache.activity_color, c.activity);
 		}
 		_draw_connection_line(connections_layer, frompos, topos, color, tocolor, lines_thickness, zoom);
 	}
@@ -1042,8 +1041,8 @@ void GraphEdit::_top_layer_draw() {
 	}
 
 	if (box_selecting) {
-		top_layer->draw_rect(box_selecting_rect, get_theme_color(SNAME("selection_fill")));
-		top_layer->draw_rect(box_selecting_rect, get_theme_color(SNAME("selection_stroke")), false);
+		top_layer->draw_rect(box_selecting_rect, theme_cache.selection_fill);
+		top_layer->draw_rect(box_selecting_rect, theme_cache.selection_stroke, false);
 	}
 }
 
@@ -1056,7 +1055,7 @@ void GraphEdit::_minimap_draw() {
 
 	// Draw the minimap background.
 	Rect2 minimap_rect = Rect2(Point2(), minimap->get_size());
-	minimap->draw_style_box(minimap->get_theme_stylebox(SNAME("panel")), minimap_rect);
+	minimap->draw_style_box(minimap->theme_cache.panel, minimap_rect);
 
 	Vector2 graph_offset = minimap->_get_graph_offset();
 	Vector2 minimap_offset = minimap->minimap_offset;
@@ -1072,10 +1071,10 @@ void GraphEdit::_minimap_draw() {
 		Vector2 node_size = minimap->_convert_from_graph_position(graph_node->get_size() * zoom);
 		Rect2 node_rect = Rect2(node_position, node_size);
 
-		Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate();
+		Ref<StyleBoxFlat> sb_minimap = minimap->theme_cache.node_style->duplicate();
 
 		// Override default values with colors provided by the GraphNode's stylebox, if possible.
-		Ref<StyleBoxFlat> sb_frame = graph_node->get_theme_stylebox(graph_node->is_selected() ? "panel_selected" : "panel");
+		Ref<StyleBoxFlat> sb_frame = graph_node->is_selected() ? graph_node->theme_cache.panel_selected : graph_node->theme_cache.panel;
 		if (sb_frame.is_valid()) {
 			Color node_color = sb_frame->get_bg_color();
 			sb_minimap->set_bg_color(node_color);
@@ -1085,7 +1084,6 @@ void GraphEdit::_minimap_draw() {
 	}
 
 	// Draw node connections.
-	Color activity_color = get_theme_color(SNAME("activity"));
 	for (const Connection &E : connections) {
 		Node *from = get_node(NodePath(E.from_node));
 		GraphNode *graph_node_from = Object::cast_to<GraphNode>(from);
@@ -1107,19 +1105,19 @@ void GraphEdit::_minimap_draw() {
 		Color to_color = graph_node_to->get_input_port_color(E.to_port);
 
 		if (E.activity > 0) {
-			from_color = from_color.lerp(activity_color, E.activity);
-			to_color = to_color.lerp(activity_color, E.activity);
+			from_color = from_color.lerp(theme_cache.activity_color, E.activity);
+			to_color = to_color.lerp(theme_cache.activity_color, E.activity);
 		}
 		_draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.5, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
 	}
 
 	// Draw the "camera" viewport.
 	Rect2 camera_rect = minimap->get_camera_rect();
-	minimap->draw_style_box(minimap->get_theme_stylebox(SNAME("camera")), camera_rect);
+	minimap->draw_style_box(minimap->theme_cache.camera_style, camera_rect);
 
 	// Draw the resizer control.
-	Ref<Texture2D> resizer = minimap->get_theme_icon(SNAME("resizer"));
-	Color resizer_color = minimap->get_theme_color(SNAME("resizer_color"));
+	Ref<Texture2D> resizer = minimap->theme_cache.resizer;
+	Color resizer_color = minimap->theme_cache.resizer_color;
 	minimap->draw_texture(resizer, Point2(), resizer_color);
 }
 
@@ -1909,6 +1907,26 @@ void GraphEdit::_bind_methods() {
 
 	BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
 	BIND_ENUM_CONSTANT(SCROLL_PANS);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, panel);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_major);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_minor);
+
+	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, GraphEdit, activity_color, "activity");
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, selection_fill);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, selection_stroke);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_in);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_out);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_reset);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, snapping_toggle);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, grid_toggle);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, minimap_toggle);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, layout);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_inner_extent);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_outer_extent);
 }
 
 GraphEdit::GraphEdit() {

+ 53 - 20
scene/gui/graph_edit.h

@@ -47,6 +47,7 @@ class GraphEditFilter : public Control {
 
 	friend class GraphEdit;
 	friend class GraphEditMinimap;
+
 	GraphEdit *ge = nullptr;
 
 	virtual bool has_point(const Point2 &p_point) const override;
@@ -63,24 +64,24 @@ class GraphEditMinimap : public Control {
 
 	GraphEdit *ge = nullptr;
 
-public:
-	GraphEditMinimap(GraphEdit *p_edit);
-
-	virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
-
-	void update_minimap();
-	Rect2 get_camera_rect();
-
-private:
 	Vector2 minimap_padding;
 	Vector2 minimap_offset;
-	Vector2 graph_proportions;
-	Vector2 graph_padding;
-	Vector2 camera_position;
-	Vector2 camera_size;
+	Vector2 graph_proportions = Vector2(1, 1);
+	Vector2 graph_padding = Vector2(0, 0);
+	Vector2 camera_position = Vector2(100, 50);
+	Vector2 camera_size = Vector2(200, 200);
+
+	bool is_pressing = false;
+	bool is_resizing = false;
+
+	struct ThemeCache {
+		Ref<StyleBox> panel;
+		Ref<StyleBox> node_style;
+		Ref<StyleBox> camera_style;
 
-	bool is_pressing;
-	bool is_resizing;
+		Ref<Texture2D> resizer;
+		Color resizer_color;
+	} theme_cache;
 
 	Vector2 _get_render_size();
 	Vector2 _get_graph_offset();
@@ -92,6 +93,17 @@ private:
 	virtual void gui_input(const Ref<InputEvent> &p_ev) override;
 
 	void _adjust_graph_scroll(const Vector2 &p_offset);
+
+protected:
+	static void _bind_methods();
+
+public:
+	virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+
+	void update_minimap();
+	Rect2 get_camera_rect();
+
+	GraphEditMinimap(GraphEdit *p_edit);
 };
 
 class GraphEdit : public Control {
@@ -150,9 +162,6 @@ private:
 	HScrollBar *h_scrollbar = nullptr;
 	VScrollBar *v_scrollbar = nullptr;
 
-	float port_hotzone_inner_extent = 0.0;
-	float port_hotzone_outer_extent = 0.0;
-
 	Ref<ViewPanner> panner;
 	bool warped_panning = true;
 
@@ -218,7 +227,30 @@ private:
 	HashSet<int> valid_left_disconnect_types;
 	HashSet<int> valid_right_disconnect_types;
 
-	void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+	struct ThemeCache {
+		float base_scale = 1.0;
+
+		Ref<StyleBox> panel;
+		Color grid_major;
+		Color grid_minor;
+
+		Color activity_color;
+		Color selection_fill;
+		Color selection_stroke;
+
+		Ref<Texture2D> zoom_in;
+		Ref<Texture2D> zoom_out;
+		Ref<Texture2D> zoom_reset;
+
+		Ref<Texture2D> snapping_toggle;
+		Ref<Texture2D> grid_toggle;
+		Ref<Texture2D> minimap_toggle;
+		Ref<Texture2D> layout;
+
+		float port_hotzone_inner_extent = 0.0;
+		float port_hotzone_outer_extent = 0.0;
+	} theme_cache;
+
 	void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
 	void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
 
@@ -262,12 +294,13 @@ private:
 	bool _check_clickable_control(Control *p_control, const Vector2 &r_mouse_pos, const Vector2 &p_offset);
 
 protected:
-	static void _bind_methods();
+	virtual void _update_theme_item_cache() override;
 
 	virtual void add_child_notify(Node *p_child) override;
 	virtual void remove_child_notify(Node *p_child) override;
 
 	void _notification(int p_what);
+	static void _bind_methods();
 
 	virtual bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
 	virtual bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);

+ 4 - 3
scene/gui/graph_element.cpp

@@ -32,6 +32,7 @@
 
 #include "core/string/translation.h"
 #include "scene/gui/graph_edit.h"
+#include "scene/theme/theme_db.h"
 
 #ifdef TOOLS_ENABLED
 void GraphElement::_edit_set_position(const Point2 &p_position) {
@@ -154,9 +155,7 @@ void GraphElement::gui_input(const Ref<InputEvent> &p_ev) {
 		if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
 			Vector2 mpos = mb->get_position();
 
-			Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
-
-			if (resizable && mpos.x > get_size().x - resizer->get_width() && mpos.y > get_size().y - resizer->get_height()) {
+			if (resizable && mpos.x > get_size().x - theme_cache.resizer->get_width() && mpos.y > get_size().y - theme_cache.resizer->get_height()) {
 				resizing = true;
 				resizing_from = mpos;
 				resizing_from_size = get_size();
@@ -241,4 +240,6 @@ void GraphElement::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("raise_request"));
 	ADD_SIGNAL(MethodInfo("close_request"));
 	ADD_SIGNAL(MethodInfo("resize_request", PropertyInfo(Variant::VECTOR2, "new_minsize")));
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphElement, resizer);
 }

+ 4 - 0
scene/gui/graph_element.h

@@ -49,6 +49,10 @@ protected:
 
 	Vector2 position_offset;
 
+	struct ThemeCache {
+		Ref<Texture2D> resizer;
+	} theme_cache;
+
 #ifdef TOOLS_ENABLED
 	void _edit_set_position(const Point2 &p_position) override;
 #endif

+ 35 - 27
scene/gui/graph_node.cpp

@@ -33,6 +33,7 @@
 #include "core/string/translation.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/label.h"
+#include "scene/theme/theme_db.h"
 
 bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
 	String str = p_name;
@@ -151,8 +152,8 @@ void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
 
 void GraphNode::_resort() {
 	Size2 new_size = get_size();
-	Ref<StyleBox> sb_panel = get_theme_stylebox(SNAME("panel"));
-	Ref<StyleBox> sb_titlebar = get_theme_stylebox(SNAME("titlebar"));
+	Ref<StyleBox> sb_panel = theme_cache.panel;
+	Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
 
 	// Resort titlebar first.
 	Size2 titlebar_size = Size2(new_size.width, titlebar_hbox->get_size().height);
@@ -164,8 +165,8 @@ void GraphNode::_resort() {
 	Size2i titlebar_min_size = titlebar_hbox->get_combined_minimum_size();
 
 	// First pass, determine minimum size AND amount of stretchable elements.
-	Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot"));
-	int separation = get_theme_constant(SNAME("separation"));
+	Ref<StyleBox> sb_slot = theme_cache.slot;
+	int separation = theme_cache.separation;
 
 	int children_count = 0;
 	int stretch_min = 0;
@@ -300,7 +301,7 @@ void GraphNode::draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Co
 
 	Point2 icon_offset;
 	if (!port_icon.is_valid()) {
-		port_icon = get_theme_icon(SNAME("port"));
+		port_icon = theme_cache.port;
 	}
 
 	icon_offset = -port_icon->get_size() * 0.5;
@@ -311,19 +312,15 @@ void GraphNode::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_DRAW: {
 			// Used for layout calculations.
-			Ref<StyleBox> sb_panel = get_theme_stylebox(SNAME("panel"));
-			Ref<StyleBox> sb_titlebar = get_theme_stylebox(SNAME("titlebar"));
+			Ref<StyleBox> sb_panel = theme_cache.panel;
+			Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
 			// Used for drawing.
-			Ref<StyleBox> sb_to_draw_panel = get_theme_stylebox(selected ? SNAME("panel_selected") : SNAME("panel"));
-			Ref<StyleBox> sb_to_draw_titlebar = get_theme_stylebox(selected ? SNAME("titlebar_selected") : SNAME("titlebar"));
+			Ref<StyleBox> sb_to_draw_panel = selected ? theme_cache.panel_selected : theme_cache.panel;
+			Ref<StyleBox> sb_to_draw_titlebar = selected ? theme_cache.titlebar_selected : theme_cache.titlebar;
 
-			Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot"));
+			Ref<StyleBox> sb_slot = theme_cache.slot;
 
-			int port_h_offset = get_theme_constant(SNAME("port_h_offset"));
-
-			Ref<Texture2D> resizer_icon = get_theme_icon(SNAME("resizer"));
-
-			Color resizer_color = get_theme_color(SNAME("resizer_color"));
+			int port_h_offset = theme_cache.port_h_offset;
 
 			Rect2 titlebar_rect(Point2(), titlebar_hbox->get_size() + sb_titlebar->get_minimum_size());
 			Size2 body_size = get_size();
@@ -377,7 +374,7 @@ void GraphNode::_notification(int p_what) {
 			}
 
 			if (resizable) {
-				draw_texture(resizer_icon, get_size() - resizer_icon->get_size(), resizer_color);
+				draw_texture(theme_cache.resizer, get_size() - theme_cache.resizer->get_size(), theme_cache.resizer_color);
 			}
 		} break;
 	}
@@ -566,11 +563,11 @@ void GraphNode::set_slot_draw_stylebox(int p_slot_index, bool p_enable) {
 }
 
 Size2 GraphNode::get_minimum_size() const {
-	Ref<StyleBox> sb_panel = get_theme_stylebox(SNAME("panel"));
-	Ref<StyleBox> sb_titlebar = get_theme_stylebox(SNAME("titlebar"));
-	Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot"));
+	Ref<StyleBox> sb_panel = theme_cache.panel;
+	Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
+	Ref<StyleBox> sb_slot = theme_cache.slot;
 
-	int separation = get_theme_constant(SNAME("separation"));
+	int separation = theme_cache.separation;
 	Size2 minsize = titlebar_hbox->get_minimum_size() + sb_titlebar->get_minimum_size();
 
 	for (int i = 0; i < get_child_count(false); i++) {
@@ -599,11 +596,11 @@ Size2 GraphNode::get_minimum_size() const {
 }
 
 void GraphNode::_port_pos_update() {
-	int edgeofs = get_theme_constant(SNAME("port_h_offset"));
-	int separation = get_theme_constant(SNAME("separation"));
+	int edgeofs = theme_cache.port_h_offset;
+	int separation = theme_cache.separation;
 
-	Ref<StyleBox> sb_panel = get_theme_stylebox(SNAME("panel"));
-	Ref<StyleBox> sb_titlebar = get_theme_stylebox(SNAME("titlebar"));
+	Ref<StyleBox> sb_panel = theme_cache.panel;
+	Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
 
 	left_port_cache.clear();
 	right_port_cache.clear();
@@ -754,9 +751,7 @@ HBoxContainer *GraphNode::get_titlebar_hbox() {
 
 Control::CursorShape GraphNode::get_cursor_shape(const Point2 &p_pos) const {
 	if (resizable) {
-		Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
-
-		if (resizing || (p_pos.x > get_size().x - resizer->get_width() && p_pos.y > get_size().y - resizer->get_height())) {
+		if (resizing || (p_pos.x > get_size().x - theme_cache.resizer->get_width() && p_pos.y > get_size().y - theme_cache.resizer->get_height())) {
 			return CURSOR_FDIAGSIZE;
 		}
 	}
@@ -830,6 +825,19 @@ void GraphNode::_bind_methods() {
 
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
 	ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "slot_index")));
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, panel);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, panel_selected);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, titlebar);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, titlebar_selected);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, slot);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphNode, separation);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphNode, port_h_offset);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphNode, port);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphNode, resizer);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphNode, resizer_color);
 }
 
 GraphNode::GraphNode() {

+ 17 - 1
scene/gui/graph_node.h

@@ -38,6 +38,8 @@ class HBoxContainer;
 class GraphNode : public GraphElement {
 	GDCLASS(GraphNode, GraphElement);
 
+	friend class GraphEdit;
+
 	struct Slot {
 		bool enable_left = false;
 		int type_left = 0;
@@ -74,9 +76,23 @@ class GraphNode : public GraphElement {
 	Vector<PortCache> right_port_cache;
 
 	HashMap<int, Slot> slot_table;
-
 	Vector<int> slot_y_cache;
 
+	struct ThemeCache {
+		Ref<StyleBox> panel;
+		Ref<StyleBox> panel_selected;
+		Ref<StyleBox> titlebar;
+		Ref<StyleBox> titlebar_selected;
+		Ref<StyleBox> slot;
+
+		int separation = 0;
+		int port_h_offset = 0;
+
+		Ref<Texture2D> port;
+		Ref<Texture2D> resizer;
+		Color resizer_color;
+	} theme_cache;
+
 	bool port_pos_dirty = true;
 
 	void _port_pos_update();

+ 4 - 0
scene/gui/grid_container.cpp

@@ -259,6 +259,10 @@ int GridContainer::get_columns() const {
 	return columns;
 }
 
+int GridContainer::get_h_separation() const {
+	return theme_cache.h_separation;
+}
+
 void GridContainer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_columns", "columns"), &GridContainer::set_columns);
 	ClassDB::bind_method(D_METHOD("get_columns"), &GridContainer::get_columns);

+ 2 - 0
scene/gui/grid_container.h

@@ -52,6 +52,8 @@ public:
 	int get_columns() const;
 	virtual Size2 get_minimum_size() const override;
 
+	int get_h_separation() const;
+
 	GridContainer();
 };
 

+ 17 - 0
scene/gui/margin_container.cpp

@@ -80,6 +80,23 @@ Vector<int> MarginContainer::get_allowed_size_flags_vertical() const {
 	return flags;
 }
 
+int MarginContainer::get_margin_size(Side p_side) const {
+	ERR_FAIL_INDEX_V((int)p_side, 4, 0);
+
+	switch (p_side) {
+		case SIDE_LEFT:
+			return theme_cache.margin_left;
+		case SIDE_RIGHT:
+			return theme_cache.margin_right;
+		case SIDE_TOP:
+			return theme_cache.margin_top;
+		case SIDE_BOTTOM:
+			return theme_cache.margin_bottom;
+	}
+
+	return 0;
+}
+
 void MarginContainer::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_SORT_CHILDREN: {

+ 2 - 0
scene/gui/margin_container.h

@@ -53,6 +53,8 @@ public:
 	virtual Vector<int> get_allowed_size_flags_horizontal() const override;
 	virtual Vector<int> get_allowed_size_flags_vertical() const override;
 
+	int get_margin_size(Side p_side) const;
+
 	MarginContainer();
 };
 

+ 2 - 2
scene/gui/popup_menu.cpp

@@ -559,8 +559,8 @@ void PopupMenu::_draw_items() {
 	RID ci = control->get_canvas_item();
 
 	Size2 margin_size;
-	margin_size.width = margin_container->get_theme_constant(SNAME("margin_right")) + margin_container->get_theme_constant(SNAME("margin_left"));
-	margin_size.height = margin_container->get_theme_constant(SNAME("margin_top")) + margin_container->get_theme_constant(SNAME("margin_bottom"));
+	margin_size.width = margin_container->get_margin_size(SIDE_LEFT) + margin_container->get_margin_size(SIDE_RIGHT);
+	margin_size.height = margin_container->get_margin_size(SIDE_TOP) + margin_container->get_margin_size(SIDE_BOTTOM);
 
 	// Space between the item content and the sides of popup menu.
 	bool rtl = control->is_layout_rtl();

+ 7 - 6
scene/gui/rich_text_label.cpp

@@ -5817,6 +5817,9 @@ void RichTextLabel::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, RichTextLabel, mono_font);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, RichTextLabel, mono_font_size);
 
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, text_highlight_h_padding);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, text_highlight_v_padding);
+
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, table_h_separation);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, table_v_separation);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_odd_row_bg);
@@ -6029,8 +6032,6 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item
 	Vector2i fbg_index = Vector2i(end, start);
 	Color last_color = Color(0, 0, 0, 0);
 	bool draw_box = false;
-	int hpad = get_theme_constant(SNAME("text_highlight_h_padding"));
-	int vpad = get_theme_constant(SNAME("text_highlight_v_padding"));
 	// Draw a box based on color tags associated with glyphs
 	for (int i = start; i < end; i++) {
 		Item *it = _get_item_at_pos(it_from, it_to, i);
@@ -6060,8 +6061,8 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item
 		if (draw_box) {
 			Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, fbg_index.y);
 			for (int j = 0; j < sel.size(); j++) {
-				Vector2 rect_off = line_off + Vector2(sel[j].x - hpad, -TS->shaped_text_get_ascent(p_rid) - vpad);
-				Vector2 rect_size = Vector2(sel[j].y - sel[j].x + 2 * hpad, TS->shaped_text_get_size(p_rid).y + 2 * vpad);
+				Vector2 rect_off = line_off + Vector2(sel[j].x - theme_cache.text_highlight_h_padding, -TS->shaped_text_get_ascent(p_rid) - theme_cache.text_highlight_v_padding);
+				Vector2 rect_size = Vector2(sel[j].y - sel[j].x + 2 * theme_cache.text_highlight_h_padding, TS->shaped_text_get_size(p_rid).y + 2 * theme_cache.text_highlight_v_padding);
 				RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
 			}
 			fbg_index = Vector2i(end, start);
@@ -6079,8 +6080,8 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item
 	if (last_color.a > 0) {
 		Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, end);
 		for (int i = 0; i < sel.size(); i++) {
-			Vector2 rect_off = line_off + Vector2(sel[i].x - hpad, -TS->shaped_text_get_ascent(p_rid) - vpad);
-			Vector2 rect_size = Vector2(sel[i].y - sel[i].x + 2 * hpad, TS->shaped_text_get_size(p_rid).y + 2 * vpad);
+			Vector2 rect_off = line_off + Vector2(sel[i].x - theme_cache.text_highlight_h_padding, -TS->shaped_text_get_ascent(p_rid) - theme_cache.text_highlight_v_padding);
+			Vector2 rect_size = Vector2(sel[i].y - sel[i].x + 2 * theme_cache.text_highlight_h_padding, TS->shaped_text_get_size(p_rid).y + 2 * theme_cache.text_highlight_v_padding);
 			RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
 		}
 	}

+ 3 - 0
scene/gui/rich_text_label.h

@@ -592,6 +592,9 @@ private:
 		Ref<Font> mono_font;
 		int mono_font_size;
 
+		int text_highlight_h_padding;
+		int text_highlight_v_padding;
+
 		int table_h_separation;
 		int table_v_separation;
 		Color table_odd_row_bg;

+ 3 - 3
scene/gui/split_container.cpp

@@ -97,7 +97,7 @@ void SplitContainerDragger::_notification(int p_what) {
 		case NOTIFICATION_MOUSE_ENTER: {
 			mouse_inside = true;
 			SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
-			if (sc->get_theme_constant(SNAME("autohide"))) {
+			if (sc->theme_cache.autohide) {
 				queue_redraw();
 			}
 		} break;
@@ -105,14 +105,14 @@ void SplitContainerDragger::_notification(int p_what) {
 		case NOTIFICATION_MOUSE_EXIT: {
 			mouse_inside = false;
 			SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
-			if (sc->get_theme_constant(SNAME("autohide"))) {
+			if (sc->theme_cache.autohide) {
 				queue_redraw();
 			}
 		} break;
 
 		case NOTIFICATION_DRAW: {
 			SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
-			if (!dragging && !mouse_inside && sc->get_theme_constant(SNAME("autohide"))) {
+			if (!dragging && !mouse_inside && sc->theme_cache.autohide) {
 				return;
 			}
 

+ 1 - 3
scene/gui/split_container.h

@@ -73,7 +73,7 @@ private:
 	struct ThemeCache {
 		int separation = 0;
 		int minimum_grab_thickness = 0;
-		int autohide = 0;
+		bool autohide = false;
 		Ref<Texture2D> grabber_icon;
 		Ref<Texture2D> grabber_icon_h;
 		Ref<Texture2D> grabber_icon_v;
@@ -85,8 +85,6 @@ private:
 	void _compute_middle_sep(bool p_clamp);
 	void _resort();
 
-	void _dragging_area_gui_input(const Ref<InputEvent> &p_event);
-
 protected:
 	bool is_fixed = false;
 

+ 6 - 13
scene/gui/text_edit.cpp

@@ -1254,7 +1254,7 @@ void TextEdit::_notification(int p_what) {
 									if ((brace_matching[c].open_match_line == line && brace_matching[c].open_match_column == glyphs[j].start) ||
 											(get_caret_column(c) == glyphs[j].start && get_caret_line(c) == line && carets_wrap_index[c] == line_wrap_index && (brace_matching[c].open_matching || brace_matching[c].open_mismatch))) {
 										if (brace_matching[c].open_mismatch) {
-											gl_color = theme_cache.brace_mismatch_color;
+											gl_color = _get_brace_mismatch_color();
 										}
 										Rect2 rect = Rect2(char_pos, ofs_y + theme_cache.font->get_underline_position(theme_cache.font_size), glyphs[j].advance * glyphs[j].repeat, MAX(theme_cache.font->get_underline_thickness(theme_cache.font_size) * theme_cache.base_scale, 1));
 										draw_rect(rect, gl_color);
@@ -1263,7 +1263,7 @@ void TextEdit::_notification(int p_what) {
 									if ((brace_matching[c].close_match_line == line && brace_matching[c].close_match_column == glyphs[j].start) ||
 											(get_caret_column(c) == glyphs[j].start + 1 && get_caret_line(c) == line && carets_wrap_index[c] == line_wrap_index && (brace_matching[c].close_matching || brace_matching[c].close_mismatch))) {
 										if (brace_matching[c].close_mismatch) {
-											gl_color = theme_cache.brace_mismatch_color;
+											gl_color = _get_brace_mismatch_color();
 										}
 										Rect2 rect = Rect2(char_pos, ofs_y + theme_cache.font->get_underline_position(theme_cache.font_size), glyphs[j].advance * glyphs[j].repeat, MAX(theme_cache.font->get_underline_thickness(theme_cache.font_size) * theme_cache.base_scale, 1));
 										draw_rect(rect, gl_color);
@@ -1313,12 +1313,12 @@ void TextEdit::_notification(int p_what) {
 
 					// is_line_folded
 					if (line_wrap_index == line_wrap_amount && line < text.size() - 1 && _is_line_hidden(line + 1)) {
-						int xofs = char_ofs + char_margin + ofs_x + (theme_cache.folded_eol_icon->get_width() / 2);
+						int xofs = char_ofs + char_margin + ofs_x + (_get_folded_eol_icon()->get_width() / 2);
 						if (xofs >= xmargin_beg && xofs < xmargin_end) {
-							int yofs = (text_height - theme_cache.folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
-							Color eol_color = theme_cache.code_folding_color;
+							int yofs = (text_height - _get_folded_eol_icon()->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
+							Color eol_color = _get_code_folding_color();
 							eol_color.a = 1;
-							theme_cache.folded_eol_icon->draw(ci, Point2(xofs, ofs_y + yofs), eol_color);
+							_get_folded_eol_icon()->draw(ci, Point2(xofs, ofs_y + yofs), eol_color);
 						}
 					}
 
@@ -3000,7 +3000,6 @@ void TextEdit::_update_theme_item_cache() {
 	Control::_update_theme_item_cache();
 
 	theme_cache.base_scale = get_theme_default_base_scale();
-	theme_cache.folded_code_region_color = get_theme_color(SNAME("folded_code_region_color"), SNAME("CodeEdit"));
 	use_selected_font_color = theme_cache.font_selected_color != Color(0, 0, 0, 0);
 
 	if (text.get_line_height() + theme_cache.line_spacing < 1) {
@@ -6474,12 +6473,6 @@ void TextEdit::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("gutter_removed"));
 
 	/* Theme items */
-	/* Internal API for CodeEdit */
-	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, TextEdit, brace_mismatch_color, "brace_mismatch_color", "CodeEdit");
-	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, TextEdit, code_folding_color, "code_folding_color", "CodeEdit");
-	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, TextEdit, folded_code_region_color, "folded_code_region_color", "CodeEdit");
-	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, TextEdit, folded_eol_icon, "folded_eol_icon", "CodeEdit");
-
 	/* Search */
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TextEdit, search_result_color);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TextEdit, search_result_border_color);

+ 8 - 7
scene/gui/text_edit.h

@@ -41,6 +41,8 @@
 class TextEdit : public Control {
 	GDCLASS(TextEdit, Control);
 
+	friend class CodeHighlighter;
+
 public:
 	/* Edit Actions. */
 	enum EditAction {
@@ -543,12 +545,6 @@ private:
 	struct ThemeCache {
 		float base_scale = 1.0;
 
-		/* Internal API for CodeEdit */
-		Color brace_mismatch_color;
-		Color code_folding_color = Color(1, 1, 1);
-		Color folded_code_region_color = Color(1, 1, 1);
-		Ref<Texture2D> folded_eol_icon;
-
 		/* Search */
 		Color search_result_color = Color(1, 1, 1);
 		Color search_result_border_color = Color(1, 1, 1);
@@ -633,7 +629,7 @@ protected:
 	virtual void _update_theme_item_cache() override;
 
 	/* Internal API for CodeEdit, pending public API. */
-	// brace matching
+	// Brace matching.
 	struct BraceMatchingData {
 		int open_match_line = -1;
 		int open_match_column = -1;
@@ -662,6 +658,11 @@ protected:
 	String lookup_symbol_word;
 	void _set_symbol_lookup_word(const String &p_symbol);
 
+	// Theme items.
+	virtual Color _get_brace_mismatch_color() const { return Color(); };
+	virtual Color _get_code_folding_color() const { return Color(); };
+	virtual Ref<Texture2D> _get_folded_eol_icon() const { return Ref<Texture2D>(); };
+
 	/* Text manipulation */
 
 	// Overridable actions

+ 19 - 19
scene/main/viewport.cpp

@@ -319,16 +319,16 @@ void Viewport::_sub_window_update(Window *p_window) {
 	Rect2i r = Rect2i(p_window->get_position(), sw.window->get_size());
 
 	if (!p_window->get_flag(Window::FLAG_BORDERLESS)) {
-		Ref<StyleBox> panel = p_window->get_theme_stylebox(gui.subwindow_focused == p_window ? SNAME("embedded_border") : SNAME("embedded_unfocused_border"));
+		Ref<StyleBox> panel = gui.subwindow_focused == p_window ? p_window->theme_cache.embedded_border : p_window->theme_cache.embedded_unfocused_border;
 		panel->draw(sw.canvas_item, r);
 
 		// Draw the title bar text.
-		Ref<Font> title_font = p_window->get_theme_font(SNAME("title_font"));
-		int font_size = p_window->get_theme_font_size(SNAME("title_font_size"));
-		Color title_color = p_window->get_theme_color(SNAME("title_color"));
-		int title_height = p_window->get_theme_constant(SNAME("title_height"));
-		int close_h_ofs = p_window->get_theme_constant(SNAME("close_h_offset"));
-		int close_v_ofs = p_window->get_theme_constant(SNAME("close_v_offset"));
+		Ref<Font> title_font = p_window->theme_cache.title_font;
+		int font_size = p_window->theme_cache.title_font_size;
+		Color title_color = p_window->theme_cache.title_color;
+		int title_height = p_window->theme_cache.title_height;
+		int close_h_ofs = p_window->theme_cache.close_h_offset;
+		int close_v_ofs = p_window->theme_cache.close_v_offset;
 
 		TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size);
 		title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs);
@@ -336,15 +336,15 @@ void Viewport::_sub_window_update(Window *p_window) {
 		int x = (r.size.width - title_text.get_size().x) / 2;
 		int y = (-title_height - title_text.get_size().y) / 2;
 
-		Color font_outline_color = p_window->get_theme_color(SNAME("title_outline_modulate"));
-		int outline_size = p_window->get_theme_constant(SNAME("title_outline_size"));
+		Color font_outline_color = p_window->theme_cache.title_outline_modulate;
+		int outline_size = p_window->theme_cache.title_outline_size;
 		if (outline_size > 0 && font_outline_color.a > 0) {
 			title_text.draw_outline(sw.canvas_item, r.position + Point2(x, y), outline_size, font_outline_color);
 		}
 		title_text.draw(sw.canvas_item, r.position + Point2(x, y), title_color);
 
 		bool pressed = gui.subwindow_focused == sw.window && gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE && gui.subwindow_drag_close_inside;
-		Ref<Texture2D> close_icon = p_window->get_theme_icon(pressed ? "close_pressed" : "close");
+		Ref<Texture2D> close_icon = pressed ? p_window->theme_cache.close_pressed : p_window->theme_cache.close;
 		close_icon->draw(sw.canvas_item, r.position + Vector2(r.size.width - close_h_ofs, -close_v_ofs));
 	}
 
@@ -2039,7 +2039,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
 					Window *sw = embedder->gui.sub_windows[i].window;
 					Rect2 swrect = Rect2i(sw->get_position(), sw->get_size());
 					if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
-						int title_height = sw->get_theme_constant(SNAME("title_height"));
+						int title_height = sw->theme_cache.title_height;
 						swrect.position.y -= title_height;
 						swrect.size.y += title_height;
 					}
@@ -2669,7 +2669,7 @@ Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subw
 
 	Rect2i r = Rect2i(p_subwindow->get_position(), p_subwindow->get_size());
 
-	int title_height = p_subwindow->get_theme_constant(SNAME("title_height"));
+	int title_height = p_subwindow->theme_cache.title_height;
 
 	r.position.y -= title_height;
 	r.size.y += title_height;
@@ -2681,7 +2681,7 @@ Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subw
 	int dist_x = p_point.x < r.position.x ? (p_point.x - r.position.x) : (p_point.x > (r.position.x + r.size.x) ? (p_point.x - (r.position.x + r.size.x)) : 0);
 	int dist_y = p_point.y < r.position.y ? (p_point.y - r.position.y) : (p_point.y > (r.position.y + r.size.y) ? (p_point.y - (r.position.y + r.size.y)) : 0);
 
-	int limit = p_subwindow->get_theme_constant(SNAME("resize_margin"));
+	int limit = p_subwindow->theme_cache.resize_margin;
 
 	if (ABS(dist_x) > limit) {
 		return SUB_WINDOW_RESIZE_DISABLED;
@@ -2866,7 +2866,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
 
 			if (!sw.window->get_flag(Window::FLAG_BORDERLESS)) {
 				// Check top bar.
-				int title_height = sw.window->get_theme_constant(SNAME("title_height"));
+				int title_height = sw.window->theme_cache.title_height;
 				Rect2i title_bar = r;
 				title_bar.position.y -= title_height;
 				title_bar.size.y = title_height;
@@ -2874,9 +2874,9 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
 				if (title_bar.size.y > 0 && title_bar.has_point(mb->get_position())) {
 					click_on_window = sw.window;
 
-					int close_h_ofs = sw.window->get_theme_constant(SNAME("close_h_offset"));
-					int close_v_ofs = sw.window->get_theme_constant(SNAME("close_v_offset"));
-					Ref<Texture2D> close_icon = sw.window->get_theme_icon(SNAME("close"));
+					int close_h_ofs = sw.window->theme_cache.close_h_offset;
+					int close_v_ofs = sw.window->theme_cache.close_v_offset;
+					Ref<Texture2D> close_icon = sw.window->theme_cache.close;
 
 					Rect2 close_rect;
 					close_rect.position = Vector2(r.position.x + r.size.x - close_h_ofs, r.position.y - close_v_ofs);
@@ -3016,8 +3016,8 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
 			Rect2 swrect_border = swrect;
 
 			if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
-				int title_height = sw->get_theme_constant(SNAME("title_height"));
-				int margin = sw->get_theme_constant(SNAME("resize_margin"));
+				int title_height = sw->theme_cache.title_height;
+				int margin = sw->theme_cache.resize_margin;
 				swrect_border.position.y -= title_height + margin;
 				swrect_border.size.y += title_height + margin * 2;
 				swrect_border.position.x -= margin;

+ 18 - 1
scene/main/window.cpp

@@ -1804,7 +1804,7 @@ Rect2i Window::fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) co
 		p_rect.position.x = 0;
 	}
 
-	int title_height = get_flag(Window::FLAG_BORDERLESS) ? 0 : get_theme_constant(SNAME("title_height"));
+	int title_height = get_flag(Window::FLAG_BORDERLESS) ? 0 : theme_cache.title_height;
 
 	if (p_rect.position.y < title_height) {
 		p_rect.position.y = title_height;
@@ -2910,6 +2910,23 @@ void Window::_bind_methods() {
 	BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS);
 
 	GDVIRTUAL_BIND(_get_contents_minimum_size);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Window, embedded_border);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Window, embedded_unfocused_border);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, Window, title_font);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, Window, title_font_size);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Window, title_color);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, title_height);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Window, title_outline_modulate);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, title_outline_size);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Window, close);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Window, close_pressed);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, close_h_offset);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, close_v_offset);
+
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, resize_margin);
 }
 
 Window::Window() {

+ 23 - 3
scene/main/window.h

@@ -42,7 +42,8 @@ class ThemeOwner;
 class ThemeContext;
 
 class Window : public Viewport {
-	GDCLASS(Window, Viewport)
+	GDCLASS(Window, Viewport);
+
 public:
 	// Keep synced with enum hint for `mode` property.
 	enum Mode {
@@ -191,6 +192,25 @@ private:
 	void _notify_theme_override_changed();
 	void _invalidate_theme_cache();
 
+	struct ThemeCache {
+		Ref<StyleBox> embedded_border;
+		Ref<StyleBox> embedded_unfocused_border;
+
+		Ref<Font> title_font;
+		int title_font_size = 0;
+		Color title_color;
+		int title_height = 0;
+		Color title_outline_modulate;
+		int title_outline_size = 0;
+
+		Ref<Texture2D> close;
+		Ref<Texture2D> close_pressed;
+		int close_h_offset = 0;
+		int close_v_offset = 0;
+
+		int resize_margin = 0;
+	} theme_cache;
+
 	Viewport *embedder = nullptr;
 
 	Transform2D window_transform;
@@ -212,12 +232,12 @@ private:
 
 protected:
 	virtual Rect2i _popup_adjust_rect() const { return Rect2i(); }
+	virtual void _post_popup() {}
 
 	virtual void _update_theme_item_cache();
 
-	virtual void _post_popup() {}
-	static void _bind_methods();
 	void _notification(int p_what);
+	static void _bind_methods();
 
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;

+ 1 - 1
scene/resources/syntax_highlighter.cpp

@@ -419,7 +419,7 @@ void CodeHighlighter::_clear_highlighting_cache() {
 }
 
 void CodeHighlighter::_update_cache() {
-	font_color = text_edit->get_theme_color(SNAME("font_color"));
+	font_color = text_edit->theme_cache.font_color;
 }
 
 void CodeHighlighter::add_keyword_color(const String &p_keyword, const Color &p_color) {