Add a focus border on ScrollContainer
Also added new unit tests for `Control`. Co-authored-by: ator-dev <dominic.codedeveloper@gmail.com>
This commit is contained in:
parent
58a7f9b4d8
commit
1ae83e690a
9 changed files with 107 additions and 2 deletions
|
@ -28,6 +28,7 @@
|
|||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
|
||||
<member name="follow_focus" type="bool" setter="set_follow_focus" getter="is_following_focus" overrides="ScrollContainer" default="true" />
|
||||
<member name="horizontal_scroll_mode" type="int" setter="set_horizontal_scroll_mode" getter="get_horizontal_scroll_mode" overrides="ScrollContainer" enum="ScrollContainer.ScrollMode" default="0" />
|
||||
</members>
|
||||
|
|
|
@ -107,6 +107,9 @@
|
|||
</constant>
|
||||
</constants>
|
||||
<theme_items>
|
||||
<theme_item name="focus" data_type="style" type="StyleBox">
|
||||
The focus border [StyleBox] of the [ScrollContainer].
|
||||
</theme_item>
|
||||
<theme_item name="panel" data_type="style" type="StyleBox">
|
||||
The background [StyleBox] of the [ScrollContainer].
|
||||
</theme_item>
|
||||
|
|
|
@ -4306,6 +4306,7 @@ EditorInspector::EditorInspector() {
|
|||
search_box = nullptr;
|
||||
_prop_edited = "property_edited";
|
||||
set_process(false);
|
||||
set_focus_mode(FocusMode::FOCUS_ALL);
|
||||
property_focusable = -1;
|
||||
property_clipboard = Variant();
|
||||
|
||||
|
@ -4324,4 +4325,6 @@ EditorInspector::EditorInspector() {
|
|||
|
||||
// `use_settings_name_style` is true by default, set the name style accordingly.
|
||||
set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
|
||||
|
||||
set_draw_focus_border(true);
|
||||
}
|
||||
|
|
|
@ -3263,6 +3263,8 @@ void EditorPropertyResource::update_property() {
|
|||
sub_inspector->set_read_only(is_read_only());
|
||||
sub_inspector->set_use_folding(is_using_folding());
|
||||
|
||||
sub_inspector->set_draw_focus_border(false);
|
||||
|
||||
sub_inspector->set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
add_child(sub_inspector);
|
||||
set_bottom_editor(sub_inspector);
|
||||
|
|
|
@ -1811,11 +1811,19 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
|
|||
|
||||
// Editor focus.
|
||||
p_theme->set_stylebox("Focus", EditorStringName(EditorStyles), p_config.button_style_focus);
|
||||
// Use a less opaque color to be less distracting for the 2D and 3D editor viewports.
|
||||
|
||||
Ref<StyleBoxFlat> style_widget_focus_viewport = p_config.button_style_focus->duplicate();
|
||||
// Make the focus outline appear to be flush with the buttons it's focusing, so not draw on top of the content.
|
||||
style_widget_focus_viewport->set_expand_margin_all(2);
|
||||
// Use a less opaque color to be less distracting for the 2D and 3D editor viewports.
|
||||
style_widget_focus_viewport->set_border_color(p_config.accent_color * Color(1, 1, 1, 0.5));
|
||||
p_theme->set_stylebox("FocusViewport", EditorStringName(EditorStyles), style_widget_focus_viewport);
|
||||
|
||||
Ref<StyleBoxFlat> style_widget_scroll_container = p_config.button_style_focus->duplicate();
|
||||
// Make the focus outline appear to be flush with the buttons it's focusing, so not draw on top of the content.
|
||||
style_widget_scroll_container->set_expand_margin_all(4);
|
||||
p_theme->set_stylebox(SNAME("focus"), "ScrollContainer", style_widget_scroll_container);
|
||||
|
||||
// This stylebox is used in 3d and 2d viewports (no borders).
|
||||
Ref<StyleBoxFlat> style_content_panel_vp = p_config.content_panel_style->duplicate();
|
||||
style_content_panel_vp->set_content_margin_individual(p_config.border_width * 2, p_config.base_margin * EDSCALE, p_config.border_width * 2, p_config.border_width * 2);
|
||||
|
|
|
@ -281,6 +281,7 @@ void ScrollContainer::_gui_focus_changed(Control *p_control) {
|
|||
if (follow_focus && is_ancestor_of(p_control)) {
|
||||
ensure_control_visible(p_control);
|
||||
}
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void ScrollContainer::ensure_control_visible(Control *p_control) {
|
||||
|
@ -366,6 +367,14 @@ void ScrollContainer::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_DRAW: {
|
||||
draw_style_box(theme_cache.panel_style, Rect2(Vector2(), get_size()));
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (_draw_focus_border && Engine::get_singleton()->is_editor_hint() && (has_focus() || child_has_focus())) {
|
||||
RID ci = get_canvas_item();
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
|
||||
draw_style_box(theme_cache.focus_style, Rect2(Point2(), get_size()));
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
|
@ -623,10 +632,26 @@ void ScrollContainer::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(SCROLL_MODE_RESERVE);
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, panel_style, "panel");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, focus_style, "focus");
|
||||
|
||||
GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
|
||||
};
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void ScrollContainer::set_draw_focus_border(bool p_draw) {
|
||||
_draw_focus_border = p_draw;
|
||||
}
|
||||
|
||||
bool ScrollContainer::get_draw_focus_border() {
|
||||
return _draw_focus_border;
|
||||
}
|
||||
|
||||
bool ScrollContainer::child_has_focus() {
|
||||
const Control *focus_owner = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr;
|
||||
return focus_owner && is_ancestor_of(focus_owner);
|
||||
}
|
||||
#endif
|
||||
|
||||
ScrollContainer::ScrollContainer() {
|
||||
h_scroll = memnew(HScrollBar);
|
||||
h_scroll->set_name("_h_scroll");
|
||||
|
|
|
@ -72,6 +72,7 @@ private:
|
|||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> panel_style;
|
||||
Ref<StyleBox> focus_style;
|
||||
} theme_cache;
|
||||
|
||||
void _cancel_drag();
|
||||
|
@ -79,6 +80,11 @@ private:
|
|||
bool _is_h_scroll_visible() const;
|
||||
bool _is_v_scroll_visible() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool _draw_focus_border = false;
|
||||
bool child_has_focus();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Size2 get_minimum_size() const override;
|
||||
|
||||
|
@ -125,6 +131,11 @@ public:
|
|||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void set_draw_focus_border(bool p_draw);
|
||||
bool get_draw_focus_border();
|
||||
#endif
|
||||
|
||||
ScrollContainer();
|
||||
};
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
|
|||
const Ref<StyleBoxFlat> button_pressed = make_flat_stylebox(style_pressed_color);
|
||||
const Ref<StyleBoxFlat> button_disabled = make_flat_stylebox(style_disabled_color);
|
||||
Ref<StyleBoxFlat> focus = make_flat_stylebox(style_focus_color, default_margin, default_margin, default_margin, default_margin, default_corner_radius, false, 2);
|
||||
// Make the focus outline appear to be flush with the buttons it's focusing.
|
||||
// Make the focus outline appear to be flush with the buttons it's focusing, so not draw on top of the content.
|
||||
focus->set_expand_margin_all(Math::round(2 * scale));
|
||||
|
||||
theme->set_stylebox(CoreStringName(normal), "Button", button_normal);
|
||||
|
@ -657,6 +657,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
|
|||
Ref<StyleBoxEmpty> empty;
|
||||
empty.instantiate();
|
||||
theme->set_stylebox(SceneStringName(panel), "ScrollContainer", empty);
|
||||
theme->set_stylebox(SNAME("focus"), "ScrollContainer", empty);
|
||||
|
||||
// Window
|
||||
|
||||
|
|
|
@ -61,6 +61,57 @@ TEST_CASE("[SceneTree][Control]") {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Control] Focus") {
|
||||
Control *ctrl = memnew(Control);
|
||||
SceneTree::get_singleton()->get_root()->add_child(ctrl);
|
||||
|
||||
SUBCASE("[SceneTree][Control] Default focus") {
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Can't grab focus with default focus mode") {
|
||||
ERR_PRINT_OFF
|
||||
ctrl->grab_focus();
|
||||
ERR_PRINT_ON
|
||||
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Can grab focus") {
|
||||
ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
ctrl->grab_focus();
|
||||
|
||||
CHECK(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Can release focus") {
|
||||
ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
ctrl->grab_focus();
|
||||
CHECK(ctrl->has_focus());
|
||||
|
||||
ctrl->release_focus();
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Only one can grab focus at the same time") {
|
||||
ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
ctrl->grab_focus();
|
||||
CHECK(ctrl->has_focus());
|
||||
|
||||
Control *other_ctrl = memnew(Control);
|
||||
SceneTree::get_singleton()->get_root()->add_child(other_ctrl);
|
||||
other_ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
other_ctrl->grab_focus();
|
||||
|
||||
CHECK(other_ctrl->has_focus());
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
|
||||
memdelete(other_ctrl);
|
||||
}
|
||||
|
||||
memdelete(ctrl);
|
||||
}
|
||||
|
||||
} // namespace TestControl
|
||||
|
||||
#endif // TEST_CONTROL_H
|
||||
|
|
Loading…
Reference in a new issue