mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-19 17:27:51 +03:00
LibGUI: Add UIDimension and UISize types
These types are used to represent size information for layouts. They can hold integer (int) values ≥0 or special values like shrink and fit.
This commit is contained in:
parent
e29314f837
commit
024305e742
Notes:
sideshowbarker
2024-07-17 09:55:37 +09:00
Author: https://github.com/frhun Commit: https://github.com/SerenityOS/serenity/commit/024305e742 Pull-request: https://github.com/SerenityOS/serenity/pull/14261 Reviewed-by: https://github.com/AtkinsSJ ✅ Reviewed-by: https://github.com/MacDue Reviewed-by: https://github.com/mjz19910
@ -75,8 +75,8 @@ void AbstractScrollableWidget::mousewheel_event(MouseEvent& event)
|
||||
void AbstractScrollableWidget::custom_layout()
|
||||
{
|
||||
auto inner_rect = frame_inner_rect_for_size(size());
|
||||
int height_wanted_by_horizontal_scrollbar = m_horizontal_scrollbar->is_visible() ? m_horizontal_scrollbar->min_height() : 0;
|
||||
int width_wanted_by_vertical_scrollbar = m_vertical_scrollbar->is_visible() ? m_vertical_scrollbar->min_width() : 0;
|
||||
int height_wanted_by_horizontal_scrollbar = m_horizontal_scrollbar->is_visible() ? int(m_horizontal_scrollbar->min_height()) : 0;
|
||||
int width_wanted_by_vertical_scrollbar = m_vertical_scrollbar->is_visible() ? int(m_vertical_scrollbar->min_width()) : 0;
|
||||
|
||||
m_vertical_scrollbar->set_relative_rect(
|
||||
inner_rect.right() + 1 - m_vertical_scrollbar->min_width(),
|
||||
|
290
Userland/Libraries/LibGUI/UIDimensions.h
Normal file
290
Userland/Libraries/LibGUI/UIDimensions.h
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Frhun <serenitystuff@frhun.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace GUI {
|
||||
|
||||
// The constants used for special values
|
||||
// Their order here, also defines their order among each other for min, max; operations, excluding Regular
|
||||
enum class SpecialDimension : int {
|
||||
Regular = 0, // only really useful for is_one_of
|
||||
Grow = -1,
|
||||
OpportunisticGrow = -2,
|
||||
Fit = -3,
|
||||
Shrink = -4,
|
||||
};
|
||||
|
||||
class UIDimension {
|
||||
friend constexpr auto AK::max<GUI::UIDimension>(GUI::UIDimension const&, GUI::UIDimension const&) -> GUI::UIDimension;
|
||||
friend constexpr auto AK::min<GUI::UIDimension>(GUI::UIDimension const&, GUI::UIDimension const&) -> GUI::UIDimension;
|
||||
|
||||
public:
|
||||
UIDimension() = delete;
|
||||
|
||||
UIDimension(int value)
|
||||
: m_value(value)
|
||||
{
|
||||
VERIFY(value >= 0);
|
||||
}
|
||||
|
||||
UIDimension(SpecialDimension special)
|
||||
: m_value(to_underlying(special))
|
||||
{
|
||||
}
|
||||
|
||||
// This is a temporary hack to get this compiling
|
||||
operator int() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_special_value() const
|
||||
{
|
||||
return m_value < 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_int() const
|
||||
{
|
||||
return m_value >= 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_shrink() const
|
||||
{
|
||||
return m_value == to_underlying(SpecialDimension::Shrink);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_grow() const
|
||||
{
|
||||
return m_value == to_underlying(SpecialDimension::Grow);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_opportunistic_grow() const
|
||||
{
|
||||
return m_value == to_underlying(SpecialDimension::OpportunisticGrow);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_fit() const
|
||||
{
|
||||
return m_value == to_underlying(SpecialDimension::Fit);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_one_of(std::initializer_list<SpecialDimension> valid_values) const
|
||||
{
|
||||
for (SpecialDimension v : valid_values) {
|
||||
if (m_value == to_underlying(v) || (v == SpecialDimension::Regular && is_int()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE constexpr bool is(SpecialDimension special_value) const
|
||||
{
|
||||
return m_value == to_underlying(special_value) || (special_value == SpecialDimension::Regular && is_int());
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
[[nodiscard]] bool is_one_of(Ts... valid_values) const
|
||||
{
|
||||
return (... || (is(forward<Ts>(valid_values))));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool operator==(UIDimension other) const
|
||||
{
|
||||
return m_value == other.m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline UIDimension must_sum_with(UIDimension other) const
|
||||
{
|
||||
VERIFY(is_int() && other.is_int());
|
||||
return UIDimension { m_value + other.m_value };
|
||||
}
|
||||
|
||||
inline void must_add(int to_add)
|
||||
{
|
||||
VERIFY(is_int());
|
||||
VERIFY(m_value >= -to_add);
|
||||
m_value += to_add;
|
||||
}
|
||||
|
||||
inline void add_if_int(int to_add)
|
||||
{
|
||||
if (is_int()) {
|
||||
m_value += to_add;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline ErrorOr<int> shrink_value() const
|
||||
{
|
||||
if (m_value >= 0)
|
||||
return m_value;
|
||||
if (m_value == to_underlying(SpecialDimension::Shrink))
|
||||
return 0;
|
||||
return Error::from_string_literal("value is neither shrink nor an integer ≥0");
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int as_int() const
|
||||
{
|
||||
VERIFY(is_int());
|
||||
return m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] AK::JsonValue as_json_value() const
|
||||
{
|
||||
if (is_int())
|
||||
return m_value;
|
||||
if (is_shrink())
|
||||
return "shrink";
|
||||
if (is_grow())
|
||||
return "grow";
|
||||
if (is_opportunistic_grow())
|
||||
return "opportunistic_grow";
|
||||
if (is_fit())
|
||||
return "fit";
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
operator AK::JsonValue() const
|
||||
{
|
||||
return this->as_json_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] static Optional<UIDimension> construct_from_json_value(AK::JsonValue const value)
|
||||
{
|
||||
if (value.is_string()) {
|
||||
String value_literal = value.as_string();
|
||||
if (value_literal == "shrink")
|
||||
return UIDimension { SpecialDimension::Shrink };
|
||||
else if (value_literal == "grow")
|
||||
return UIDimension { SpecialDimension::Grow };
|
||||
else if (value_literal == "opportunistic_grow")
|
||||
return UIDimension { SpecialDimension::OpportunisticGrow };
|
||||
else if (value_literal == "fit")
|
||||
return UIDimension { SpecialDimension::Fit };
|
||||
else
|
||||
return {};
|
||||
} else {
|
||||
int value_int = value.to_i32();
|
||||
if (value_int < 0)
|
||||
return {};
|
||||
return UIDimension(value_int);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Remove these following methods when the move to the new layout system is completed
|
||||
[[nodiscard]] inline bool operator==(int other) const
|
||||
{
|
||||
return m_value == other;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
|
||||
class UISize : public Gfx::Size<UIDimension> {
|
||||
|
||||
public:
|
||||
UISize() = delete;
|
||||
|
||||
UISize(int in_width, int in_height)
|
||||
: Gfx::Size<UIDimension>(in_width, in_height)
|
||||
{
|
||||
}
|
||||
|
||||
UISize(Gfx::IntSize size)
|
||||
: UISize(size.width(), size.height())
|
||||
{
|
||||
}
|
||||
|
||||
UISize(SpecialDimension special)
|
||||
: Gfx::Size<UIDimension>(UIDimension { special }, UIDimension { special })
|
||||
{
|
||||
}
|
||||
|
||||
UISize(UIDimension width, UIDimension height)
|
||||
: Gfx::Size<UIDimension>(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
inline UISize replace_component_if_matching_with(UIDimension to_match, UISize replacement)
|
||||
{
|
||||
if (width() == to_match)
|
||||
set_width(replacement.width());
|
||||
if (height() == to_match)
|
||||
set_height(replacement.height());
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool has_only_int_values() const
|
||||
{
|
||||
return width().is_int() && height().is_int();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool either_is(UIDimension to_match) const
|
||||
{
|
||||
return (width() == to_match || height() == to_match);
|
||||
}
|
||||
|
||||
operator Gfx::IntSize() const
|
||||
{
|
||||
return Gfx::IntSize(width().as_int(), height().as_int());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<>
|
||||
inline auto max<GUI::UIDimension>(GUI::UIDimension const& a, GUI::UIDimension const& b) -> GUI::UIDimension
|
||||
{
|
||||
if ((a.is_int() && b.is_int()) || (a.is_special_value() && b.is_special_value()))
|
||||
return a.m_value > b.m_value ? a : b;
|
||||
if (a.is_grow() || b.is_grow())
|
||||
return GUI::SpecialDimension::Grow;
|
||||
if (a.is_opportunistic_grow() || b.is_opportunistic_grow())
|
||||
return GUI::SpecialDimension::OpportunisticGrow;
|
||||
if (a.is_fit() || b.is_fit())
|
||||
return GUI::SpecialDimension::Fit;
|
||||
if (a.is_shrink())
|
||||
return b;
|
||||
if (b.is_shrink())
|
||||
return a;
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline auto min<GUI::UIDimension>(GUI::UIDimension const& a, GUI::UIDimension const& b) -> GUI::UIDimension
|
||||
{
|
||||
if ((a.is_int() && b.is_int()) || (a.is_special_value() && b.is_special_value()))
|
||||
return a.m_value < b.m_value ? a : b;
|
||||
if (a.is_shrink() || b.is_shrink())
|
||||
return GUI::SpecialDimension::Shrink;
|
||||
if (a.is_int())
|
||||
return a;
|
||||
if (b.is_int())
|
||||
return b;
|
||||
if (a.is_fit() || b.is_fit())
|
||||
return GUI::SpecialDimension::Fit;
|
||||
if (a.is_opportunistic_grow() || b.is_opportunistic_grow())
|
||||
return GUI::SpecialDimension::OpportunisticGrow;
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline auto clamp<GUI::UIDimension>(GUI::UIDimension const& input, GUI::UIDimension const& lower_bound, GUI::UIDimension const& upper_bound) -> GUI::UIDimension
|
||||
{
|
||||
return min(max(input, lower_bound), upper_bound);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user