LibGUI+Applications: Govern Splitter resizing by opportunistic growth
This patch replaces the concept of fixed resizees with opportunistic ones which use the new SpecialDimension::OpportunisticGrow UISize. This lets us simplify splitter resize code and take advantage of the layout system's automatic calculations for minimum size and expansion. Functionally the same as before, but fixes Splitter's unintended ability to grow window size.
This commit is contained in:
parent
6f2a304971
commit
a808cfa75c
Notes:
sideshowbarker
2024-07-17 11:30:05 +09:00
Author: https://github.com/thankyouverycool Commit: https://github.com/SerenityOS/serenity/commit/a808cfa75c Pull-request: https://github.com/SerenityOS/serenity/pull/14774 Issue: https://github.com/SerenityOS/serenity/issues/14554
5 changed files with 29 additions and 57 deletions
|
@ -43,11 +43,10 @@
|
||||||
|
|
||||||
@GUI::HorizontalSplitter {
|
@GUI::HorizontalSplitter {
|
||||||
name: "splitter"
|
name: "splitter"
|
||||||
first_resizee_minimum_size: 80
|
|
||||||
|
|
||||||
@GUI::TreeView {
|
@GUI::TreeView {
|
||||||
name: "tree_view"
|
name: "tree_view"
|
||||||
fixed_width: 175
|
preferred_width: 175
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@GUI::HorizontalSplitter {
|
@GUI::HorizontalSplitter {
|
||||||
fixed_resizee: "Second"
|
opportunistic_resizee: "First"
|
||||||
|
|
||||||
@GUI::Widget {
|
@GUI::Widget {
|
||||||
layout: @GUI::VerticalBoxLayout {}
|
layout: @GUI::VerticalBoxLayout {}
|
||||||
|
@ -217,7 +217,7 @@
|
||||||
|
|
||||||
@GUI::Widget {
|
@GUI::Widget {
|
||||||
name: "unicode_block_container"
|
name: "unicode_block_container"
|
||||||
fixed_width: 175
|
preferred_width: 175
|
||||||
layout: @GUI::VerticalBoxLayout {}
|
layout: @GUI::VerticalBoxLayout {}
|
||||||
|
|
||||||
@GUI::TextBox {
|
@GUI::TextBox {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
@GUI::TabWidget {
|
@GUI::TabWidget {
|
||||||
name: "tab_widget"
|
name: "tab_widget"
|
||||||
fixed_width: 200
|
preferred_width: 200
|
||||||
container_margins: [6]
|
container_margins: [6]
|
||||||
|
|
||||||
@GUI::TreeView {
|
@GUI::TreeView {
|
||||||
|
|
|
@ -20,11 +20,9 @@ namespace GUI {
|
||||||
Splitter::Splitter(Orientation orientation)
|
Splitter::Splitter(Orientation orientation)
|
||||||
: m_orientation(orientation)
|
: m_orientation(orientation)
|
||||||
{
|
{
|
||||||
REGISTER_INT_PROPERTY("first_resizee_minimum_size", first_resizee_minimum_size, set_first_resizee_minimum_size);
|
REGISTER_ENUM_PROPERTY("opportunistic_resizee", opportunisitic_resizee, set_opportunisitic_resizee, OpportunisticResizee,
|
||||||
REGISTER_INT_PROPERTY("second_resizee_minimum_size", second_resizee_minimum_size, set_second_resizee_minimum_size);
|
{ OpportunisticResizee::First, "First" },
|
||||||
REGISTER_ENUM_PROPERTY("fixed_resizee", fixed_resizee, set_fixed_resizee, FixedResizee,
|
{ OpportunisticResizee::Second, "Second" });
|
||||||
{ FixedResizee::First, "First" },
|
|
||||||
{ FixedResizee::Second, "Second" });
|
|
||||||
|
|
||||||
set_background_role(ColorRole::Button);
|
set_background_role(ColorRole::Button);
|
||||||
set_layout<BoxLayout>(orientation);
|
set_layout<BoxLayout>(orientation);
|
||||||
|
@ -197,7 +195,6 @@ void Splitter::recompute_grabbables()
|
||||||
set_hovered_grabbable(&m_grabbables[old_hovered_index.value()]);
|
set_hovered_grabbable(&m_grabbables[old_hovered_index.value()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Respect the child widgets min and max sizes
|
|
||||||
void Splitter::mousemove_event(MouseEvent& event)
|
void Splitter::mousemove_event(MouseEvent& event)
|
||||||
{
|
{
|
||||||
auto* grabbable = grabbable_at(event.position());
|
auto* grabbable = grabbable_at(event.position());
|
||||||
|
@ -206,50 +203,33 @@ void Splitter::mousemove_event(MouseEvent& event)
|
||||||
override_cursor(grabbable != nullptr);
|
override_cursor(grabbable != nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto delta = event.position() - m_resize_origin;
|
|
||||||
if (!m_first_resizee || !m_second_resizee) {
|
if (!m_first_resizee || !m_second_resizee) {
|
||||||
// One or both of the resizees were deleted during an ongoing resize, screw this.
|
|
||||||
m_resizing = false;
|
m_resizing = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto new_first_resizee_size = m_first_resizee_start_size;
|
|
||||||
auto new_second_resizee_size = m_second_resizee_start_size;
|
|
||||||
|
|
||||||
new_first_resizee_size.set_primary_size_for_orientation(m_orientation, new_first_resizee_size.primary_size_for_orientation(m_orientation) + delta.primary_offset_for_orientation(m_orientation));
|
auto delta = (event.position() - m_resize_origin).primary_offset_for_orientation(m_orientation);
|
||||||
new_second_resizee_size.set_primary_size_for_orientation(m_orientation, new_second_resizee_size.primary_size_for_orientation(m_orientation) - delta.primary_offset_for_orientation(m_orientation));
|
auto new_first_resizee_size = clamp(m_first_resizee_start_size.primary_size_for_orientation(m_orientation) + delta, 0, m_first_resizee_max_size);
|
||||||
|
auto new_second_resizee_size = clamp(m_second_resizee_start_size.primary_size_for_orientation(m_orientation) - delta, 0, m_second_resizee_max_size);
|
||||||
if (new_first_resizee_size.primary_size_for_orientation(m_orientation) < m_first_resizee_minimum_size) {
|
|
||||||
int correction = m_first_resizee_minimum_size - new_first_resizee_size.primary_size_for_orientation(m_orientation);
|
|
||||||
new_first_resizee_size.set_primary_size_for_orientation(m_orientation, new_first_resizee_size.primary_size_for_orientation(m_orientation) + correction);
|
|
||||||
new_second_resizee_size.set_primary_size_for_orientation(m_orientation, new_second_resizee_size.primary_size_for_orientation(m_orientation) - correction);
|
|
||||||
}
|
|
||||||
if (new_second_resizee_size.primary_size_for_orientation(m_orientation) < m_second_resizee_minimum_size) {
|
|
||||||
int correction = m_second_resizee_minimum_size - new_second_resizee_size.primary_size_for_orientation(m_orientation);
|
|
||||||
new_second_resizee_size.set_primary_size_for_orientation(m_orientation, new_second_resizee_size.primary_size_for_orientation(m_orientation) + correction);
|
|
||||||
new_first_resizee_size.set_primary_size_for_orientation(m_orientation, new_first_resizee_size.primary_size_for_orientation(m_orientation) - correction);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_orientation == Orientation::Horizontal) {
|
if (m_orientation == Orientation::Horizontal) {
|
||||||
if (fixed_resizee() == FixedResizee::First) {
|
if (opportunisitic_resizee() == OpportunisticResizee::First) {
|
||||||
m_first_resizee->set_fixed_width(new_first_resizee_size.width());
|
m_first_resizee->set_preferred_width(SpecialDimension::OpportunisticGrow);
|
||||||
m_second_resizee->set_min_width(SpecialDimension::Shrink);
|
m_second_resizee->set_preferred_width(new_second_resizee_size);
|
||||||
m_second_resizee->set_max_width(SpecialDimension::Grow);
|
|
||||||
} else {
|
} else {
|
||||||
VERIFY(fixed_resizee() == FixedResizee::Second);
|
VERIFY(opportunisitic_resizee() == OpportunisticResizee::Second);
|
||||||
m_second_resizee->set_fixed_width(new_second_resizee_size.width());
|
m_second_resizee->set_preferred_width(SpecialDimension::OpportunisticGrow);
|
||||||
m_first_resizee->set_min_width(SpecialDimension::Shrink);
|
m_first_resizee->set_preferred_width(new_first_resizee_size);
|
||||||
m_first_resizee->set_max_width(SpecialDimension::Grow);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (fixed_resizee() == FixedResizee::First) {
|
if (opportunisitic_resizee() == OpportunisticResizee::First) {
|
||||||
m_first_resizee->set_fixed_height(new_first_resizee_size.height());
|
m_first_resizee->set_preferred_height(SpecialDimension::OpportunisticGrow);
|
||||||
m_second_resizee->set_min_height(SpecialDimension::Shrink);
|
m_second_resizee->set_preferred_height(new_second_resizee_size);
|
||||||
m_second_resizee->set_max_height(SpecialDimension::Grow);
|
|
||||||
} else {
|
} else {
|
||||||
VERIFY(fixed_resizee() == FixedResizee::Second);
|
VERIFY(opportunisitic_resizee() == OpportunisticResizee::Second);
|
||||||
m_second_resizee->set_fixed_height(new_second_resizee_size.height());
|
m_second_resizee->set_preferred_height(SpecialDimension::OpportunisticGrow);
|
||||||
m_first_resizee->set_min_height(SpecialDimension::Shrink);
|
m_first_resizee->set_preferred_height(new_first_resizee_size);
|
||||||
m_first_resizee->set_max_height(SpecialDimension::Grow);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,13 +252,13 @@ void Splitter::custom_layout()
|
||||||
if (m_last_child_count > child_widgets.size()) {
|
if (m_last_child_count > child_widgets.size()) {
|
||||||
bool has_child_to_fill_space = false;
|
bool has_child_to_fill_space = false;
|
||||||
for (auto& child : child_widgets) {
|
for (auto& child : child_widgets) {
|
||||||
if (child.max_size().primary_size_for_orientation(m_orientation).is_grow()) {
|
if (child.preferred_size().primary_size_for_orientation(m_orientation).is_opportunistic_grow()) {
|
||||||
has_child_to_fill_space = true;
|
has_child_to_fill_space = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!has_child_to_fill_space)
|
if (!has_child_to_fill_space)
|
||||||
child_widgets.last().set_preferred_size(SpecialDimension::Grow);
|
child_widgets.last().set_preferred_size(SpecialDimension::OpportunisticGrow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,18 +15,13 @@ class Splitter : public Widget {
|
||||||
C_OBJECT(Splitter);
|
C_OBJECT(Splitter);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class FixedResizee {
|
enum class OpportunisticResizee {
|
||||||
First,
|
First,
|
||||||
Second
|
Second
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~Splitter() override = default;
|
virtual ~Splitter() override = default;
|
||||||
|
|
||||||
int first_resizee_minimum_size() { return m_first_resizee_minimum_size; }
|
|
||||||
void set_first_resizee_minimum_size(int minimum_size) { m_first_resizee_minimum_size = minimum_size; }
|
|
||||||
int second_resizee_minimum_size() { return m_second_resizee_minimum_size; }
|
|
||||||
void set_second_resizee_minimum_size(int minimum_size) { m_second_resizee_minimum_size = minimum_size; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit Splitter(Gfx::Orientation);
|
explicit Splitter(Gfx::Orientation);
|
||||||
|
|
||||||
|
@ -40,8 +35,8 @@ protected:
|
||||||
virtual void did_layout() override;
|
virtual void did_layout() override;
|
||||||
virtual void custom_layout() override;
|
virtual void custom_layout() override;
|
||||||
|
|
||||||
FixedResizee fixed_resizee() const { return m_fixed_resizee; }
|
OpportunisticResizee opportunisitic_resizee() const { return m_opportunistic_resizee; }
|
||||||
void set_fixed_resizee(FixedResizee resizee) { m_fixed_resizee = resizee; }
|
void set_opportunisitic_resizee(OpportunisticResizee resizee) { m_opportunistic_resizee = resizee; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void override_cursor(bool do_override);
|
void override_cursor(bool do_override);
|
||||||
|
@ -55,9 +50,7 @@ private:
|
||||||
WeakPtr<Widget> m_second_resizee;
|
WeakPtr<Widget> m_second_resizee;
|
||||||
Gfx::IntSize m_first_resizee_start_size;
|
Gfx::IntSize m_first_resizee_start_size;
|
||||||
Gfx::IntSize m_second_resizee_start_size;
|
Gfx::IntSize m_second_resizee_start_size;
|
||||||
int m_first_resizee_minimum_size { 0 };
|
OpportunisticResizee m_opportunistic_resizee { OpportunisticResizee::Second };
|
||||||
int m_second_resizee_minimum_size { 0 };
|
|
||||||
FixedResizee m_fixed_resizee { FixedResizee::First };
|
|
||||||
size_t m_last_child_count { 0 };
|
size_t m_last_child_count { 0 };
|
||||||
int m_first_resizee_max_size { 0 };
|
int m_first_resizee_max_size { 0 };
|
||||||
int m_second_resizee_max_size { 0 };
|
int m_second_resizee_max_size { 0 };
|
||||||
|
|
Loading…
Add table
Reference in a new issue