Formatting
publish / publish (push) Successful in 15m37s

This commit is contained in:
khodowany
2026-09-14 23:56:06 -05:00
parent 86cf029ad7
commit bb96aaf95d
30 changed files with 1030 additions and 950 deletions
+4
View File
@@ -91,6 +91,10 @@ jobs:
restore-keys: conan-${{ runner.os }}- restore-keys: conan-${{ runner.os }}-
- name: Configure native release - name: Configure native release
run: python3 modulus.py configure --target x86_64_gcc12_release run: python3 modulus.py configure --target x86_64_gcc12_release
- name: Check code style
run: |
python3 -m pip install --no-cache-dir -r build/build_tools/docker/format-requirements.txt
python3 modulus.py format --check
- name: Build native release - name: Build native release
run: python3 modulus.py build --target x86_64_gcc12_release run: python3 modulus.py build --target x86_64_gcc12_release
- name: Test native release - name: Test native release
+2
View File
@@ -6,6 +6,8 @@ Luma uses Modulus for shared build tooling, Boost.JSON, and raylib integration.
## Build ## Build
Code style is managed by Modulus: run `./modulus.py format` or `./modulus.py format --check`. The generated Neovim integration uses the same pinned container formatter for C++ and Python, including existing format-on-save toggles. Configure first to prepare tooling; customize `tooling.editor.clang_format` and `tooling.format.exclude` in `modulus.json`, not generated editor files. See the shared Modulus README for the compact layout and no-alias policy.
Configure, build, and test the default target: Configure, build, and test the default target:
```sh ```sh
-8
View File
@@ -40,15 +40,12 @@ class luma_conan(conan.ConanFile):
"tests/*.cpp", "tests/*.cpp",
"tests/*.hpp", "tests/*.hpp",
) )
def config_options(self): def config_options(self):
if self.settings.os == "Windows": if self.settings.os == "Windows":
del self.options.fPIC del self.options.fPIC
def configure(self): def configure(self):
if self.options.shared: if self.options.shared:
del self.options.fPIC del self.options.fPIC
def requirements(self): def requirements(self):
self.requires( self.requires(
"modulus/latest", "modulus/latest",
@@ -58,7 +55,6 @@ class luma_conan(conan.ConanFile):
"with_raylib": True, "with_raylib": True,
}, },
) )
def generate(self): def generate(self):
deps = conan.tools.cmake.CMakeDeps(self) deps = conan.tools.cmake.CMakeDeps(self)
deps.generate() deps.generate()
@@ -68,14 +64,12 @@ class luma_conan(conan.ConanFile):
toolchain.variables["BUILD_SHARED_LIBS"] = bool(self.options.shared) toolchain.variables["BUILD_SHARED_LIBS"] = bool(self.options.shared)
toolchain.variables["BUILD_TESTING"] = bool(self.options.with_tests) toolchain.variables["BUILD_TESTING"] = bool(self.options.with_tests)
toolchain.generate() toolchain.generate()
def build(self): def build(self):
cmake = conan.tools.cmake.CMake(self) cmake = conan.tools.cmake.CMake(self)
cmake.configure() cmake.configure()
cmake.build() cmake.build()
if self.options.with_tests and conan.tools.build.can_run(self): if self.options.with_tests and conan.tools.build.can_run(self):
cmake.test(target="check") cmake.test(target="check")
def package_id(self): def package_id(self):
self.info.options.rm_safe("with_tests") self.info.options.rm_safe("with_tests")
for requirement in ( for requirement in (
@@ -85,11 +79,9 @@ class luma_conan(conan.ConanFile):
): ):
if requirement in self.info.requires.pkg_names: if requirement in self.info.requires.pkg_names:
self.info.requires.remove(requirement) self.info.requires.remove(requirement)
def package(self): def package(self):
cmake = conan.tools.cmake.CMake(self) cmake = conan.tools.cmake.CMake(self)
cmake.install() cmake.install()
def package_info(self): def package_info(self):
self.cpp_info.set_property( self.cpp_info.set_property(
"cmake_file_name", "cmake_file_name",
+1
View File
@@ -7,6 +7,7 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace luma { namespace luma {
enum class easing { enum class easing {
+8 -6
View File
@@ -23,7 +23,9 @@
namespace luma { namespace luma {
namespace detail { namespace detail {
struct application_core; struct application_core;
} }
struct frame_stats { struct frame_stats {
@@ -34,11 +36,11 @@ struct frame_stats {
class application { class application {
public: public:
explicit application(luma::application_config config = {}); explicit application(luma::application_config config = {});
application(luma::application const&) = delete; application(luma::application const &) = delete;
application(luma::application&&) noexcept; application(luma::application &&) noexcept;
~application(); ~application();
auto operator=(luma::application const&) -> luma::application& = delete; auto operator=(luma::application const &) -> luma::application & = delete;
auto operator=(luma::application&&) noexcept -> luma::application&; auto operator=(luma::application &&) noexcept -> luma::application &;
auto dispatch(std::function<std::expected<void, modulus::error>()> task) -> std::expected<void, modulus::error>; auto dispatch(std::function<std::expected<void, modulus::error>()> task) -> std::expected<void, modulus::error>;
auto register_route(std::string name, std::function<std::expected<std::unique_ptr<luma::control>, modulus::error>()> factory) -> std::expected<void, modulus::error>; auto register_route(std::string name, std::function<std::expected<std::unique_ptr<luma::control>, modulus::error>()> factory) -> std::expected<void, modulus::error>;
auto replace_route(std::string name) -> std::expected<void, modulus::error>; auto replace_route(std::string name) -> std::expected<void, modulus::error>;
@@ -58,7 +60,7 @@ public:
auto set_root(std::expected<std::unique_ptr<luma::control>, modulus::error> root) -> std::expected<void, modulus::error>; auto set_root(std::expected<std::unique_ptr<luma::control>, modulus::error> root) -> std::expected<void, modulus::error>;
auto set_root(std::unique_ptr<luma::control> root) -> std::expected<void, modulus::error>; auto set_root(std::unique_ptr<luma::control> root) -> std::expected<void, modulus::error>;
template <typename CONTROL> template <typename CONTROL>
requires (std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>) requires(std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>)
auto set_root(std::unique_ptr<CONTROL> root) -> std::expected<void, modulus::error>; auto set_root(std::unique_ptr<CONTROL> root) -> std::expected<void, modulus::error>;
auto show_dialog(luma::dialog_options options) -> std::expected<void, modulus::error>; auto show_dialog(luma::dialog_options options) -> std::expected<void, modulus::error>;
auto close_dialog(luma::dialog_result result) -> std::expected<void, modulus::error>; auto close_dialog(luma::dialog_result result) -> std::expected<void, modulus::error>;
@@ -73,7 +75,7 @@ private:
}; };
template <typename CONTROL> template <typename CONTROL>
requires (std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>) requires(std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>)
auto application::set_root(std::unique_ptr<CONTROL> root) -> std::expected<void, modulus::error> { auto application::set_root(std::unique_ptr<CONTROL> root) -> std::expected<void, modulus::error> {
return set_root(std::unique_ptr<luma::control>{std::move(root)}); return set_root(std::unique_ptr<luma::control>{std::move(root)});
} }
+5 -4
View File
@@ -11,22 +11,23 @@
#include <luma/render.hpp> #include <luma/render.hpp>
namespace luma { namespace luma {
class renderer_resources { class renderer_resources {
public: public:
virtual ~renderer_resources() = default; virtual ~renderer_resources() = default;
virtual auto load_texture(std::string const& path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> = 0; virtual auto load_texture(std::string const &path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> = 0;
}; };
class asset_cache { class asset_cache {
public: public:
explicit asset_cache(luma::renderer_resources& resources); explicit asset_cache(luma::renderer_resources &resources);
auto texture(std::string const& path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error>; auto texture(std::string const &path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error>;
auto clear() -> void; auto clear() -> void;
private: private:
struct in_flight_load; struct in_flight_load;
luma::renderer_resources* resources_{}; luma::renderer_resources *resources_{};
mutable std::mutex mutex_{}; mutable std::mutex mutex_{};
std::uint64_t generation_{}; std::uint64_t generation_{};
std::unordered_map<std::string, std::shared_ptr<luma::texture>> textures_{}; std::unordered_map<std::string, std::shared_ptr<luma::texture>> textures_{};
+11 -10
View File
@@ -10,6 +10,7 @@
#include <luma/theme.hpp> #include <luma/theme.hpp>
namespace luma { namespace luma {
struct display_config { struct display_config {
@@ -31,15 +32,15 @@ struct application_config {
luma::theme theme{luma::default_theme()}; luma::theme theme{luma::default_theme()};
}; };
auto validate(luma::display_config const& config) -> std::expected<void, modulus::error>; auto validate(luma::display_config const &config) -> std::expected<void, modulus::error>;
auto validate(luma::touch_config const& config) -> std::expected<void, modulus::error>; auto validate(luma::touch_config const &config) -> std::expected<void, modulus::error>;
auto validate(luma::application_config const& config) -> std::expected<void, modulus::error>; auto validate(luma::application_config const &config) -> std::expected<void, modulus::error>;
auto parse_display_config(boost::json::value const& value) -> std::expected<luma::display_config, modulus::error>; auto parse_display_config(boost::json::value const &value) -> std::expected<luma::display_config, modulus::error>;
auto parse_touch_config(boost::json::value const& value) -> std::expected<luma::touch_config, modulus::error>; auto parse_touch_config(boost::json::value const &value) -> std::expected<luma::touch_config, modulus::error>;
auto parse_application_config(boost::json::value const& value) -> std::expected<luma::application_config, modulus::error>; auto parse_application_config(boost::json::value const &value) -> std::expected<luma::application_config, modulus::error>;
auto load_application_config(std::string const& path) -> std::expected<luma::application_config, modulus::error>; auto load_application_config(std::string const &path) -> std::expected<luma::application_config, modulus::error>;
auto to_json(luma::display_config const& config) -> boost::json::value; auto to_json(luma::display_config const &config) -> boost::json::value;
auto to_json(luma::touch_config const& config) -> boost::json::value; auto to_json(luma::touch_config const &config) -> boost::json::value;
auto to_json(luma::application_config const& config) -> boost::json::value; auto to_json(luma::application_config const &config) -> boost::json::value;
} }
+6 -3
View File
@@ -6,19 +6,22 @@
#include <luma/geometry.hpp> #include <luma/geometry.hpp>
namespace luma { namespace luma {
class draw_context; class draw_context;
struct theme; struct theme;
struct touch_event; struct touch_event;
class control { class control {
public: public:
virtual ~control(); virtual ~control();
virtual auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size = 0; virtual auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size = 0;
virtual auto arrange(luma::rect bounds) -> void; virtual auto arrange(luma::rect bounds) -> void;
virtual auto draw(luma::draw_context& context, luma::theme const& theme) const -> void = 0; virtual auto draw(luma::draw_context &context, luma::theme const &theme) const -> void = 0;
virtual auto handle_touch(luma::touch_event const& event) -> std::bool_t; virtual auto handle_touch(luma::touch_event const &event) -> std::bool_t;
virtual auto invalidate_layout() -> void; virtual auto invalidate_layout() -> void;
virtual auto bind_layout_invalidator(std::function<void()> invalidator) -> void; virtual auto bind_layout_invalidator(std::function<void()> invalidator) -> void;
virtual auto focusable() const -> std::bool_t; virtual auto focusable() const -> std::bool_t;
+7 -9
View File
@@ -76,7 +76,6 @@ public:
std::weak_ptr<void> lifetime{}; std::weak_ptr<void> lifetime{};
std::function<void()> callback{}; std::function<void()> callback{};
}; };
text_input_state(luma::text_field_options options, std::function<void(std::string)> on_change); text_input_state(luma::text_field_options options, std::function<void(std::string)> on_change);
auto snapshot() const -> luma::text_field_options; auto snapshot() const -> luma::text_field_options;
auto text() const -> std::string; auto text() const -> std::string;
@@ -101,7 +100,7 @@ auto numeric_keyboard() -> luma::keyboard_layout;
auto make_text_input(luma::text_field_options options, std::function<void(std::string)> on_change) -> std::expected<std::shared_ptr<luma::text_input_state>, modulus::error>; auto make_text_input(luma::text_field_options options, std::function<void(std::string)> on_change) -> std::expected<std::shared_ptr<luma::text_input_state>, modulus::error>;
template <typename... CONTROLS> template <typename... CONTROLS>
auto children(CONTROLS&&... controls) -> std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error>; auto children(CONTROLS &&...controls) -> std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error>;
auto label(std::string text) -> std::expected<std::unique_ptr<luma::control>, modulus::error>; auto label(std::string text) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
auto button(std::string text, std::function<void()> on_click) -> std::expected<std::unique_ptr<luma::control>, modulus::error>; auto button(std::string text, std::function<void()> on_click) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
@@ -123,7 +122,7 @@ auto stack(std::vector<std::unique_ptr<luma::control>> children) -> std::expecte
auto scroll_view(std::expected<std::unique_ptr<luma::control>, modulus::error> child, std::bool_t vertical = true, std::bool_t horizontal = false) -> std::expected<std::unique_ptr<luma::control>, modulus::error>; auto scroll_view(std::expected<std::unique_ptr<luma::control>, modulus::error> child, std::bool_t vertical = true, std::bool_t horizontal = false) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
auto scroll_view(std::unique_ptr<luma::control> child, std::bool_t vertical = true, std::bool_t horizontal = false) -> std::expected<std::unique_ptr<luma::control>, modulus::error>; auto scroll_view(std::unique_ptr<luma::control> child, std::bool_t vertical = true, std::bool_t horizontal = false) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
template <typename CONTROL> template <typename CONTROL>
requires (std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>) requires(std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>)
auto scroll_view(std::unique_ptr<CONTROL> child, std::bool_t vertical = true, std::bool_t horizontal = false) -> std::expected<std::unique_ptr<luma::control>, modulus::error>; auto scroll_view(std::unique_ptr<CONTROL> child, std::bool_t vertical = true, std::bool_t horizontal = false) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
auto list(std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> children, std::float32_t gap = 4.0F) -> std::expected<std::unique_ptr<luma::control>, modulus::error>; auto list(std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> children, std::float32_t gap = 4.0F) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
auto list(std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap = 4.0F) -> std::expected<std::unique_ptr<luma::control>, modulus::error>; auto list(std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap = 4.0F) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
@@ -153,17 +152,16 @@ auto normalize_child(std::expected<std::unique_ptr<CONTROL>, modulus::error> chi
} }
template <typename CONTROL> template <typename CONTROL>
requires (std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>) requires(std::is_base_of_v<luma::control, CONTROL> && !std::is_same_v<luma::control, CONTROL>)
auto luma::scroll_view(std::unique_ptr<CONTROL> child, std::bool_t vertical, std::bool_t horizontal) -> std::expected<std::unique_ptr<luma::control>, modulus::error> { auto luma::scroll_view(std::unique_ptr<CONTROL> child, std::bool_t vertical, std::bool_t horizontal) -> std::expected<std::unique_ptr<luma::control>, modulus::error> {
return luma::scroll_view(std::unique_ptr<luma::control>{std::move(child)}, vertical, horizontal); return luma::scroll_view(std::unique_ptr<luma::control>{std::move(child)}, vertical, horizontal);
} }
template <typename... CONTROLS> template <typename... CONTROLS>
auto luma::children(CONTROLS&&... controls) -> std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> { auto luma::children(CONTROLS &&...controls) -> std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> {
auto result = std::vector<std::unique_ptr<luma::control>>{}; auto result = std::vector<std::unique_ptr<luma::control>>{};
result.reserve(sizeof...(controls)); result.reserve(sizeof...(controls));
auto append = [&result](auto &&input) -> std::expected<void, modulus::error> {
auto append = [&result](auto&& input) -> std::expected<void, modulus::error> {
auto child = luma::detail::normalize_child(std::forward<decltype(input)>(input)); auto child = luma::detail::normalize_child(std::forward<decltype(input)>(input));
if (!child) { if (!child) {
return modulus::fail<void>(child.error()); return modulus::fail<void>(child.error());
@@ -171,13 +169,13 @@ auto luma::children(CONTROLS&&... controls) -> std::expected<std::vector<std::un
result.push_back(std::move(*child)); result.push_back(std::move(*child));
return {}; return {};
}; };
auto status = std::expected<void, modulus::error>{}; auto status = std::expected<void, modulus::error>{};
([&] { ([&] {
if (status) { if (status) {
status = append(std::forward<CONTROLS>(controls)); status = append(std::forward<CONTROLS>(controls));
} }
}(), ...); }(),
...);
if (!status) { if (!status) {
return modulus::fail<std::vector<std::unique_ptr<luma::control>>>(status.error()); return modulus::fail<std::vector<std::unique_ptr<luma::control>>>(status.error());
} }
+1
View File
@@ -7,6 +7,7 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace luma::detail { namespace luma::detail {
constexpr std::int32_t error_invalid_argument{1}; constexpr std::int32_t error_invalid_argument{1};
+1
View File
@@ -5,6 +5,7 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace luma { namespace luma {
struct point { struct point {
+1
View File
@@ -6,6 +6,7 @@
#include <luma/geometry.hpp> #include <luma/geometry.hpp>
namespace luma { namespace luma {
enum class touch_phase { enum class touch_phase {
+4 -3
View File
@@ -14,6 +14,7 @@
#include <luma/control.hpp> #include <luma/control.hpp>
namespace luma { namespace luma {
enum class dialog_result { enum class dialog_result {
@@ -41,10 +42,10 @@ public:
auto close_dialog(luma::dialog_result result) -> std::expected<void, modulus::error>; auto close_dialog(luma::dialog_result result) -> std::expected<void, modulus::error>;
auto show_toast(luma::toast toast) -> std::expected<void, modulus::error>; auto show_toast(luma::toast toast) -> std::expected<void, modulus::error>;
auto tick(std::chrono::steady_clock::duration delta) -> std::bool_t; auto tick(std::chrono::steady_clock::duration delta) -> std::bool_t;
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto arrange(luma::rect bounds) -> void override; auto arrange(luma::rect bounds) -> void override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto invalidate_layout() -> void override; auto invalidate_layout() -> void override;
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override; auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
private: private:
+4 -3
View File
@@ -8,6 +8,7 @@
#include <luma/geometry.hpp> #include <luma/geometry.hpp>
#include <luma/theme.hpp> #include <luma/theme.hpp>
namespace luma { namespace luma {
struct texture_info { struct texture_info {
@@ -29,11 +30,11 @@ public:
virtual auto stroke_line(luma::point from, luma::point to, std::float32_t thickness, luma::color color) -> void = 0; virtual auto stroke_line(luma::point from, luma::point to, std::float32_t thickness, luma::color color) -> void = 0;
virtual auto fill_ellipse(luma::rect bounds, luma::color color) -> void = 0; virtual auto fill_ellipse(luma::rect bounds, luma::color color) -> void = 0;
virtual auto stroke_ellipse(luma::rect bounds, std::float32_t thickness, luma::color color) -> void = 0; virtual auto stroke_ellipse(luma::rect bounds, std::float32_t thickness, luma::color color) -> void = 0;
virtual auto draw_text(std::string_view text, luma::point origin, luma::text_style const& style, luma::color color) -> void = 0; virtual auto draw_text(std::string_view text, luma::point origin, luma::text_style const &style, luma::color color) -> void = 0;
virtual auto draw_texture(luma::texture const& texture, luma::rect source, luma::rect destination, luma::color tint) -> void = 0; virtual auto draw_texture(luma::texture const &texture, luma::rect source, luma::rect destination, luma::color tint) -> void = 0;
virtual auto push_clip(luma::rect bounds) -> void = 0; virtual auto push_clip(luma::rect bounds) -> void = 0;
virtual auto pop_clip() -> void = 0; virtual auto pop_clip() -> void = 0;
virtual auto measure_text(std::string_view text, luma::text_style const& style) const -> luma::size = 0; virtual auto measure_text(std::string_view text, luma::text_style const &style) const -> luma::size = 0;
}; };
} }
+6 -5
View File
@@ -11,19 +11,20 @@
#include <luma/control.hpp> #include <luma/control.hpp>
namespace luma { namespace luma {
class router { class router {
public: public:
auto register_route(std::string name, std::function<std::expected<std::unique_ptr<luma::control>, modulus::error>()> factory) -> std::expected<void, modulus::error>; auto register_route(std::string name, std::function<std::expected<std::unique_ptr<luma::control>, modulus::error>()> factory) -> std::expected<void, modulus::error>;
auto set_retire_handler(std::function<void(std::unique_ptr<luma::control>)> handler) -> void; auto set_retire_handler(std::function<void(std::unique_ptr<luma::control>)> handler) -> void;
auto replace(std::string const& name) -> std::expected<void, modulus::error>; auto replace(std::string const &name) -> std::expected<void, modulus::error>;
auto push(std::string const& name) -> std::expected<void, modulus::error>; auto push(std::string const &name) -> std::expected<void, modulus::error>;
auto pop() -> std::expected<void, modulus::error>; auto pop() -> std::expected<void, modulus::error>;
auto clear_stack() -> void; auto clear_stack() -> void;
auto current() -> luma::control*; auto current() -> luma::control *;
auto current() const -> luma::control const*; auto current() const -> luma::control const *;
auto current_route() const -> std::string const&; auto current_route() const -> std::string const &;
private: private:
struct entry { struct entry {
std::string name{}; std::string name{};
+8 -7
View File
@@ -8,6 +8,7 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace luma { namespace luma {
struct color { struct color {
@@ -38,12 +39,12 @@ struct theme {
}; };
auto default_theme() -> luma::theme; auto default_theme() -> luma::theme;
auto validate(luma::text_style const& style) -> std::expected<void, modulus::error>; auto validate(luma::text_style const &style) -> std::expected<void, modulus::error>;
auto validate(luma::theme const& theme) -> std::expected<void, modulus::error>; auto validate(luma::theme const &theme) -> std::expected<void, modulus::error>;
auto parse_color(boost::json::value const& value) -> std::expected<luma::color, modulus::error>; auto parse_color(boost::json::value const &value) -> std::expected<luma::color, modulus::error>;
auto parse_theme(boost::json::value const& value) -> std::expected<luma::theme, modulus::error>; auto parse_theme(boost::json::value const &value) -> std::expected<luma::theme, modulus::error>;
auto to_json(luma::color const& color) -> boost::json::value; auto to_json(luma::color const &color) -> boost::json::value;
auto to_json(luma::text_style const& style) -> boost::json::value; auto to_json(luma::text_style const &style) -> boost::json::value;
auto to_json(luma::theme const& theme) -> boost::json::value; auto to_json(luma::theme const &theme) -> boost::json::value;
} }
+12 -11
View File
@@ -13,6 +13,7 @@
#include <luma/control.hpp> #include <luma/control.hpp>
#include <luma/geometry.hpp> #include <luma/geometry.hpp>
namespace luma { namespace luma {
class window_manager; class window_manager;
@@ -27,17 +28,17 @@ struct window_options {
class window { class window {
public: public:
window(luma::window const&) = delete; window(luma::window const &) = delete;
window(luma::window&&) noexcept = default; window(luma::window &&) noexcept = default;
auto operator=(luma::window const&) -> luma::window& = delete; auto operator=(luma::window const &) -> luma::window & = delete;
auto operator=(luma::window&&) noexcept -> luma::window& = default; auto operator=(luma::window &&) noexcept -> luma::window & = default;
auto id() const -> std::uint64_t; auto id() const -> std::uint64_t;
auto options() const -> luma::window_options const&; auto options() const -> luma::window_options const &;
auto set_bounds(luma::rect bounds) -> std::expected<void, modulus::error>; auto set_bounds(luma::rect bounds) -> std::expected<void, modulus::error>;
auto set_visible(std::bool_t value) -> void; auto set_visible(std::bool_t value) -> void;
auto visible() const -> std::bool_t; auto visible() const -> std::bool_t;
auto content() -> luma::control*; auto content() -> luma::control *;
auto content() const -> luma::control const*; auto content() const -> luma::control const *;
private: private:
friend class luma::window_manager; friend class luma::window_manager;
window(std::uint64_t id, luma::window_options options, std::unique_ptr<luma::control> content); window(std::uint64_t id, luma::window_options options, std::unique_ptr<luma::control> content);
@@ -53,12 +54,12 @@ public:
auto bring_to_front(std::uint64_t id) -> std::expected<void, modulus::error>; auto bring_to_front(std::uint64_t id) -> std::expected<void, modulus::error>;
auto set_bounds(std::uint64_t id, luma::rect bounds) -> std::expected<void, modulus::error>; auto set_bounds(std::uint64_t id, luma::rect bounds) -> std::expected<void, modulus::error>;
auto set_visible(std::uint64_t id, std::bool_t visible) -> std::expected<void, modulus::error>; auto set_visible(std::uint64_t id, std::bool_t visible) -> std::expected<void, modulus::error>;
auto find(std::uint64_t id) -> luma::window*; auto find(std::uint64_t id) -> luma::window *;
auto clear() -> void; auto clear() -> void;
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto arrange(luma::rect bounds) -> void override; auto arrange(luma::rect bounds) -> void override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto invalidate_layout() -> void override; auto invalidate_layout() -> void override;
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override; auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
private: private:
+6 -2
View File
@@ -16,6 +16,7 @@ COMMANDS = (
"package", "package",
"lsp", "lsp",
"clangd", "clangd",
"format",
) )
COMMON_DIR = pathlib.Path("build") / "build_common" COMMON_DIR = pathlib.Path("build") / "build_common"
TOOLS_DIR = pathlib.Path("build") / "build_tools" TOOLS_DIR = pathlib.Path("build") / "build_tools"
@@ -60,6 +61,9 @@ def runtime_ready(root):
and (root / TOOLS_DIR / "clangd.py").is_file() and (root / TOOLS_DIR / "clangd.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "cli.py").is_file() and (root / RUNTIME_DIR / "modulus" / "cli.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "export.py").is_file() and (root / RUNTIME_DIR / "modulus" / "export.py").is_file()
and (root / TOOLS_DIR / "format.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "formatting.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "format_layout.py").is_file()
) )
def optional_credentials(): def optional_credentials():
@@ -340,9 +344,9 @@ def ensure_runtime(root):
def delegate(command, argv): def delegate(command, argv):
root = repo_root() root = repo_root()
if command == "clangd": if command in {"clangd", "format"}:
if not runtime_ready(root): if not runtime_ready(root):
print("Modulus clangd tooling is missing; run ./modulus.py configure first", file=sys.stderr) print(f"Modulus {command} tooling is missing; run ./modulus.py configure first", file=sys.stderr)
return 1 return 1
else: else:
ensure_runtime(root) ensure_runtime(root)
+8 -11
View File
@@ -11,6 +11,7 @@
#include <luma/detail/errors.hpp> #include <luma/detail/errors.hpp>
namespace { namespace {
auto invalid(std::string message) -> std::expected<void, modulus::error> { auto invalid(std::string message) -> std::expected<void, modulus::error> {
@@ -26,7 +27,7 @@ auto internal(std::string message) -> std::expected<void, modulus::error> {
return luma::detail::fail<void>(luma::detail::error_internal, std::move(message)); return luma::detail::fail<void>(luma::detail::error_internal, std::move(message));
} }
auto validate(luma::animation const& animation) -> std::expected<void, modulus::error> { auto validate(luma::animation const &animation) -> std::expected<void, modulus::error> {
if (!std::isfinite(animation.from)) { if (!std::isfinite(animation.from)) {
return invalid("animation from value must be finite"); return invalid("animation from value must be finite");
} }
@@ -42,11 +43,11 @@ auto validate(luma::animation const& animation) -> std::expected<void, modulus::
return {}; return {};
} }
auto callback_error(std::char_t const* action, std::exception const& error) -> std::expected<void, modulus::error> { auto callback_error(std::char_t const *action, std::exception const &error) -> std::expected<void, modulus::error> {
return internal(std::string{"animation "} + action + " callback failed: " + error.what()); return internal(std::string{"animation "} + action + " callback failed: " + error.what());
} }
auto callback_error(std::char_t const* action) -> std::expected<void, modulus::error> { auto callback_error(std::char_t const *action) -> std::expected<void, modulus::error> {
return internal(std::string{"animation "} + action + " callback failed with an unknown exception"); return internal(std::string{"animation "} + action + " callback failed with an unknown exception");
} }
@@ -104,12 +105,10 @@ auto luma::animation_graph::tick(std::chrono::steady_clock::duration delta) -> s
if (ticking_) { if (ticking_) {
return internal("animation graph is already ticking"); return internal("animation graph is already ticking");
} }
auto active = std::move(animations_); auto active = std::move(animations_);
animations_.clear(); animations_.clear();
ticking_ = true; ticking_ = true;
clear_requested_ = false; clear_requested_ = false;
auto fail_tick = [this](std::expected<void, modulus::error> status) { auto fail_tick = [this](std::expected<void, modulus::error> status) {
ticking_ = false; ticking_ = false;
clear_requested_ = false; clear_requested_ = false;
@@ -117,8 +116,7 @@ auto luma::animation_graph::tick(std::chrono::steady_clock::duration delta) -> s
pending_.clear(); pending_.clear();
return status; return status;
}; };
for (auto &running : active) {
for (auto& running : active) {
running.elapsed += delta; running.elapsed += delta;
auto duration = std::chrono::duration_cast<std::chrono::steady_clock::duration>(running.animation.duration); auto duration = std::chrono::duration_cast<std::chrono::steady_clock::duration>(running.animation.duration);
if (duration.count() <= 0) { if (duration.count() <= 0) {
@@ -135,7 +133,7 @@ auto luma::animation_graph::tick(std::chrono::steady_clock::duration delta) -> s
} }
try { try {
running.animation.apply(*interpolated); running.animation.apply(*interpolated);
} catch (std::exception const& error) { } catch (std::exception const &error) {
return fail_tick(callback_error("apply", error)); return fail_tick(callback_error("apply", error));
} catch (...) { } catch (...) {
return fail_tick(callback_error("apply")); return fail_tick(callback_error("apply"));
@@ -147,7 +145,7 @@ auto luma::animation_graph::tick(std::chrono::steady_clock::duration delta) -> s
if (running.animation.finished) { if (running.animation.finished) {
try { try {
running.animation.finished(); running.animation.finished();
} catch (std::exception const& error) { } catch (std::exception const &error) {
return fail_tick(callback_error("finished", error)); return fail_tick(callback_error("finished", error));
} catch (...) { } catch (...) {
return fail_tick(callback_error("finished")); return fail_tick(callback_error("finished"));
@@ -160,9 +158,8 @@ auto luma::animation_graph::tick(std::chrono::steady_clock::duration delta) -> s
} }
animations_.push_back(std::move(running)); animations_.push_back(std::move(running));
} }
ticking_ = false; ticking_ = false;
for (auto& operation : pending_) { for (auto &operation : pending_) {
if (operation.kind == luma::animation_graph::pending_kind::CLEAR) { if (operation.kind == luma::animation_graph::pending_kind::CLEAR) {
animations_.clear(); animations_.clear();
} else { } else {
+97 -101
View File
@@ -38,6 +38,7 @@
#include <luma/render.hpp> #include <luma/render.hpp>
#include <luma/routing.hpp> #include <luma/routing.hpp>
namespace luma::detail { namespace luma::detail {
struct application_core { struct application_core {
@@ -59,7 +60,7 @@ struct application_core {
luma::window_manager windows{}; luma::window_manager windows{};
std::unique_ptr<luma::asset_cache> assets{}; std::unique_ptr<luma::asset_cache> assets{};
std::unique_ptr<luma::renderer_resources> resources{}; std::unique_ptr<luma::renderer_resources> resources{};
luma::overlay_host* overlays{}; luma::overlay_host *overlays{};
std::atomic_bool layout_dirty{true}; std::atomic_bool layout_dirty{true};
std::atomic_bool redraw_dirty{true}; std::atomic_bool redraw_dirty{true};
std::bool_t layout_ready{false}; std::bool_t layout_ready{false};
@@ -75,12 +76,11 @@ namespace {
struct font_key { struct font_key {
std::string path{}; std::string path{};
std::int32_t size{}; std::int32_t size{};
auto operator==(font_key const &other) const -> std::bool_t;
auto operator==(font_key const& other) const -> std::bool_t;
}; };
struct font_key_hash { struct font_key_hash {
auto operator()(font_key const& key) const noexcept -> std::size_t; auto operator()(font_key const &key) const noexcept -> std::size_t;
}; };
struct raylib_resource_state { struct raylib_resource_state {
@@ -111,7 +111,7 @@ private:
class raylib_resources final : public luma::renderer_resources { class raylib_resources final : public luma::renderer_resources {
public: public:
explicit raylib_resources(std::shared_ptr<raylib_resource_state> state); explicit raylib_resources(std::shared_ptr<raylib_resource_state> state);
auto load_texture(std::string const& path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> override; auto load_texture(std::string const &path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> override;
auto state() const -> std::shared_ptr<raylib_resource_state>; auto state() const -> std::shared_ptr<raylib_resource_state>;
private: private:
std::shared_ptr<raylib_resource_state> state_{}; std::shared_ptr<raylib_resource_state> state_{};
@@ -125,11 +125,11 @@ public:
auto stroke_line(luma::point from, luma::point to, std::float32_t thickness, luma::color color) -> void override; auto stroke_line(luma::point from, luma::point to, std::float32_t thickness, luma::color color) -> void override;
auto fill_ellipse(luma::rect bounds, luma::color color) -> void override; auto fill_ellipse(luma::rect bounds, luma::color color) -> void override;
auto stroke_ellipse(luma::rect bounds, std::float32_t thickness, luma::color color) -> void override; auto stroke_ellipse(luma::rect bounds, std::float32_t thickness, luma::color color) -> void override;
auto draw_text(std::string_view text, luma::point origin, luma::text_style const& style, luma::color color) -> void override; auto draw_text(std::string_view text, luma::point origin, luma::text_style const &style, luma::color color) -> void override;
auto draw_texture(luma::texture const& texture, luma::rect source, luma::rect destination, luma::color tint) -> void override; auto draw_texture(luma::texture const &texture, luma::rect source, luma::rect destination, luma::color tint) -> void override;
auto push_clip(luma::rect bounds) -> void override; auto push_clip(luma::rect bounds) -> void override;
auto pop_clip() -> void override; auto pop_clip() -> void override;
auto measure_text(std::string_view text, luma::text_style const& style) const -> luma::size override; auto measure_text(std::string_view text, luma::text_style const &style) const -> luma::size override;
private: private:
std::shared_ptr<raylib_resource_state> state_{}; std::shared_ptr<raylib_resource_state> state_{};
std::vector<luma::rect> clip_stack_{}; std::vector<luma::rect> clip_stack_{};
@@ -138,8 +138,8 @@ private:
class scope_exit { class scope_exit {
public: public:
explicit scope_exit(std::function<void()> action); explicit scope_exit(std::function<void()> action);
scope_exit(scope_exit const&) = delete; scope_exit(scope_exit const &) = delete;
auto operator=(scope_exit const&) -> scope_exit& = delete; auto operator=(scope_exit const &) -> scope_exit & = delete;
~scope_exit(); ~scope_exit();
private: private:
std::function<void()> action_{}; std::function<void()> action_{};
@@ -149,8 +149,8 @@ private:
class drawing_scope { class drawing_scope {
public: public:
drawing_scope(); drawing_scope();
drawing_scope(drawing_scope const&) = delete; drawing_scope(drawing_scope const &) = delete;
auto operator=(drawing_scope const&) -> drawing_scope& = delete; auto operator=(drawing_scope const &) -> drawing_scope & = delete;
~drawing_scope(); ~drawing_scope();
auto end() -> void; auto end() -> void;
private: private:
@@ -159,12 +159,12 @@ private:
class ui_traversal_scope { class ui_traversal_scope {
public: public:
explicit ui_traversal_scope(luma::detail::application_core& core); explicit ui_traversal_scope(luma::detail::application_core &core);
ui_traversal_scope(ui_traversal_scope const&) = delete; ui_traversal_scope(ui_traversal_scope const &) = delete;
auto operator=(ui_traversal_scope const&) -> ui_traversal_scope& = delete; auto operator=(ui_traversal_scope const &) -> ui_traversal_scope & = delete;
~ui_traversal_scope(); ~ui_traversal_scope();
private: private:
luma::detail::application_core& core_; luma::detail::application_core &core_;
}; };
constexpr auto max_layout_passes = std::uint32_t{8}; constexpr auto max_layout_passes = std::uint32_t{8};
@@ -200,25 +200,25 @@ auto same_rect(luma::rect left, luma::rect right) -> std::bool_t {
return left.x == right.x && left.y == right.y && left.width == right.width && left.height == right.height; return left.x == right.x && left.y == right.y && left.width == right.width && left.height == right.height;
} }
auto make_event(luma::touch_event const& event, luma::touch_point point) -> luma::touch_event { auto make_event(luma::touch_event const &event, luma::touch_point point) -> luma::touch_event {
return { return {
.points = {point}, .points = {point},
.time = event.time, .time = event.time,
}; };
} }
auto invalidate_layout_cache(luma::detail::application_core& core) -> void; auto invalidate_layout_cache(luma::detail::application_core &core) -> void;
auto mark_redraw_dirty(luma::detail::application_core& core) -> void { auto mark_redraw_dirty(luma::detail::application_core &core) -> void {
core.redraw_dirty.store(true, std::memory_order_release); core.redraw_dirty.store(true, std::memory_order_release);
} }
auto mark_layout_dirty(luma::detail::application_core& core) -> void { auto mark_layout_dirty(luma::detail::application_core &core) -> void {
core.layout_dirty.store(true, std::memory_order_release); core.layout_dirty.store(true, std::memory_order_release);
mark_redraw_dirty(core); mark_redraw_dirty(core);
} }
auto make_layout_invalidator(std::shared_ptr<luma::detail::application_core> const& core) -> std::function<void()> { auto make_layout_invalidator(std::shared_ptr<luma::detail::application_core> const &core) -> std::function<void()> {
auto weak = std::weak_ptr<luma::detail::application_core>{core}; auto weak = std::weak_ptr<luma::detail::application_core>{core};
return [weak] { return [weak] {
if (auto locked = weak.lock()) { if (auto locked = weak.lock()) {
@@ -227,17 +227,17 @@ auto make_layout_invalidator(std::shared_ptr<luma::detail::application_core> con
}; };
} }
auto bind_layout_invalidator(std::shared_ptr<luma::detail::application_core> const& core, luma::control& control) -> void { auto bind_layout_invalidator(std::shared_ptr<luma::detail::application_core> const &core, luma::control &control) -> void {
control.bind_layout_invalidator(make_layout_invalidator(core)); control.bind_layout_invalidator(make_layout_invalidator(core));
} }
auto flush_retired_controls(luma::detail::application_core& core) -> void { auto flush_retired_controls(luma::detail::application_core &core) -> void {
if (core.traversal_depth == 0) { if (core.traversal_depth == 0) {
core.retired_controls.clear(); core.retired_controls.clear();
} }
} }
auto retire_control(luma::detail::application_core& core, std::unique_ptr<luma::control> control) -> void { auto retire_control(luma::detail::application_core &core, std::unique_ptr<luma::control> control) -> void {
if (control == nullptr) { if (control == nullptr) {
return; return;
} }
@@ -248,7 +248,7 @@ auto retire_control(luma::detail::application_core& core, std::unique_ptr<luma::
core.retired_controls.push_back(std::move(control)); core.retired_controls.push_back(std::move(control));
} }
auto install_retire_handlers(std::shared_ptr<luma::detail::application_core> const& core) -> void { auto install_retire_handlers(std::shared_ptr<luma::detail::application_core> const &core) -> void {
auto weak = std::weak_ptr<luma::detail::application_core>{core}; auto weak = std::weak_ptr<luma::detail::application_core>{core};
core->router.set_retire_handler([weak](std::unique_ptr<luma::control> control) mutable { core->router.set_retire_handler([weak](std::unique_ptr<luma::control> control) mutable {
if (auto locked = weak.lock()) { if (auto locked = weak.lock()) {
@@ -257,27 +257,27 @@ auto install_retire_handlers(std::shared_ptr<luma::detail::application_core> con
}); });
} }
auto active_control(luma::detail::application_core& core) -> luma::control* { auto active_control(luma::detail::application_core &core) -> luma::control * {
auto* active = core.root.get(); auto *active = core.root.get();
if (active == nullptr) { if (active == nullptr) {
active = core.router.current(); active = core.router.current();
} }
return active; return active;
} }
auto invalidate_layout_cache(luma::detail::application_core& core) -> void { auto invalidate_layout_cache(luma::detail::application_core &core) -> void {
auto traversal = ui_traversal_scope{core}; auto traversal = ui_traversal_scope{core};
if (auto* active = active_control(core); active != nullptr) { if (auto *active = active_control(core); active != nullptr) {
active->invalidate_layout(); active->invalidate_layout();
} }
core.windows.invalidate_layout(); core.windows.invalidate_layout();
} }
template <typename VALUE, typename TASK> template <typename VALUE, typename TASK>
auto run_task(TASK& task) -> std::expected<VALUE, modulus::error> { auto run_task(TASK &task) -> std::expected<VALUE, modulus::error> {
try { try {
return task(); return task();
} catch (std::exception const& error) { } catch (std::exception const &error) {
return internal<VALUE>(error.what()); return internal<VALUE>(error.what());
} catch (...) { } catch (...) {
return internal<VALUE>("UI task failed with an unknown exception"); return internal<VALUE>("UI task failed with an unknown exception");
@@ -285,7 +285,7 @@ auto run_task(TASK& task) -> std::expected<VALUE, modulus::error> {
} }
template <typename VALUE, typename TASK> template <typename VALUE, typename TASK>
auto run_ui_task(luma::detail::application_core& core, TASK& task, std::bool_t dirty_after) -> std::expected<VALUE, modulus::error> { auto run_ui_task(luma::detail::application_core &core, TASK &task, std::bool_t dirty_after) -> std::expected<VALUE, modulus::error> {
auto result = run_task<VALUE>(task); auto result = run_task<VALUE>(task);
if (dirty_after) { if (dirty_after) {
mark_layout_dirty(core); mark_layout_dirty(core);
@@ -294,15 +294,13 @@ auto run_ui_task(luma::detail::application_core& core, TASK& task, std::bool_t d
} }
template <typename VALUE, typename TASK> template <typename VALUE, typename TASK>
auto dispatch_to_ui(std::shared_ptr<luma::detail::application_core> const& core, TASK&& input, std::bool_t dirty_after = true) -> std::expected<VALUE, modulus::error> { auto dispatch_to_ui(std::shared_ptr<luma::detail::application_core> const &core, TASK &&input, std::bool_t dirty_after = true) -> std::expected<VALUE, modulus::error> {
if (!core) { if (!core) {
return unavailable<VALUE>("application is not initialized"); return unavailable<VALUE>("application is not initialized");
} }
auto task = std::decay_t<TASK>{std::forward<TASK>(input)}; auto task = std::decay_t<TASK>{std::forward<TASK>(input)};
auto should_post = false; auto should_post = false;
auto wait_for_startup = false; auto wait_for_startup = false;
{ {
auto lock = std::unique_lock{core->lifecycle_mutex}; auto lock = std::unique_lock{core->lifecycle_mutex};
if (core->stopping.load(std::memory_order_acquire)) { if (core->stopping.load(std::memory_order_acquire)) {
@@ -317,7 +315,6 @@ auto dispatch_to_ui(std::shared_ptr<luma::detail::application_core> const& core,
should_post = running && accepting; should_post = running && accepting;
wait_for_startup = running && !accepting; wait_for_startup = running && !accepting;
} }
if (!should_post || wait_for_startup) { if (!should_post || wait_for_startup) {
auto immediate_lock = std::scoped_lock{core->immediate_mutex}; auto immediate_lock = std::scoped_lock{core->immediate_mutex};
auto lock = std::unique_lock{core->lifecycle_mutex}; auto lock = std::unique_lock{core->lifecycle_mutex};
@@ -337,7 +334,6 @@ auto dispatch_to_ui(std::shared_ptr<luma::detail::application_core> const& core,
} }
should_post = true; should_post = true;
} }
auto future = std::future<std::expected<VALUE, modulus::error>>{}; auto future = std::future<std::expected<VALUE, modulus::error>>{};
auto promise = std::make_shared<std::promise<std::expected<VALUE, modulus::error>>>(); auto promise = std::make_shared<std::promise<std::expected<VALUE, modulus::error>>>();
future = promise->get_future(); future = promise->get_future();
@@ -359,21 +355,20 @@ auto dispatch_to_ui(std::shared_ptr<luma::detail::application_core> const& core,
} }
promise->set_value(run_ui_task<VALUE>(*core, queued, dirty_after)); promise->set_value(run_ui_task<VALUE>(*core, queued, dirty_after));
}); });
} catch (std::exception const& error) { } catch (std::exception const &error) {
return internal<VALUE>(error.what()); return internal<VALUE>(error.what());
} catch (...) { } catch (...) {
return internal<VALUE>("failed to queue UI task"); return internal<VALUE>("failed to queue UI task");
} }
return future.get(); return future.get();
} }
auto drain_ui_tasks(luma::detail::application_core& core) -> std::size_t { auto drain_ui_tasks(luma::detail::application_core &core) -> std::size_t {
core.ui_context.restart(); core.ui_context.restart();
return core.ui_context.poll(); return core.ui_context.poll();
} }
auto finish_run(luma::detail::application_core& core) -> void { auto finish_run(luma::detail::application_core &core) -> void {
auto lock = std::scoped_lock{core.lifecycle_mutex}; auto lock = std::scoped_lock{core.lifecycle_mutex};
core.accepting_tasks.store(false, std::memory_order_release); core.accepting_tasks.store(false, std::memory_order_release);
core.running.store(false, std::memory_order_release); core.running.store(false, std::memory_order_release);
@@ -382,7 +377,7 @@ auto finish_run(luma::detail::application_core& core) -> void {
core.stop_requested.store(false, std::memory_order_release); core.stop_requested.store(false, std::memory_order_release);
} }
auto cleanup_ui_state(luma::detail::application_core& core) -> void { auto cleanup_ui_state(luma::detail::application_core &core) -> void {
if (core.assets) { if (core.assets) {
core.assets->clear(); core.assets->clear();
} }
@@ -398,7 +393,7 @@ auto cleanup_ui_state(luma::detail::application_core& core) -> void {
mark_layout_dirty(core); mark_layout_dirty(core);
} }
auto unload_record(raylib_resource_state::texture_record& record) -> void { auto unload_record(raylib_resource_state::texture_record &record) -> void {
if (!record.unloaded && record.texture.id != 0) { if (!record.unloaded && record.texture.id != 0) {
UnloadTexture(record.texture); UnloadTexture(record.texture);
record.unloaded = true; record.unloaded = true;
@@ -406,7 +401,7 @@ auto unload_record(raylib_resource_state::texture_record& record) -> void {
} }
} }
auto flush_pending_unloads(raylib_resource_state& state) -> std::bool_t { auto flush_pending_unloads(raylib_resource_state &state) -> std::bool_t {
assert(state.ui_thread == std::this_thread::get_id()); assert(state.ui_thread == std::this_thread::get_id());
auto pending = std::vector<std::shared_ptr<raylib_resource_state::texture_record>>{}; auto pending = std::vector<std::shared_ptr<raylib_resource_state::texture_record>>{};
{ {
@@ -414,7 +409,7 @@ auto flush_pending_unloads(raylib_resource_state& state) -> std::bool_t {
pending.swap(state.pending_unloads); pending.swap(state.pending_unloads);
} }
auto unloaded = false; auto unloaded = false;
for (auto& record : pending) { for (auto &record : pending) {
if (record) { if (record) {
unload_record(*record); unload_record(*record);
unloaded = true; unloaded = true;
@@ -423,7 +418,7 @@ auto flush_pending_unloads(raylib_resource_state& state) -> std::bool_t {
return unloaded; return unloaded;
} }
auto unload_all_textures(raylib_resource_state& state) -> void { auto unload_all_textures(raylib_resource_state &state) -> void {
assert(state.ui_thread == std::this_thread::get_id()); assert(state.ui_thread == std::this_thread::get_id());
static_cast<void>(flush_pending_unloads(state)); static_cast<void>(flush_pending_unloads(state));
auto records = std::vector<std::shared_ptr<raylib_resource_state::texture_record>>{}; auto records = std::vector<std::shared_ptr<raylib_resource_state::texture_record>>{};
@@ -439,14 +434,14 @@ auto unload_all_textures(raylib_resource_state& state) -> void {
} }
state.records.erase(write, state.records.end()); state.records.erase(write, state.records.end());
} }
for (auto& record : records) { for (auto &record : records) {
unload_record(*record); unload_record(*record);
} }
} }
auto unload_all_fonts(raylib_resource_state& state) -> void { auto unload_all_fonts(raylib_resource_state &state) -> void {
assert(state.ui_thread == std::this_thread::get_id()); assert(state.ui_thread == std::this_thread::get_id());
for (auto& [path, font] : state.fonts) { for (auto &[path, font] : state.fonts) {
static_cast<void>(path); static_cast<void>(path);
if (font.texture.id != 0) { if (font.texture.id != 0) {
UnloadFont(font); UnloadFont(font);
@@ -455,7 +450,7 @@ auto unload_all_fonts(raylib_resource_state& state) -> void {
state.fonts.clear(); state.fonts.clear();
} }
auto resolve_font(raylib_resource_state& state, luma::text_style const& style) -> Font { auto resolve_font(raylib_resource_state &state, luma::text_style const &style) -> Font {
assert(state.ui_thread == std::this_thread::get_id()); assert(state.ui_thread == std::this_thread::get_id());
assert(IsWindowReady()); assert(IsWindowReady());
if (style.font.empty()) { if (style.font.empty()) {
@@ -496,7 +491,7 @@ auto begin_clip(luma::rect bounds) -> void {
BeginScissorMode(static_cast<std::int32_t>(bounds.x), static_cast<std::int32_t>(bounds.y), static_cast<std::int32_t>(bounds.width), static_cast<std::int32_t>(bounds.height)); BeginScissorMode(static_cast<std::int32_t>(bounds.x), static_cast<std::int32_t>(bounds.y), static_cast<std::int32_t>(bounds.width), static_cast<std::int32_t>(bounds.height));
} }
auto poll_touch(std::unordered_map<std::int32_t, luma::point>& previous, std::int32_t max_points) -> luma::touch_event { auto poll_touch(std::unordered_map<std::int32_t, luma::point> &previous, std::int32_t max_points) -> luma::touch_event {
assert(IsWindowReady()); assert(IsWindowReady());
assert(max_points > 0); assert(max_points > 0);
auto current = std::unordered_map<std::int32_t, luma::point>{}; auto current = std::unordered_map<std::int32_t, luma::point>{};
@@ -517,7 +512,7 @@ auto poll_touch(std::unordered_map<std::int32_t, luma::point>& previous, std::in
.phase = phase, .phase = phase,
}); });
} }
for (auto const& [id, position] : previous) { for (auto const &[id, position] : previous) {
if (!current.contains(id)) { if (!current.contains(id)) {
points.push_back({ points.push_back({
.id = id, .id = id,
@@ -533,17 +528,18 @@ auto poll_touch(std::unordered_map<std::int32_t, luma::point>& previous, std::in
}; };
} }
auto font_key::operator==(font_key const& other) const -> std::bool_t { auto font_key::operator==(font_key const &other) const -> std::bool_t {
return path == other.path && size == other.size; return path == other.path && size == other.size;
} }
auto font_key_hash::operator()(font_key const& key) const noexcept -> std::size_t { auto font_key_hash::operator()(font_key const &key) const noexcept -> std::size_t {
auto seed = std::hash<std::string>{}(key.path); auto seed = std::hash<std::string>{}(key.path);
seed ^= std::hash<std::int32_t>{}(key.size) + 0x9e3779b9U + (seed << 6U) + (seed >> 2U); seed ^= std::hash<std::int32_t>{}(key.size) + 0x9e3779b9U + (seed << 6U) + (seed >> 2U);
return seed; return seed;
} }
scope_exit::scope_exit(std::function<void()> action) : action_{std::move(action)} {} scope_exit::scope_exit(std::function<void()> action)
: action_{std::move(action)} {}
scope_exit::~scope_exit() { scope_exit::~scope_exit() {
if (!active_ || !action_) { if (!active_ || !action_) {
@@ -576,7 +572,8 @@ auto drawing_scope::end() -> void {
} }
} }
ui_traversal_scope::ui_traversal_scope(luma::detail::application_core& core) : core_{core} { ui_traversal_scope::ui_traversal_scope(luma::detail::application_core &core)
: core_{core} {
++core_.traversal_depth; ++core_.traversal_depth;
} }
@@ -586,7 +583,8 @@ ui_traversal_scope::~ui_traversal_scope() {
flush_retired_controls(core_); flush_retired_controls(core_);
} }
raylib_texture::raylib_texture(std::shared_ptr<raylib_resource_state::texture_record> record, std::shared_ptr<raylib_resource_state> state) : record_{std::move(record)}, state_{std::move(state)} { raylib_texture::raylib_texture(std::shared_ptr<raylib_resource_state::texture_record> record, std::shared_ptr<raylib_resource_state> state)
: record_{std::move(record)}, state_{std::move(state)} {
assert(record_ != nullptr); assert(record_ != nullptr);
assert(record_->texture.id != 0); assert(record_->texture.id != 0);
assert(record_->texture.width > 0); assert(record_->texture.width > 0);
@@ -623,11 +621,12 @@ auto raylib_texture::native() const -> Texture2D {
return record_->texture; return record_->texture;
} }
raylib_resources::raylib_resources(std::shared_ptr<raylib_resource_state> state) : state_{std::move(state)} { raylib_resources::raylib_resources(std::shared_ptr<raylib_resource_state> state)
: state_{std::move(state)} {
assert(state_ != nullptr); assert(state_ != nullptr);
} }
auto raylib_resources::load_texture(std::string const& path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> { auto raylib_resources::load_texture(std::string const &path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> {
assert(state_ != nullptr); assert(state_ != nullptr);
assert(state_->active.load(std::memory_order_acquire)); assert(state_->active.load(std::memory_order_acquire));
assert(state_->ui_thread == std::this_thread::get_id()); assert(state_->ui_thread == std::this_thread::get_id());
@@ -652,29 +651,32 @@ auto raylib_resources::state() const -> std::shared_ptr<raylib_resource_state> {
return state_; return state_;
} }
raylib_draw_context::raylib_draw_context(std::shared_ptr<raylib_resource_state> state) : state_{std::move(state)} { raylib_draw_context::raylib_draw_context(std::shared_ptr<raylib_resource_state> state)
: state_{std::move(state)} {
assert(state_ != nullptr); assert(state_ != nullptr);
} }
auto raylib_draw_context::fill_rect(luma::rect bounds, luma::color color) -> void { auto raylib_draw_context::fill_rect(luma::rect bounds, luma::color color) -> void {
assert(IsWindowReady()); assert(IsWindowReady());
DrawRectangleRec({ DrawRectangleRec({
.x = bounds.x, .x = bounds.x,
.y = bounds.y, .y = bounds.y,
.width = bounds.width, .width = bounds.width,
.height = bounds.height, .height = bounds.height,
}, to_raylib(color)); },
to_raylib(color));
} }
auto raylib_draw_context::stroke_rect(luma::rect bounds, std::float32_t thickness, luma::color color) -> void { auto raylib_draw_context::stroke_rect(luma::rect bounds, std::float32_t thickness, luma::color color) -> void {
assert(IsWindowReady()); assert(IsWindowReady());
assert(thickness >= 0.0F); assert(thickness >= 0.0F);
DrawRectangleLinesEx({ DrawRectangleLinesEx({
.x = bounds.x, .x = bounds.x,
.y = bounds.y, .y = bounds.y,
.width = bounds.width, .width = bounds.width,
.height = bounds.height, .height = bounds.height,
}, thickness, to_raylib(color)); },
thickness, to_raylib(color));
} }
auto raylib_draw_context::stroke_line(luma::point from, luma::point to, std::float32_t thickness, luma::color color) -> void { auto raylib_draw_context::stroke_line(luma::point from, luma::point to, std::float32_t thickness, luma::color color) -> void {
@@ -719,21 +721,22 @@ auto raylib_draw_context::stroke_ellipse(luma::rect bounds, std::float32_t thick
} }
} }
auto raylib_draw_context::draw_text(std::string_view text, luma::point origin, luma::text_style const& style, luma::color color) -> void { auto raylib_draw_context::draw_text(std::string_view text, luma::point origin, luma::text_style const &style, luma::color color) -> void {
assert(IsWindowReady()); assert(IsWindowReady());
assert(state_ != nullptr); assert(state_ != nullptr);
assert(style.size > 0.0F); assert(style.size > 0.0F);
auto owned = std::string{text}; auto owned = std::string{text};
auto font = resolve_font(*state_, style); auto font = resolve_font(*state_, style);
DrawTextEx(font, owned.c_str(), { DrawTextEx(font, owned.c_str(), {
.x = origin.x, .x = origin.x,
.y = origin.y, .y = origin.y,
}, style.size, 1.0F, to_raylib(color)); },
style.size, 1.0F, to_raylib(color));
} }
auto raylib_draw_context::draw_texture(luma::texture const& texture, luma::rect source, luma::rect destination, luma::color tint) -> void { auto raylib_draw_context::draw_texture(luma::texture const &texture, luma::rect source, luma::rect destination, luma::color tint) -> void {
assert(IsWindowReady()); assert(IsWindowReady());
auto const* native = dynamic_cast<raylib_texture const*>(&texture); auto const *native = dynamic_cast<raylib_texture const *>(&texture);
if (native == nullptr || !native->valid()) { if (native == nullptr || !native->valid()) {
return; return;
} }
@@ -776,7 +779,7 @@ auto raylib_draw_context::pop_clip() -> void {
} }
} }
auto raylib_draw_context::measure_text(std::string_view text, luma::text_style const& style) const -> luma::size { auto raylib_draw_context::measure_text(std::string_view text, luma::text_style const &style) const -> luma::size {
assert(IsWindowReady()); assert(IsWindowReady());
assert(state_ != nullptr); assert(state_ != nullptr);
assert(style.size > 0.0F); assert(style.size > 0.0F);
@@ -791,15 +794,17 @@ auto raylib_draw_context::measure_text(std::string_view text, luma::text_style c
} }
luma::detail::application_core::application_core(luma::application_config value) : ui_strand{boost::asio::make_strand(ui_context)}, config{std::move(value)} {} luma::detail::application_core::application_core(luma::application_config value)
: ui_strand{boost::asio::make_strand(ui_context)}, config{std::move(value)} {}
luma::application::application(luma::application_config config) : core_{std::make_shared<luma::detail::application_core>(std::move(config))} { luma::application::application(luma::application_config config)
: core_{std::make_shared<luma::detail::application_core>(std::move(config))} {
assert(core_ != nullptr); assert(core_ != nullptr);
install_retire_handlers(core_); install_retire_handlers(core_);
core_->windows.bind_layout_invalidator(make_layout_invalidator(core_)); core_->windows.bind_layout_invalidator(make_layout_invalidator(core_));
} }
luma::application::application(luma::application&& other) noexcept { luma::application::application(luma::application &&other) noexcept {
auto lock = std::scoped_lock{other.core_mutex_}; auto lock = std::scoped_lock{other.core_mutex_};
core_ = std::move(other.core_); core_ = std::move(other.core_);
} }
@@ -808,7 +813,7 @@ luma::application::~application() {
request_stop(); request_stop();
} }
auto luma::application::operator=(luma::application&& other) noexcept -> luma::application& { auto luma::application::operator=(luma::application &&other) noexcept -> luma::application & {
if (this != &other) { if (this != &other) {
request_stop(); request_stop();
auto lock = std::scoped_lock{ auto lock = std::scoped_lock{
@@ -851,7 +856,7 @@ auto luma::application::replace_route(std::string name) -> std::expected<void, m
return dispatch_to_ui<void>(core, [core, name = std::move(name)] { return dispatch_to_ui<void>(core, [core, name = std::move(name)] {
auto status = core->router.replace(name); auto status = core->router.replace(name);
if (status) { if (status) {
if (auto* current = core->router.current(); current != nullptr) { if (auto *current = core->router.current(); current != nullptr) {
bind_layout_invalidator(core, *current); bind_layout_invalidator(core, *current);
} }
} }
@@ -865,7 +870,7 @@ auto luma::application::push_route(std::string name) -> std::expected<void, modu
return dispatch_to_ui<void>(core, [core, name = std::move(name)] { return dispatch_to_ui<void>(core, [core, name = std::move(name)] {
auto status = core->router.push(name); auto status = core->router.push(name);
if (status) { if (status) {
if (auto* current = core->router.current(); current != nullptr) { if (auto *current = core->router.current(); current != nullptr) {
bind_layout_invalidator(core, *current); bind_layout_invalidator(core, *current);
} }
} }
@@ -878,7 +883,7 @@ auto luma::application::pop_route() -> std::expected<void, modulus::error> {
return dispatch_to_ui<void>(core, [core] { return dispatch_to_ui<void>(core, [core] {
auto status = core->router.pop(); auto status = core->router.pop();
if (status) { if (status) {
if (auto* current = core->router.current(); current != nullptr) { if (auto *current = core->router.current(); current != nullptr) {
bind_layout_invalidator(core, *current); bind_layout_invalidator(core, *current);
} }
} }
@@ -888,9 +893,7 @@ auto luma::application::pop_route() -> std::expected<void, modulus::error> {
auto luma::application::current_route() const -> std::expected<std::string, modulus::error> { auto luma::application::current_route() const -> std::expected<std::string, modulus::error> {
auto core = core_snapshot(); auto core = core_snapshot();
return dispatch_to_ui<std::string>(core, [core] { return dispatch_to_ui<std::string>(core, [core] { return modulus::success(std::string{core->router.current_route()}); }, false);
return modulus::success(std::string{core->router.current_route()});
}, false);
} }
auto luma::application::add_animation(luma::animation animation) -> std::expected<void, modulus::error> { auto luma::application::add_animation(luma::animation animation) -> std::expected<void, modulus::error> {
@@ -910,9 +913,7 @@ auto luma::application::clear_animations() -> std::expected<void, modulus::error
auto luma::application::animations_empty() const -> std::expected<std::bool_t, modulus::error> { auto luma::application::animations_empty() const -> std::expected<std::bool_t, modulus::error> {
auto core = core_snapshot(); auto core = core_snapshot();
return dispatch_to_ui<std::bool_t>(core, [core] { return dispatch_to_ui<std::bool_t>(core, [core] { return modulus::success(core->animations.empty()); }, false);
return modulus::success(core->animations.empty());
}, false);
} }
auto luma::application::open_window(luma::window_options options, std::unique_ptr<luma::control> content) -> std::expected<std::uint64_t, modulus::error> { auto luma::application::open_window(luma::window_options options, std::unique_ptr<luma::control> content) -> std::expected<std::uint64_t, modulus::error> {
@@ -981,8 +982,7 @@ auto luma::application::load_texture(std::string path) -> std::expected<std::sha
if (!core->renderer_active.load(std::memory_order_acquire) || !core->assets) { if (!core->renderer_active.load(std::memory_order_acquire) || !core->assets) {
return unavailable<std::shared_ptr<luma::texture>>("texture loading requires an active renderer"); return unavailable<std::shared_ptr<luma::texture>>("texture loading requires an active renderer");
} }
return core->assets->texture(path); return core->assets->texture(path); }, false);
}, false);
} }
auto luma::application::request_layout() -> std::expected<void, modulus::error> { auto luma::application::request_layout() -> std::expected<void, modulus::error> {
@@ -1100,7 +1100,6 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
core->running.store(true, std::memory_order_release); core->running.store(true, std::memory_order_release);
core->accepting_tasks.store(false, std::memory_order_release); core->accepting_tasks.store(false, std::memory_order_release);
} }
auto resource_state = std::shared_ptr<raylib_resource_state>{}; auto resource_state = std::shared_ptr<raylib_resource_state>{};
auto window_ready = false; auto window_ready = false;
[[maybe_unused]] auto cleanup = scope_exit{[&core, &resource_state, &window_ready] { [[maybe_unused]] auto cleanup = scope_exit{[&core, &resource_state, &window_ready] {
@@ -1123,7 +1122,6 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
} }
finish_run(*core); finish_run(*core);
}}; }};
InitWindow(core->config.display.width, core->config.display.height, core->config.title.c_str()); InitWindow(core->config.display.width, core->config.display.height, core->config.title.c_str());
if (!IsWindowReady()) { if (!IsWindowReady()) {
return unavailable<void>("failed to initialize raylib window"); return unavailable<void>("failed to initialize raylib window");
@@ -1135,7 +1133,6 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
auto current_target_fps = core->config.display.target_fps; auto current_target_fps = core->config.display.target_fps;
auto const idle_fps = core->config.display.idle_fps; auto const idle_fps = core->config.display.idle_fps;
SetTargetFPS(current_target_fps); SetTargetFPS(current_target_fps);
resource_state = std::make_shared<raylib_resource_state>(); resource_state = std::make_shared<raylib_resource_state>();
resource_state->ui_thread = std::this_thread::get_id(); resource_state->ui_thread = std::this_thread::get_id();
resource_state->active.store(true, std::memory_order_release); resource_state->active.store(true, std::memory_order_release);
@@ -1147,7 +1144,6 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
core->accepting_tasks.store(true, std::memory_order_release); core->accepting_tasks.store(true, std::memory_order_release);
} }
startup_lock.unlock(); startup_lock.unlock();
auto context = raylib_draw_context{resource_state}; auto context = raylib_draw_context{resource_state};
auto previous_touches = std::unordered_map<std::int32_t, luma::point>{}; auto previous_touches = std::unordered_map<std::int32_t, luma::point>{};
auto previous_frame = std::chrono::steady_clock::now(); auto previous_frame = std::chrono::steady_clock::now();
@@ -1181,13 +1177,13 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
} }
auto touch_requires_layout = false; auto touch_requires_layout = false;
if (!event.points.empty()) { if (!event.points.empty()) {
for (auto const& point : event.points) { for (auto const &point : event.points) {
auto single = make_event(event, point); auto single = make_event(event, point);
if (core->windows.handle_touch(single)) { if (core->windows.handle_touch(single)) {
touch_requires_layout = touch_requires_layout || point.phase != luma::touch_phase::MOVE; touch_requires_layout = touch_requires_layout || point.phase != luma::touch_phase::MOVE;
continue; continue;
} }
auto* active = active_control(*core); auto *active = active_control(*core);
if (active != nullptr && active->handle_touch(single)) { if (active != nullptr && active->handle_touch(single)) {
touch_requires_layout = touch_requires_layout || point.phase != luma::touch_phase::MOVE; touch_requires_layout = touch_requires_layout || point.phase != luma::touch_phase::MOVE;
} }
@@ -1223,7 +1219,7 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
if (pass > max_layout_passes) { if (pass > max_layout_passes) {
return modulus::fail<void>(internal_error("application layout did not stabilize")); return modulus::fail<void>(internal_error("application layout did not stabilize"));
} }
auto* active = active_control(*core); auto *active = active_control(*core);
if (active != nullptr) { if (active != nullptr) {
active->measure(context, core->config.theme, limits); active->measure(context, core->config.theme, limits);
active->arrange(display); active->arrange(display);
@@ -1244,7 +1240,7 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
if (should_draw) { if (should_draw) {
auto drawing = drawing_scope{}; auto drawing = drawing_scope{};
ClearBackground(to_raylib(core->config.theme.background)); ClearBackground(to_raylib(core->config.theme.background));
auto* active = active_control(*core); auto *active = active_control(*core);
if (active != nullptr) { if (active != nullptr) {
active->draw(context, core->config.theme); active->draw(context, core->config.theme);
} }
@@ -1255,7 +1251,7 @@ auto luma::application::run() -> std::expected<void, modulus::error> {
PollInputEvents(); PollInputEvents();
} }
} }
} catch (std::exception const& error) { } catch (std::exception const &error) {
return modulus::fail<void>(internal_error(std::string{"application frame failed: "} + error.what())); return modulus::fail<void>(internal_error(std::string{"application frame failed: "} + error.what()));
} catch (...) { } catch (...) {
return modulus::fail<void>(internal_error("application frame failed with an unknown exception")); return modulus::fail<void>(internal_error("application frame failed with an unknown exception"));
+5 -3
View File
@@ -16,6 +16,7 @@
#include <luma/detail/errors.hpp> #include <luma/detail/errors.hpp>
struct luma::asset_cache::in_flight_load { struct luma::asset_cache::in_flight_load {
std::condition_variable ready{}; std::condition_variable ready{};
std::thread::id owner{}; std::thread::id owner{};
@@ -23,11 +24,12 @@ struct luma::asset_cache::in_flight_load {
std::bool_t done{false}; std::bool_t done{false};
}; };
luma::asset_cache::asset_cache(luma::renderer_resources& resources) : resources_{&resources} { luma::asset_cache::asset_cache(luma::renderer_resources &resources)
: resources_{&resources} {
assert(resources_ != nullptr); assert(resources_ != nullptr);
} }
auto luma::asset_cache::texture(std::string const& path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> { auto luma::asset_cache::texture(std::string const &path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> {
assert(resources_ != nullptr); assert(resources_ != nullptr);
if (path.empty()) { if (path.empty()) {
return luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_invalid_argument, "texture path must not be empty"); return luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_invalid_argument, "texture path must not be empty");
@@ -68,7 +70,7 @@ auto luma::asset_cache::texture(std::string const& path) -> std::expected<std::s
modulus::log_debug("luma texture loading: " + path); modulus::log_debug("luma texture loading: " + path);
try { try {
loaded = resources_->load_texture(path); loaded = resources_->load_texture(path);
} catch (std::exception const& error) { } catch (std::exception const &error) {
loaded = luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_internal, std::string{"texture load failed: "} + error.what()); loaded = luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_internal, std::string{"texture load failed: "} + error.what());
} catch (...) { } catch (...) {
loaded = luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_internal, "texture load failed with an unknown exception"); loaded = luma::detail::fail<std::shared_ptr<luma::texture>>(luma::detail::error_internal, "texture load failed with an unknown exception");
+28 -26
View File
@@ -15,13 +15,13 @@
#include <luma/detail/errors.hpp> #include <luma/detail/errors.hpp>
#include <luma/theme.hpp> #include <luma/theme.hpp>
namespace { namespace {
constexpr auto json_options = modulus::json_config::options{ constexpr auto json_options = modulus::json_config::options{
.category = "luma", .category = "luma",
.invalid_argument_code = luma::detail::error_invalid_argument, .invalid_argument_code = luma::detail::error_invalid_argument,
.not_found_code = luma::detail::error_not_found .not_found_code = luma::detail::error_not_found};
};
auto invalid_display(std::string message) -> std::expected<luma::display_config, modulus::error> { auto invalid_display(std::string message) -> std::expected<luma::display_config, modulus::error> {
assert(!message.empty()); assert(!message.empty());
@@ -40,7 +40,7 @@ auto invalid_application(std::string message) -> std::expected<luma::application
} }
auto luma::validate(luma::display_config const& config) -> std::expected<void, modulus::error> { auto luma::validate(luma::display_config const &config) -> std::expected<void, modulus::error> {
if (config.width <= 0) { if (config.width <= 0) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "display.width must be positive"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "display.width must be positive");
} }
@@ -59,14 +59,14 @@ auto luma::validate(luma::display_config const& config) -> std::expected<void, m
return {}; return {};
} }
auto luma::validate(luma::touch_config const& config) -> std::expected<void, modulus::error> { auto luma::validate(luma::touch_config const &config) -> std::expected<void, modulus::error> {
if (config.max_points <= 0) { if (config.max_points <= 0) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "touch.max_points must be positive"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "touch.max_points must be positive");
} }
return {}; return {};
} }
auto luma::validate(luma::application_config const& config) -> std::expected<void, modulus::error> { auto luma::validate(luma::application_config const &config) -> std::expected<void, modulus::error> {
if (config.title.empty()) { if (config.title.empty()) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "title must not be empty"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "title must not be empty");
} }
@@ -82,18 +82,19 @@ auto luma::validate(luma::application_config const& config) -> std::expected<voi
return {}; return {};
} }
auto luma::parse_display_config(boost::json::value const& value) -> std::expected<luma::display_config, modulus::error> { auto luma::parse_display_config(boost::json::value const &value) -> std::expected<luma::display_config, modulus::error> {
auto object = modulus::json_config::require_object(value, "display", json_options); auto object = modulus::json_config::require_object(value, "display", json_options);
if (!object) { if (!object) {
return invalid_display(std::string{object.error().message()}); return invalid_display(std::string{object.error().message()});
} }
auto unknown = modulus::json_config::reject_unknown_fields(**object, { auto unknown = modulus::json_config::reject_unknown_fields(**object, {
"width", "width",
"height", "height",
"target_fps", "target_fps",
"idle_fps", "idle_fps",
"fullscreen", "fullscreen",
}, "display", json_options); },
"display", json_options);
if (!unknown) { if (!unknown) {
return invalid_display(std::string{unknown.error().message()}); return invalid_display(std::string{unknown.error().message()});
} }
@@ -129,7 +130,7 @@ auto luma::parse_display_config(boost::json::value const& value) -> std::expecte
return modulus::success(config); return modulus::success(config);
} }
auto luma::parse_touch_config(boost::json::value const& value) -> std::expected<luma::touch_config, modulus::error> { auto luma::parse_touch_config(boost::json::value const &value) -> std::expected<luma::touch_config, modulus::error> {
auto object = modulus::json_config::require_object(value, "touch", json_options); auto object = modulus::json_config::require_object(value, "touch", json_options);
if (!object) { if (!object) {
return invalid_touch(std::string{object.error().message()}); return invalid_touch(std::string{object.error().message()});
@@ -150,17 +151,18 @@ auto luma::parse_touch_config(boost::json::value const& value) -> std::expected<
return modulus::success(config); return modulus::success(config);
} }
auto luma::parse_application_config(boost::json::value const& value) -> std::expected<luma::application_config, modulus::error> { auto luma::parse_application_config(boost::json::value const &value) -> std::expected<luma::application_config, modulus::error> {
auto object = modulus::json_config::require_object(value, "application config", json_options); auto object = modulus::json_config::require_object(value, "application config", json_options);
if (!object) { if (!object) {
return invalid_application(std::string{object.error().message()}); return invalid_application(std::string{object.error().message()});
} }
auto unknown = modulus::json_config::reject_unknown_fields(**object, { auto unknown = modulus::json_config::reject_unknown_fields(**object, {
"title", "title",
"display", "display",
"touch", "touch",
"theme", "theme",
}, "application config", json_options); },
"application config", json_options);
if (!unknown) { if (!unknown) {
return invalid_application(std::string{unknown.error().message()}); return invalid_application(std::string{unknown.error().message()});
} }
@@ -170,21 +172,21 @@ auto luma::parse_application_config(boost::json::value const& value) -> std::exp
return invalid_application(std::string{title.error().message()}); return invalid_application(std::string{title.error().message()});
} }
config.title = *title; config.title = *title;
if (auto const* display = (*object)->if_contains("display")) { if (auto const *display = (*object)->if_contains("display")) {
auto parsed = luma::parse_display_config(*display); auto parsed = luma::parse_display_config(*display);
if (!parsed) { if (!parsed) {
return invalid_application(std::string{parsed.error().message()}); return invalid_application(std::string{parsed.error().message()});
} }
config.display = *parsed; config.display = *parsed;
} }
if (auto const* touch = (*object)->if_contains("touch")) { if (auto const *touch = (*object)->if_contains("touch")) {
auto parsed = luma::parse_touch_config(*touch); auto parsed = luma::parse_touch_config(*touch);
if (!parsed) { if (!parsed) {
return invalid_application(std::string{parsed.error().message()}); return invalid_application(std::string{parsed.error().message()});
} }
config.touch = *parsed; config.touch = *parsed;
} }
if (auto const* theme = (*object)->if_contains("theme")) { if (auto const *theme = (*object)->if_contains("theme")) {
auto parsed = luma::parse_theme(*theme); auto parsed = luma::parse_theme(*theme);
if (!parsed) { if (!parsed) {
return invalid_application(std::string{parsed.error().message()}); return invalid_application(std::string{parsed.error().message()});
@@ -197,7 +199,7 @@ auto luma::parse_application_config(boost::json::value const& value) -> std::exp
return modulus::success(config); return modulus::success(config);
} }
auto luma::load_application_config(std::string const& path) -> std::expected<luma::application_config, modulus::error> { auto luma::load_application_config(std::string const &path) -> std::expected<luma::application_config, modulus::error> {
auto value = modulus::json_config::load_value(path, json_options); auto value = modulus::json_config::load_value(path, json_options);
if (!value) { if (!value) {
return modulus::fail<luma::application_config>(value.error()); return modulus::fail<luma::application_config>(value.error());
@@ -205,7 +207,7 @@ auto luma::load_application_config(std::string const& path) -> std::expected<lum
return luma::parse_application_config(*value); return luma::parse_application_config(*value);
} }
auto luma::to_json(luma::display_config const& config) -> boost::json::value { auto luma::to_json(luma::display_config const &config) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"width", "width",
@@ -230,7 +232,7 @@ auto luma::to_json(luma::display_config const& config) -> boost::json::value {
}; };
} }
auto luma::to_json(luma::touch_config const& config) -> boost::json::value { auto luma::to_json(luma::touch_config const &config) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"max_points", "max_points",
@@ -239,7 +241,7 @@ auto luma::to_json(luma::touch_config const& config) -> boost::json::value {
}; };
} }
auto luma::to_json(luma::application_config const& config) -> boost::json::value { auto luma::to_json(luma::application_config const &config) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"title", "title",
+2 -1
View File
@@ -7,6 +7,7 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace { namespace {
auto valid_rect(luma::rect value) -> std::bool_t { auto valid_rect(luma::rect value) -> std::bool_t {
@@ -22,7 +23,7 @@ auto luma::control::arrange(luma::rect bounds) -> void {
bounds_ = bounds; bounds_ = bounds;
} }
auto luma::control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto luma::control::handle_touch(luma::touch_event const &event) -> std::bool_t {
static_cast<void>(event); static_cast<void>(event);
return false; return false;
} }
+177 -161
View File
@@ -27,21 +27,21 @@
#include <luma/render.hpp> #include <luma/render.hpp>
#include <luma/theme.hpp> #include <luma/theme.hpp>
namespace { namespace {
constexpr auto panel_padding = luma::edge_insets{ constexpr auto panel_padding = luma::edge_insets{
.left = 8.0F, .left = 8.0F,
.top = 8.0F, .top = 8.0F,
.right = 8.0F, .right = 8.0F,
.bottom = 8.0F .bottom = 8.0F};
};
constexpr auto scroll_drag_threshold = std::float32_t{6.0F}; constexpr auto scroll_drag_threshold = std::float32_t{6.0F};
class label_control final : public luma::control { class label_control final : public luma::control {
public: public:
explicit label_control(std::string text); explicit label_control(std::string text);
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
private: private:
std::string text_; std::string text_;
}; };
@@ -49,9 +49,9 @@ private:
class button_control final : public luma::control { class button_control final : public luma::control {
public: public:
button_control(std::string text, std::function<void()> on_click); button_control(std::string text, std::function<void()> on_click);
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto focusable() const -> std::bool_t override; auto focusable() const -> std::bool_t override;
private: private:
std::string text_; std::string text_;
@@ -62,9 +62,9 @@ private:
class text_field_control final : public luma::control { class text_field_control final : public luma::control {
public: public:
explicit text_field_control(std::shared_ptr<luma::text_input_state> input); explicit text_field_control(std::shared_ptr<luma::text_input_state> input);
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto invalidate_layout() -> void override; auto invalidate_layout() -> void override;
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override; auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
auto focusable() const -> std::bool_t override; auto focusable() const -> std::bool_t override;
@@ -81,9 +81,9 @@ class soft_keyboard_control final : public luma::control {
public: public:
soft_keyboard_control(luma::keyboard_layout layout, std::function<void(std::string)> on_text, std::function<void()> on_enter); soft_keyboard_control(luma::keyboard_layout layout, std::function<void(std::string)> on_text, std::function<void()> on_enter);
soft_keyboard_control(luma::keyboard_layout layout, std::shared_ptr<luma::text_input_state> input, std::function<void()> on_enter); soft_keyboard_control(luma::keyboard_layout layout, std::shared_ptr<luma::text_input_state> input, std::function<void()> on_enter);
auto measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
private: private:
luma::keyboard_layout layout_; luma::keyboard_layout layout_;
std::function<void(std::string)> on_text_; std::function<void(std::string)> on_text_;
@@ -94,10 +94,10 @@ private:
class panel_control final : public luma::control { class panel_control final : public luma::control {
public: public:
explicit panel_control(std::vector<std::unique_ptr<luma::control>> children); explicit panel_control(std::vector<std::unique_ptr<luma::control>> children);
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto arrange(luma::rect bounds) -> void override; auto arrange(luma::rect bounds) -> void override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto invalidate_layout() -> void override; auto invalidate_layout() -> void override;
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override; auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
private: private:
@@ -107,8 +107,8 @@ private:
class spacer_control final : public luma::control { class spacer_control final : public luma::control {
public: public:
explicit spacer_control(luma::size value); explicit spacer_control(luma::size value);
auto measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size override;
auto draw(luma::draw_context&, luma::theme const&) const -> void override; auto draw(luma::draw_context &, luma::theme const &) const -> void override;
private: private:
luma::size value_; luma::size value_;
}; };
@@ -116,8 +116,8 @@ private:
class progress_bar_control final : public luma::control { class progress_bar_control final : public luma::control {
public: public:
explicit progress_bar_control(std::float32_t value); explicit progress_bar_control(std::float32_t value);
auto measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
private: private:
std::float32_t value_; std::float32_t value_;
}; };
@@ -125,8 +125,8 @@ private:
class image_control final : public luma::control { class image_control final : public luma::control {
public: public:
image_control(std::shared_ptr<luma::texture> texture, luma::image_fit fit); image_control(std::shared_ptr<luma::texture> texture, luma::image_fit fit);
auto measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
private: private:
std::shared_ptr<luma::texture> texture_; std::shared_ptr<luma::texture> texture_;
luma::image_fit fit_; luma::image_fit fit_;
@@ -135,10 +135,10 @@ private:
class linear_control final : public luma::control { class linear_control final : public luma::control {
public: public:
linear_control(luma::direction direction, std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap, luma::alignment align); linear_control(luma::direction direction, std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap, luma::alignment align);
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto arrange(luma::rect bounds) -> void override; auto arrange(luma::rect bounds) -> void override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto invalidate_layout() -> void override; auto invalidate_layout() -> void override;
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override; auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
private: private:
@@ -152,10 +152,10 @@ private:
class stack_control final : public luma::control { class stack_control final : public luma::control {
public: public:
explicit stack_control(std::vector<std::unique_ptr<luma::control>> children); explicit stack_control(std::vector<std::unique_ptr<luma::control>> children);
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto arrange(luma::rect bounds) -> void override; auto arrange(luma::rect bounds) -> void override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto invalidate_layout() -> void override; auto invalidate_layout() -> void override;
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override; auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
private: private:
@@ -165,10 +165,10 @@ private:
class scroll_view_control final : public luma::control { class scroll_view_control final : public luma::control {
public: public:
scroll_view_control(std::unique_ptr<luma::control> child, std::bool_t vertical, std::bool_t horizontal); scroll_view_control(std::unique_ptr<luma::control> child, std::bool_t vertical, std::bool_t horizontal);
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override; auto measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size override;
auto arrange(luma::rect bounds) -> void override; auto arrange(luma::rect bounds) -> void override;
auto draw(luma::draw_context& context, luma::theme const& theme) const -> void override; auto draw(luma::draw_context &context, luma::theme const &theme) const -> void override;
auto handle_touch(luma::touch_event const& event) -> std::bool_t override; auto handle_touch(luma::touch_event const &event) -> std::bool_t override;
auto invalidate_layout() -> void override; auto invalidate_layout() -> void override;
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override; auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
private: private:
@@ -177,7 +177,7 @@ private:
luma::point last{}; luma::point last{};
std::bool_t child_active{false}; std::bool_t child_active{false};
}; };
auto handle_point(luma::touch_event const& event, luma::touch_point const& point) -> std::bool_t; auto handle_point(luma::touch_event const &event, luma::touch_point const &point) -> std::bool_t;
auto invalidate_child_measure() -> void; auto invalidate_child_measure() -> void;
auto arrange_child() -> void; auto arrange_child() -> void;
auto clamp_offset() -> void; auto clamp_offset() -> void;
@@ -202,9 +202,9 @@ auto finite_size(luma::size value) -> std::bool_t {
auto finite_rect(luma::rect value) -> std::bool_t { auto finite_rect(luma::rect value) -> std::bool_t {
return std::isfinite(value.x) && std::isfinite(value.y) && finite_size({ return std::isfinite(value.x) && std::isfinite(value.y) && finite_size({
.width = value.width, .width = value.width,
.height = value.height, .height = value.height,
}); });
} }
auto finite_constraints(luma::constraints value) -> std::bool_t { auto finite_constraints(luma::constraints value) -> std::bool_t {
@@ -219,11 +219,11 @@ auto same_constraints(luma::constraints left, luma::constraints right) -> std::b
return same_size(left.min, right.min) && same_size(left.max, right.max); return same_size(left.min, right.min) && same_size(left.max, right.max);
} }
auto same_text_style(luma::text_style const& left, luma::text_style const& right) -> std::bool_t { auto same_text_style(luma::text_style const &left, luma::text_style const &right) -> std::bool_t {
return left.font == right.font && left.size == right.size && left.line_height == right.line_height; return left.font == right.font && left.size == right.size && left.line_height == right.line_height;
} }
auto same_theme_metrics(luma::theme const& left, luma::theme const& right) -> std::bool_t { auto same_theme_metrics(luma::theme const &left, luma::theme const &right) -> std::bool_t {
return same_text_style(left.body, right.body) && same_text_style(left.title, right.title) && left.radius == right.radius && left.spacing == right.spacing; return same_text_style(left.body, right.body) && same_text_style(left.title, right.title) && left.radius == right.radius && left.spacing == right.spacing;
} }
@@ -232,26 +232,26 @@ auto invalid(std::string message) -> std::expected<VALUE, modulus::error> {
return luma::detail::fail<VALUE>(luma::detail::error_invalid_argument, std::move(message)); return luma::detail::fail<VALUE>(luma::detail::error_invalid_argument, std::move(message));
} }
auto throw_error(modulus::error const& error) -> void { auto throw_error(modulus::error const &error) -> void {
throw std::runtime_error(error.describe()); throw std::runtime_error(error.describe());
} }
auto throw_if_failed(std::expected<void, modulus::error> const& status) -> void { auto throw_if_failed(std::expected<void, modulus::error> const &status) -> void {
if (!status) { if (!status) {
throw_error(status.error()); throw_error(status.error());
} }
} }
auto make_event(luma::touch_event const& event, luma::touch_point point) -> luma::touch_event { auto make_event(luma::touch_event const &event, luma::touch_point point) -> luma::touch_event {
return { return {
.points = {point}, .points = {point},
.time = event.time, .time = event.time,
}; };
} }
auto dispatch_to_children(std::vector<std::unique_ptr<luma::control>>& children, luma::touch_event const& event) -> std::bool_t { auto dispatch_to_children(std::vector<std::unique_ptr<luma::control>> &children, luma::touch_event const &event) -> std::bool_t {
auto handled = false; auto handled = false;
for (auto const& point : event.points) { for (auto const &point : event.points) {
auto single = make_event(event, point); auto single = make_event(event, point);
for (auto current = children.rbegin(); current != children.rend(); ++current) { for (auto current = children.rbegin(); current != children.rend(); ++current) {
assert(*current != nullptr); assert(*current != nullptr);
@@ -264,8 +264,8 @@ auto dispatch_to_children(std::vector<std::unique_ptr<luma::control>>& children,
return handled; return handled;
} }
auto find_point(luma::touch_event const& event, std::int32_t id) -> luma::touch_point const* { auto find_point(luma::touch_event const &event, std::int32_t id) -> luma::touch_point const * {
for (auto const& point : event.points) { for (auto const &point : event.points) {
if (point.id == id) { if (point.id == id) {
return &point; return &point;
} }
@@ -273,8 +273,8 @@ auto find_point(luma::touch_event const& event, std::int32_t id) -> luma::touch_
return nullptr; return nullptr;
} }
auto first_point_with_phase(luma::touch_event const& event, luma::touch_phase phase) -> luma::touch_point const* { auto first_point_with_phase(luma::touch_event const &event, luma::touch_phase phase) -> luma::touch_point const * {
for (auto const& point : event.points) { for (auto const &point : event.points) {
if (point.phase == phase) { if (point.phase == phase) {
return &point; return &point;
} }
@@ -282,8 +282,8 @@ auto first_point_with_phase(luma::touch_event const& event, luma::touch_phase ph
return nullptr; return nullptr;
} }
auto first_point_inside(luma::touch_event const& event, luma::rect bounds, luma::touch_phase phase) -> luma::touch_point const* { auto first_point_inside(luma::touch_event const &event, luma::rect bounds, luma::touch_phase phase) -> luma::touch_point const * {
for (auto const& point : event.points) { for (auto const &point : event.points) {
if (point.phase == phase && luma::contains(bounds, point.position)) { if (point.phase == phase && luma::contains(bounds, point.position)) {
return &point; return &point;
} }
@@ -291,13 +291,13 @@ auto first_point_inside(luma::touch_event const& event, luma::rect bounds, luma:
return nullptr; return nullptr;
} }
auto valid_keyboard_layout(luma::keyboard_layout const& layout) -> std::expected<void, modulus::error> { auto valid_keyboard_layout(luma::keyboard_layout const &layout) -> std::expected<void, modulus::error> {
if (layout.rows.empty()) { if (layout.rows.empty()) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "keyboard layout must contain at least one row"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "keyboard layout must contain at least one row");
} }
auto has_key = false; auto has_key = false;
for (auto const& row : layout.rows) { for (auto const &row : layout.rows) {
for (auto const& key : row) { for (auto const &key : row) {
has_key = true; has_key = true;
if (key.label.empty()) { if (key.label.empty()) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "keyboard key label must not be empty"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "keyboard key label must not be empty");
@@ -310,8 +310,8 @@ auto valid_keyboard_layout(luma::keyboard_layout const& layout) -> std::expected
return {}; return {};
} }
auto validate_children(std::vector<std::unique_ptr<luma::control>> const& children) -> std::expected<void, modulus::error> { auto validate_children(std::vector<std::unique_ptr<luma::control>> const &children) -> std::expected<void, modulus::error> {
for (auto const& child : children) { for (auto const &child : children) {
if (child == nullptr) { if (child == nullptr) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "child control must not be null"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "child control must not be null");
} }
@@ -319,7 +319,7 @@ auto validate_children(std::vector<std::unique_ptr<luma::control>> const& childr
return {}; return {};
} }
auto centered_text_origin(luma::draw_context& context, std::string const& text, luma::rect bounds, luma::text_style const& style) -> luma::point { auto centered_text_origin(luma::draw_context &context, std::string const &text, luma::rect bounds, luma::text_style const &style) -> luma::point {
assert(finite_rect(bounds)); assert(finite_rect(bounds));
auto measured = context.measure_text(text, style); auto measured = context.measure_text(text, style);
assert(finite_size(measured)); assert(finite_size(measured));
@@ -359,52 +359,56 @@ auto fit_rect(luma::rect bounds, luma::texture_info info, luma::image_fit fit) -
}; };
} }
label_control::label_control(std::string text) : text_{std::move(text)} { label_control::label_control(std::string text)
: text_{std::move(text)} {
assert(!text_.empty()); assert(!text_.empty());
} }
auto label_control::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto label_control::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
auto measured = context.measure_text(text_, theme.body); auto measured = context.measure_text(text_, theme.body);
assert(finite_size(measured)); assert(finite_size(measured));
return luma::clamp(measured, limits); return luma::clamp(measured, limits);
} }
auto label_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto label_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
assert(!text_.empty()); assert(!text_.empty());
context.draw_text(text_, { context.draw_text(text_, {
.x = bounds().x, .x = bounds().x,
.y = bounds().y, .y = bounds().y,
}, theme.body, enabled() ? theme.text : theme.text_muted); },
theme.body, enabled() ? theme.text : theme.text_muted);
} }
button_control::button_control(std::string text, std::function<void()> on_click) : text_{std::move(text)}, on_click_{std::move(on_click)} { button_control::button_control(std::string text, std::function<void()> on_click)
: text_{std::move(text)}, on_click_{std::move(on_click)} {
assert(!text_.empty()); assert(!text_.empty());
assert(on_click_); assert(on_click_);
} }
auto button_control::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto button_control::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
auto text_size = context.measure_text(text_, theme.body); auto text_size = context.measure_text(text_, theme.body);
assert(finite_size(text_size)); assert(finite_size(text_size));
return luma::clamp({ return luma::clamp({
.width = text_size.width + 32.0F, .width = text_size.width + 32.0F,
.height = std::max(std::float32_t{48}, text_size.height + std::float32_t{16}), .height = std::max(std::float32_t{48}, text_size.height + std::float32_t{16}),
}, limits); },
limits);
} }
auto button_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto button_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
auto fill = pressed_id_.has_value() ? theme.surface_active : theme.surface; auto fill = pressed_id_.has_value() ? theme.surface_active : theme.surface;
context.fill_rect(bounds(), fill); context.fill_rect(bounds(), fill);
context.stroke_rect(bounds(), 1.0F, focused() ? theme.accent : theme.border); context.stroke_rect(bounds(), 1.0F, focused() ? theme.accent : theme.border);
context.draw_text(text_, centered_text_origin(context, text_, bounds(), theme.body), theme.body, enabled() ? theme.text : theme.text_muted); context.draw_text(text_, centered_text_origin(context, text_, bounds(), theme.body), theme.body, enabled() ? theme.text : theme.text_muted);
} }
auto button_control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto button_control::handle_touch(luma::touch_event const &event) -> std::bool_t {
if (!enabled()) { if (!enabled()) {
return false; return false;
} }
auto const* point = pressed_id_ ? find_point(event, *pressed_id_) : first_point_inside(event, bounds(), luma::touch_phase::DOWN); auto const *point = pressed_id_ ? find_point(event, *pressed_id_) : first_point_inside(event, bounds(), luma::touch_phase::DOWN);
if (point == nullptr) { if (point == nullptr) {
return false; return false;
} }
@@ -434,11 +438,12 @@ auto button_control::focusable() const -> std::bool_t {
return true; return true;
} }
text_field_control::text_field_control(std::shared_ptr<luma::text_input_state> input) : input_{std::move(input)} { text_field_control::text_field_control(std::shared_ptr<luma::text_input_state> input)
: input_{std::move(input)} {
assert(input_ != nullptr); assert(input_ != nullptr);
} }
auto text_field_control::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto text_field_control::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
auto version = input_->version(); auto version = input_->version();
if (cached_limits_ && cached_style_ && same_constraints(*cached_limits_, limits) && same_text_style(*cached_style_, theme.body) && cached_version_ == version) { if (cached_limits_ && cached_style_ && same_constraints(*cached_limits_, limits) && same_text_style(*cached_style_, theme.body) && cached_version_ == version) {
@@ -451,13 +456,14 @@ auto text_field_control::measure(luma::draw_context& context, luma::theme const&
cached_style_ = theme.body; cached_style_ = theme.body;
cached_version_ = version; cached_version_ = version;
cached_size_ = luma::clamp({ cached_size_ = luma::clamp({
.width = std::max(std::float32_t{240}, measured.width + std::float32_t{24}), .width = std::max(std::float32_t{240}, measured.width + std::float32_t{24}),
.height = std::max(std::float32_t{48}, measured.height + std::float32_t{16}), .height = std::max(std::float32_t{48}, measured.height + std::float32_t{16}),
}, limits); },
limits);
return cached_size_; return cached_size_;
} }
auto text_field_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto text_field_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
context.fill_rect(bounds(), theme.surface); context.fill_rect(bounds(), theme.surface);
context.stroke_rect(bounds(), 1.0F, input_->focused() ? theme.accent : theme.border); context.stroke_rect(bounds(), 1.0F, input_->focused() ? theme.accent : theme.border);
auto options = input_->snapshot(); auto options = input_->snapshot();
@@ -468,16 +474,17 @@ auto text_field_control::draw(luma::draw_context& context, luma::theme const& th
text = std::string(options.text.size(), '*'); text = std::string(options.text.size(), '*');
} }
context.draw_text(text, { context.draw_text(text, {
.x = bounds().x + 12.0F, .x = bounds().x + 12.0F,
.y = bounds().y + 8.0F, .y = bounds().y + 8.0F,
}, theme.body, options.text.empty() ? theme.text_muted : theme.text); },
theme.body, options.text.empty() ? theme.text_muted : theme.text);
} }
auto text_field_control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto text_field_control::handle_touch(luma::touch_event const &event) -> std::bool_t {
if (!enabled()) { if (!enabled()) {
return false; return false;
} }
auto const* point = first_point_inside(event, bounds(), luma::touch_phase::DOWN); auto const *point = first_point_inside(event, bounds(), luma::touch_phase::DOWN);
if (point == nullptr) { if (point == nullptr) {
point = first_point_with_phase(event, luma::touch_phase::DOWN); point = first_point_with_phase(event, luma::touch_phase::DOWN);
} }
@@ -513,28 +520,31 @@ auto text_field_control::focusable() const -> std::bool_t {
return true; return true;
} }
soft_keyboard_control::soft_keyboard_control(luma::keyboard_layout layout, std::function<void(std::string)> on_text, std::function<void()> on_enter) : layout_{std::move(layout)}, on_text_{std::move(on_text)}, on_enter_{std::move(on_enter)} { soft_keyboard_control::soft_keyboard_control(luma::keyboard_layout layout, std::function<void(std::string)> on_text, std::function<void()> on_enter)
: layout_{std::move(layout)}, on_text_{std::move(on_text)}, on_enter_{std::move(on_enter)} {
assert(!layout_.rows.empty()); assert(!layout_.rows.empty());
assert(on_text_); assert(on_text_);
} }
soft_keyboard_control::soft_keyboard_control(luma::keyboard_layout layout, std::shared_ptr<luma::text_input_state> input, std::function<void()> on_enter) : layout_{std::move(layout)}, on_enter_{std::move(on_enter)}, input_{std::move(input)} { soft_keyboard_control::soft_keyboard_control(luma::keyboard_layout layout, std::shared_ptr<luma::text_input_state> input, std::function<void()> on_enter)
: layout_{std::move(layout)}, on_enter_{std::move(on_enter)}, input_{std::move(input)} {
assert(!layout_.rows.empty()); assert(!layout_.rows.empty());
assert(input_ != nullptr); assert(input_ != nullptr);
} }
auto soft_keyboard_control::measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size { auto soft_keyboard_control::measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
return luma::clamp({ return luma::clamp({
.width = 640.0F, .width = 640.0F,
.height = static_cast<std::float32_t>(layout_.rows.size()) * 56.0F, .height = static_cast<std::float32_t>(layout_.rows.size()) * 56.0F,
}, limits); },
limits);
} }
auto soft_keyboard_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto soft_keyboard_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
auto key_height = bounds().height / static_cast<std::float32_t>(layout_.rows.size()); auto key_height = bounds().height / static_cast<std::float32_t>(layout_.rows.size());
for (auto row_index = std::size_t{}; row_index < layout_.rows.size(); ++row_index) { for (auto row_index = std::size_t{}; row_index < layout_.rows.size(); ++row_index) {
auto const& row = layout_.rows[row_index]; auto const &row = layout_.rows[row_index];
if (row.empty()) { if (row.empty()) {
continue; continue;
} }
@@ -549,27 +559,28 @@ auto soft_keyboard_control::draw(luma::draw_context& context, luma::theme const&
context.fill_rect(rect, theme.surface); context.fill_rect(rect, theme.surface);
context.stroke_rect(rect, 1.0F, theme.border); context.stroke_rect(rect, 1.0F, theme.border);
context.draw_text(row[column_index].label, { context.draw_text(row[column_index].label, {
.x = rect.x + 12.0F, .x = rect.x + 12.0F,
.y = rect.y + 10.0F, .y = rect.y + 10.0F,
}, theme.body, theme.text); },
theme.body, theme.text);
} }
} }
} }
auto soft_keyboard_control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto soft_keyboard_control::handle_touch(luma::touch_event const &event) -> std::bool_t {
auto const* point = first_point_inside(event, bounds(), luma::touch_phase::UP); auto const *point = first_point_inside(event, bounds(), luma::touch_phase::UP);
if (point == nullptr) { if (point == nullptr) {
return false; return false;
} }
auto row_count = layout_.rows.size(); auto row_count = layout_.rows.size();
assert(row_count > 0); assert(row_count > 0);
auto row_index = std::min(row_count - 1, static_cast<std::size_t>((point->position.y - bounds().y) / (bounds().height / static_cast<std::float32_t>(row_count)))); auto row_index = std::min(row_count - 1, static_cast<std::size_t>((point->position.y - bounds().y) / (bounds().height / static_cast<std::float32_t>(row_count))));
auto const& row = layout_.rows[row_index]; auto const &row = layout_.rows[row_index];
if (row.empty()) { if (row.empty()) {
return true; return true;
} }
auto column_index = std::min(row.size() - 1, static_cast<std::size_t>((point->position.x - bounds().x) / (bounds().width / static_cast<std::float32_t>(row.size())))); auto column_index = std::min(row.size() - 1, static_cast<std::size_t>((point->position.x - bounds().x) / (bounds().width / static_cast<std::float32_t>(row.size()))));
auto const& key = row[column_index]; auto const &key = row[column_index];
switch (key.kind) { switch (key.kind) {
case luma::keyboard_key_kind::CHARACTER: case luma::keyboard_key_kind::CHARACTER:
if (input_) { if (input_) {
@@ -604,13 +615,14 @@ auto soft_keyboard_control::handle_touch(luma::touch_event const& event) -> std:
return true; return true;
} }
panel_control::panel_control(std::vector<std::unique_ptr<luma::control>> children) : children_{std::move(children)} {} panel_control::panel_control(std::vector<std::unique_ptr<luma::control>> children)
: children_{std::move(children)} {}
auto panel_control::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto panel_control::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
auto child_limits = luma::deflate(limits, panel_padding); auto child_limits = luma::deflate(limits, panel_padding);
auto result = child_limits.min; auto result = child_limits.min;
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
auto measured = child->measure(context, theme, child_limits); auto measured = child->measure(context, theme, child_limits);
result.width = std::max(result.width, measured.width); result.width = std::max(result.width, measured.width);
@@ -625,26 +637,26 @@ auto panel_control::arrange(luma::rect bounds) -> void {
assert(finite_rect(bounds)); assert(finite_rect(bounds));
luma::control::arrange(bounds); luma::control::arrange(bounds);
auto content = luma::inset(bounds, panel_padding); auto content = luma::inset(bounds, panel_padding);
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->arrange(content); child->arrange(content);
} }
} }
auto panel_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto panel_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
context.fill_rect(bounds(), theme.surface); context.fill_rect(bounds(), theme.surface);
for (auto const& child : children_) { for (auto const &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->draw(context, theme); child->draw(context, theme);
} }
} }
auto panel_control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto panel_control::handle_touch(luma::touch_event const &event) -> std::bool_t {
return dispatch_to_children(children_, event); return dispatch_to_children(children_, event);
} }
auto panel_control::invalidate_layout() -> void { auto panel_control::invalidate_layout() -> void {
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->invalidate_layout(); child->invalidate_layout();
} }
@@ -652,62 +664,68 @@ auto panel_control::invalidate_layout() -> void {
auto panel_control::bind_layout_invalidator(std::function<void()> invalidator) -> void { auto panel_control::bind_layout_invalidator(std::function<void()> invalidator) -> void {
luma::control::bind_layout_invalidator(invalidator); luma::control::bind_layout_invalidator(invalidator);
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->bind_layout_invalidator(invalidator); child->bind_layout_invalidator(invalidator);
} }
} }
spacer_control::spacer_control(luma::size value) : value_{value} { spacer_control::spacer_control(luma::size value)
: value_{value} {
assert(finite_size(value_)); assert(finite_size(value_));
} }
auto spacer_control::measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size { auto spacer_control::measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
return luma::clamp(value_, limits); return luma::clamp(value_, limits);
} }
auto spacer_control::draw(luma::draw_context&, luma::theme const&) const -> void {} auto spacer_control::draw(luma::draw_context &, luma::theme const &) const -> void {}
progress_bar_control::progress_bar_control(std::float32_t value) : value_{std::clamp(value, std::float32_t{}, std::float32_t{1})} { progress_bar_control::progress_bar_control(std::float32_t value)
: value_{std::clamp(value, std::float32_t{}, std::float32_t{1})} {
assert(std::isfinite(value)); assert(std::isfinite(value));
} }
auto progress_bar_control::measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size { auto progress_bar_control::measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
return luma::clamp({ return luma::clamp({
.width = 160.0F, .width = 160.0F,
.height = 16.0F, .height = 16.0F,
}, limits); },
limits);
} }
auto progress_bar_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto progress_bar_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
context.fill_rect(bounds(), theme.surface); context.fill_rect(bounds(), theme.surface);
context.fill_rect({ context.fill_rect({
.x = bounds().x, .x = bounds().x,
.y = bounds().y, .y = bounds().y,
.width = bounds().width * value_, .width = bounds().width * value_,
.height = bounds().height, .height = bounds().height,
}, theme.accent); },
theme.accent);
} }
image_control::image_control(std::shared_ptr<luma::texture> texture, luma::image_fit fit) : texture_{std::move(texture)}, fit_{fit} { image_control::image_control(std::shared_ptr<luma::texture> texture, luma::image_fit fit)
: texture_{std::move(texture)}, fit_{fit} {
assert(texture_ != nullptr); assert(texture_ != nullptr);
} }
auto image_control::measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size { auto image_control::measure(luma::draw_context &, luma::theme const &, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
auto info = texture_->info(); auto info = texture_->info();
if (info.width <= 0 || info.height <= 0) { if (info.width <= 0 || info.height <= 0) {
return luma::clamp({}, limits); return luma::clamp({}, limits);
} }
return luma::clamp({ return luma::clamp({
.width = static_cast<std::float32_t>(info.width), .width = static_cast<std::float32_t>(info.width),
.height = static_cast<std::float32_t>(info.height), .height = static_cast<std::float32_t>(info.height),
}, limits); },
limits);
} }
auto image_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto image_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
auto info = texture_->info(); auto info = texture_->info();
if (info.width <= 0 || info.height <= 0) { if (info.width <= 0 || info.height <= 0) {
return; return;
@@ -721,18 +739,19 @@ auto image_control::draw(luma::draw_context& context, luma::theme const& theme)
context.draw_texture(*texture_, source, fit_rect(bounds(), info, fit_), theme.text); context.draw_texture(*texture_, source, fit_rect(bounds(), info, fit_), theme.text);
} }
linear_control::linear_control(luma::direction direction, std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap, luma::alignment align) : direction_{direction}, children_{std::move(children)}, gap_{gap}, align_{align} { linear_control::linear_control(luma::direction direction, std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap, luma::alignment align)
: direction_{direction}, children_{std::move(children)}, gap_{gap}, align_{align} {
assert(std::isfinite(gap_)); assert(std::isfinite(gap_));
assert(gap_ >= 0.0F); assert(gap_ >= 0.0F);
} }
auto linear_control::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto linear_control::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
measured_.clear(); measured_.clear();
measured_.reserve(children_.size()); measured_.reserve(children_.size());
auto result = luma::size{}; auto result = luma::size{};
for (auto index = std::size_t{}; index < children_.size(); ++index) { for (auto index = std::size_t{}; index < children_.size(); ++index) {
auto& child = children_[index]; auto &child = children_[index];
assert(child != nullptr); assert(child != nullptr);
auto child_limits = limits; auto child_limits = limits;
if (direction_ == luma::direction::ROW) { if (direction_ == luma::direction::ROW) {
@@ -762,7 +781,7 @@ auto linear_control::arrange(luma::rect bounds) -> void {
luma::control::arrange(bounds); luma::control::arrange(bounds);
auto cursor = direction_ == luma::direction::ROW ? bounds.x : bounds.y; auto cursor = direction_ == luma::direction::ROW ? bounds.x : bounds.y;
for (auto index = std::size_t{}; index < children_.size(); ++index) { for (auto index = std::size_t{}; index < children_.size(); ++index) {
auto& child = children_[index]; auto &child = children_[index];
assert(child != nullptr); assert(child != nullptr);
auto measured = index < measured_.size() ? measured_[index] : luma::size{}; auto measured = index < measured_.size() ? measured_[index] : luma::size{};
auto child_bounds = bounds; auto child_bounds = bounds;
@@ -791,20 +810,20 @@ auto linear_control::arrange(luma::rect bounds) -> void {
} }
} }
auto linear_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto linear_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
for (auto const& child : children_) { for (auto const &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->draw(context, theme); child->draw(context, theme);
} }
} }
auto linear_control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto linear_control::handle_touch(luma::touch_event const &event) -> std::bool_t {
return dispatch_to_children(children_, event); return dispatch_to_children(children_, event);
} }
auto linear_control::invalidate_layout() -> void { auto linear_control::invalidate_layout() -> void {
measured_.clear(); measured_.clear();
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->invalidate_layout(); child->invalidate_layout();
} }
@@ -812,19 +831,19 @@ auto linear_control::invalidate_layout() -> void {
auto linear_control::bind_layout_invalidator(std::function<void()> invalidator) -> void { auto linear_control::bind_layout_invalidator(std::function<void()> invalidator) -> void {
luma::control::bind_layout_invalidator(invalidator); luma::control::bind_layout_invalidator(invalidator);
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->bind_layout_invalidator(invalidator); child->bind_layout_invalidator(invalidator);
} }
} }
stack_control::stack_control(std::vector<std::unique_ptr<luma::control>> children)
: children_{std::move(children)} {}
stack_control::stack_control(std::vector<std::unique_ptr<luma::control>> children) : children_{std::move(children)} {} auto stack_control::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
auto stack_control::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
auto result = limits.min; auto result = limits.min;
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
auto measured = child->measure(context, theme, limits); auto measured = child->measure(context, theme, limits);
result.width = std::max(result.width, measured.width); result.width = std::max(result.width, measured.width);
@@ -836,25 +855,25 @@ auto stack_control::measure(luma::draw_context& context, luma::theme const& them
auto stack_control::arrange(luma::rect bounds) -> void { auto stack_control::arrange(luma::rect bounds) -> void {
assert(finite_rect(bounds)); assert(finite_rect(bounds));
luma::control::arrange(bounds); luma::control::arrange(bounds);
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->arrange(bounds); child->arrange(bounds);
} }
} }
auto stack_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto stack_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
for (auto const& child : children_) { for (auto const &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->draw(context, theme); child->draw(context, theme);
} }
} }
auto stack_control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto stack_control::handle_touch(luma::touch_event const &event) -> std::bool_t {
return dispatch_to_children(children_, event); return dispatch_to_children(children_, event);
} }
auto stack_control::invalidate_layout() -> void { auto stack_control::invalidate_layout() -> void {
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->invalidate_layout(); child->invalidate_layout();
} }
@@ -862,19 +881,19 @@ auto stack_control::invalidate_layout() -> void {
auto stack_control::bind_layout_invalidator(std::function<void()> invalidator) -> void { auto stack_control::bind_layout_invalidator(std::function<void()> invalidator) -> void {
luma::control::bind_layout_invalidator(invalidator); luma::control::bind_layout_invalidator(invalidator);
for (auto& child : children_) { for (auto &child : children_) {
assert(child != nullptr); assert(child != nullptr);
child->bind_layout_invalidator(invalidator); child->bind_layout_invalidator(invalidator);
} }
} }
scroll_view_control::scroll_view_control(std::unique_ptr<luma::control> child, std::bool_t vertical, std::bool_t horizontal)
scroll_view_control::scroll_view_control(std::unique_ptr<luma::control> child, std::bool_t vertical, std::bool_t horizontal) : child_{std::move(child)}, vertical_{vertical}, horizontal_{horizontal} { : child_{std::move(child)}, vertical_{vertical}, horizontal_{horizontal} {
assert(child_ != nullptr); assert(child_ != nullptr);
assert(vertical_ || horizontal_); assert(vertical_ || horizontal_);
} }
auto scroll_view_control::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto scroll_view_control::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
assert(finite_constraints(limits)); assert(finite_constraints(limits));
auto max = std::numeric_limits<std::float32_t>::max(); auto max = std::numeric_limits<std::float32_t>::max();
auto child_limits = luma::constraints{ auto child_limits = luma::constraints{
@@ -903,21 +922,21 @@ auto scroll_view_control::arrange(luma::rect bounds) -> void {
arrange_child(); arrange_child();
} }
auto scroll_view_control::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto scroll_view_control::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
context.push_clip(bounds()); context.push_clip(bounds());
child_->draw(context, theme); child_->draw(context, theme);
context.pop_clip(); context.pop_clip();
} }
auto scroll_view_control::handle_touch(luma::touch_event const& event) -> std::bool_t { auto scroll_view_control::handle_touch(luma::touch_event const &event) -> std::bool_t {
auto handled = false; auto handled = false;
for (auto const& point : event.points) { for (auto const &point : event.points) {
handled = handle_point(event, point) || handled; handled = handle_point(event, point) || handled;
} }
return handled; return handled;
} }
auto scroll_view_control::handle_point(luma::touch_event const& event, luma::touch_point const& point) -> std::bool_t { auto scroll_view_control::handle_point(luma::touch_event const &event, luma::touch_point const &point) -> std::bool_t {
if (point.phase == luma::touch_phase::DOWN) { if (point.phase == luma::touch_phase::DOWN) {
if (!luma::contains(bounds(), point.position)) { if (!luma::contains(bounds(), point.position)) {
return false; return false;
@@ -938,13 +957,11 @@ auto scroll_view_control::handle_point(luma::touch_event const& event, luma::tou
} }
return true; return true;
} }
auto found = touches_.find(point.id); auto found = touches_.find(point.id);
if (found == touches_.end()) { if (found == touches_.end()) {
return false; return false;
} }
auto& state = found->second; auto &state = found->second;
if (point.phase == luma::touch_phase::MOVE) { if (point.phase == luma::touch_phase::MOVE) {
auto delta = luma::point{ auto delta = luma::point{
.x = point.position.x - state.last.x, .x = point.position.x - state.last.x,
@@ -994,7 +1011,6 @@ auto scroll_view_control::handle_point(luma::touch_event const& event, luma::tou
} }
return true; return true;
} }
if (point.phase == luma::touch_phase::UP || point.phase == luma::touch_phase::CANCEL) { if (point.phase == luma::touch_phase::UP || point.phase == luma::touch_phase::CANCEL) {
auto const scrolling = scroll_id_ == point.id && dragging_; auto const scrolling = scroll_id_ == point.id && dragging_;
if (state.child_active) { if (state.child_active) {
@@ -1015,7 +1031,6 @@ auto scroll_view_control::handle_point(luma::touch_event const& event, luma::tou
touches_.erase(found); touches_.erase(found);
return true; return true;
} }
return true; return true;
} }
@@ -1073,7 +1088,7 @@ auto accepts_text(luma::text_input_mode mode, std::string_view current, std::str
return true; return true;
} }
auto validate_text_options(luma::text_field_options const& options) -> std::expected<void, modulus::error> { auto validate_text_options(luma::text_field_options const &options) -> std::expected<void, modulus::error> {
if (options.max_length == 0) { if (options.max_length == 0) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "text field max length must be positive"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "text field max length must be positive");
} }
@@ -1090,13 +1105,13 @@ auto callback_failure(std::string message) -> std::expected<void, modulus::error
return luma::detail::fail<void>(luma::detail::error_internal, std::move(message)); return luma::detail::fail<void>(luma::detail::error_internal, std::move(message));
} }
auto notify_text_changed(std::function<void(std::string)> const& callback, std::string value) -> std::expected<void, modulus::error> { auto notify_text_changed(std::function<void(std::string)> const &callback, std::string value) -> std::expected<void, modulus::error> {
if (!callback) { if (!callback) {
return {}; return {};
} }
try { try {
callback(std::move(value)); callback(std::move(value));
} catch (std::exception const& error) { } catch (std::exception const &error) {
return callback_failure(std::string{"text change callback failed: "} + error.what()); return callback_failure(std::string{"text change callback failed: "} + error.what());
} catch (...) { } catch (...) {
return callback_failure("text change callback failed with an unknown exception"); return callback_failure("text change callback failed with an unknown exception");
@@ -1104,8 +1119,8 @@ auto notify_text_changed(std::function<void(std::string)> const& callback, std::
return {}; return {};
} }
auto notify_layout_listeners(std::vector<std::function<void()>> const& listeners) -> void { auto notify_layout_listeners(std::vector<std::function<void()>> const &listeners) -> void {
for (auto const& listener : listeners) { for (auto const &listener : listeners) {
if (!listener) { if (!listener) {
continue; continue;
} }
@@ -1116,7 +1131,7 @@ auto notify_layout_listeners(std::vector<std::function<void()>> const& listeners
} }
} }
auto collect_layout_listeners(std::vector<luma::text_input_state::layout_listener>& listeners) -> std::vector<std::function<void()>> { auto collect_layout_listeners(std::vector<luma::text_input_state::layout_listener> &listeners) -> std::vector<std::function<void()>> {
auto callbacks = std::vector<std::function<void()>>{}; auto callbacks = std::vector<std::function<void()>>{};
auto write = listeners.begin(); auto write = listeners.begin();
for (auto read = listeners.begin(); read != listeners.end(); ++read) { for (auto read = listeners.begin(); read != listeners.end(); ++read) {
@@ -1135,7 +1150,8 @@ auto collect_layout_listeners(std::vector<luma::text_input_state::layout_listene
} }
luma::text_input_state::text_input_state(luma::text_field_options options, std::function<void(std::string)> on_change) : options_{std::move(options)}, on_change_{std::move(on_change)} { luma::text_input_state::text_input_state(luma::text_field_options options, std::function<void(std::string)> on_change)
: options_{std::move(options)}, on_change_{std::move(on_change)} {
assert(options_.max_length > 0); assert(options_.max_length > 0);
assert(on_change_); assert(on_change_);
} }
+1
View File
@@ -5,6 +5,7 @@
#include <modulus.hpp> #include <modulus.hpp>
auto luma::detail::make_error(std::int32_t code, std::string message, modulus::source_location location) -> modulus::error { auto luma::detail::make_error(std::int32_t code, std::string message, modulus::source_location location) -> modulus::error {
return modulus::make_error("luma", code, std::move(message), location); return modulus::make_error("luma", code, std::move(message), location);
} }
+43 -37
View File
@@ -17,13 +17,14 @@
#include <luma/render.hpp> #include <luma/render.hpp>
#include <luma/theme.hpp> #include <luma/theme.hpp>
namespace { namespace {
class scope_exit { class scope_exit {
public: public:
explicit scope_exit(std::function<void()> action); explicit scope_exit(std::function<void()> action);
scope_exit(scope_exit const&) = delete; scope_exit(scope_exit const &) = delete;
auto operator=(scope_exit const&) -> scope_exit& = delete; auto operator=(scope_exit const &) -> scope_exit & = delete;
~scope_exit(); ~scope_exit();
private: private:
std::function<void()> action_{}; std::function<void()> action_{};
@@ -53,11 +54,11 @@ auto dialog_bounds(luma::rect bounds) -> luma::rect {
auto dialog_body_bounds(luma::rect bounds) -> luma::rect { auto dialog_body_bounds(luma::rect bounds) -> luma::rect {
return luma::inset(dialog_bounds(bounds), { return luma::inset(dialog_bounds(bounds), {
16.0F, 16.0F,
56.0F, 56.0F,
16.0F, 16.0F,
72.0F, 72.0F,
}); });
} }
auto accept_bounds(luma::rect bounds, std::bool_t has_cancel) -> luma::rect { auto accept_bounds(luma::rect bounds, std::bool_t has_cancel) -> luma::rect {
@@ -82,18 +83,19 @@ auto cancel_bounds(luma::rect bounds) -> luma::rect {
}; };
} }
auto draw_button(luma::draw_context& context, luma::theme const& theme, luma::rect bounds, std::string const& text, luma::color fill) -> void { auto draw_button(luma::draw_context &context, luma::theme const &theme, luma::rect bounds, std::string const &text, luma::color fill) -> void {
context.fill_rect(bounds, fill); context.fill_rect(bounds, fill);
context.stroke_rect(bounds, 1.0F, theme.border); context.stroke_rect(bounds, 1.0F, theme.border);
auto measured = context.measure_text(text, theme.body); auto measured = context.measure_text(text, theme.body);
context.draw_text(text, { context.draw_text(text, {
.x = bounds.x + (bounds.width - measured.width) * 0.5F, .x = bounds.x + (bounds.width - measured.width) * 0.5F,
.y = bounds.y + (bounds.height - measured.height) * 0.5F, .y = bounds.y + (bounds.height - measured.height) * 0.5F,
}, theme.body, theme.text); },
theme.body, theme.text);
} }
auto first_point_inside(luma::touch_event const& event, luma::rect bounds, luma::touch_phase phase) -> luma::touch_point const* { auto first_point_inside(luma::touch_event const &event, luma::rect bounds, luma::touch_phase phase) -> luma::touch_point const * {
for (auto const& point : event.points) { for (auto const &point : event.points) {
if (point.phase == phase && luma::contains(bounds, point.position)) { if (point.phase == phase && luma::contains(bounds, point.position)) {
return &point; return &point;
} }
@@ -101,8 +103,8 @@ auto first_point_inside(luma::touch_event const& event, luma::rect bounds, luma:
return nullptr; return nullptr;
} }
auto any_point_inside(luma::touch_event const& event, luma::rect bounds) -> std::bool_t { auto any_point_inside(luma::touch_event const &event, luma::rect bounds) -> std::bool_t {
for (auto const& point : event.points) { for (auto const &point : event.points) {
if (luma::contains(bounds, point.position)) { if (luma::contains(bounds, point.position)) {
return true; return true;
} }
@@ -110,13 +112,14 @@ auto any_point_inside(luma::touch_event const& event, luma::rect bounds) -> std:
return false; return false;
} }
auto throw_if_failed(std::expected<void, modulus::error> const& status) -> void { auto throw_if_failed(std::expected<void, modulus::error> const &status) -> void {
if (!status) { if (!status) {
throw std::runtime_error(status.error().describe()); throw std::runtime_error(status.error().describe());
} }
} }
scope_exit::scope_exit(std::function<void()> action) : action_{std::move(action)} {} scope_exit::scope_exit(std::function<void()> action)
: action_{std::move(action)} {}
scope_exit::~scope_exit() { scope_exit::~scope_exit() {
if (action_) { if (action_) {
@@ -126,7 +129,8 @@ scope_exit::~scope_exit() {
} }
luma::overlay_host::overlay_host(std::unique_ptr<luma::control> content) : content_{std::move(content)} { luma::overlay_host::overlay_host(std::unique_ptr<luma::control> content)
: content_{std::move(content)} {
assert(content_ != nullptr); assert(content_ != nullptr);
} }
@@ -163,7 +167,7 @@ auto luma::overlay_host::close_dialog(luma::dialog_result result) -> std::expect
if (callback) { if (callback) {
try { try {
callback(result); callback(result);
} catch (std::exception const& error) { } catch (std::exception const &error) {
return luma::detail::fail<void>(luma::detail::error_internal, std::string{"dialog close callback failed: "} + error.what()); return luma::detail::fail<void>(luma::detail::error_internal, std::string{"dialog close callback failed: "} + error.what());
} catch (...) { } catch (...) {
return luma::detail::fail<void>(luma::detail::error_internal, "dialog close callback failed with an unknown exception"); return luma::detail::fail<void>(luma::detail::error_internal, "dialog close callback failed with an unknown exception");
@@ -226,7 +230,7 @@ auto luma::overlay_host::tick(std::chrono::steady_clock::duration delta) -> std:
return false; return false;
} }
auto luma::overlay_host::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto luma::overlay_host::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
assert(content_ != nullptr); assert(content_ != nullptr);
begin_traversal(); begin_traversal();
auto guard = scope_exit{[this] { auto guard = scope_exit{[this] {
@@ -234,14 +238,14 @@ auto luma::overlay_host::measure(luma::draw_context& context, luma::theme const&
}}; }};
auto measured = content_->measure(context, theme, limits); auto measured = content_->measure(context, theme, limits);
if (dialog_ && dialog_->body) { if (dialog_ && dialog_->body) {
auto* body = dialog_->body.get(); auto *body = dialog_->body.get();
body->measure(context, theme, { body->measure(context, theme, {
.min = {}, .min = {},
.max = { .max = {
.width = limits.max.width * 0.8F, .width = limits.max.width * 0.8F,
.height = limits.max.height * 0.8F, .height = limits.max.height * 0.8F,
}, },
}); });
} }
return measured; return measured;
} }
@@ -255,12 +259,12 @@ auto luma::overlay_host::arrange(luma::rect bounds) -> void {
luma::control::arrange(bounds); luma::control::arrange(bounds);
content_->arrange(bounds); content_->arrange(bounds);
if (dialog_ && dialog_->body) { if (dialog_ && dialog_->body) {
auto* body = dialog_->body.get(); auto *body = dialog_->body.get();
body->arrange(dialog_body_bounds(bounds)); body->arrange(dialog_body_bounds(bounds));
} }
} }
auto luma::overlay_host::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto luma::overlay_host::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
assert(content_ != nullptr); assert(content_ != nullptr);
begin_traversal(); begin_traversal();
auto guard = scope_exit{[this] { auto guard = scope_exit{[this] {
@@ -272,13 +276,14 @@ auto luma::overlay_host::draw(luma::draw_context& context, luma::theme const& th
auto title = dialog_->title; auto title = dialog_->title;
auto accept_text = dialog_->accept_text; auto accept_text = dialog_->accept_text;
auto cancel_text = dialog_->cancel_text; auto cancel_text = dialog_->cancel_text;
auto* body = dialog_->body.get(); auto *body = dialog_->body.get();
context.fill_rect(dialog, theme.surface); context.fill_rect(dialog, theme.surface);
context.stroke_rect(dialog, 2.0F, theme.accent); context.stroke_rect(dialog, 2.0F, theme.accent);
context.draw_text(title, { context.draw_text(title, {
.x = dialog.x + 16.0F, .x = dialog.x + 16.0F,
.y = dialog.y + 16.0F, .y = dialog.y + 16.0F,
}, theme.title, theme.text); },
theme.title, theme.text);
body->draw(context, theme); body->draw(context, theme);
auto has_cancel = !cancel_text.empty(); auto has_cancel = !cancel_text.empty();
if (has_cancel) { if (has_cancel) {
@@ -295,13 +300,14 @@ auto luma::overlay_host::draw(luma::draw_context& context, luma::theme const& th
}; };
context.fill_rect(toast_bounds, theme.surface_active); context.fill_rect(toast_bounds, theme.surface_active);
context.draw_text(toasts_.front().message, { context.draw_text(toasts_.front().message, {
.x = toast_bounds.x + 16.0F, .x = toast_bounds.x + 16.0F,
.y = toast_bounds.y + 10.0F, .y = toast_bounds.y + 10.0F,
}, theme.body, theme.text); },
theme.body, theme.text);
} }
} }
auto luma::overlay_host::handle_touch(luma::touch_event const& event) -> std::bool_t { auto luma::overlay_host::handle_touch(luma::touch_event const &event) -> std::bool_t {
assert(content_ != nullptr); assert(content_ != nullptr);
begin_traversal(); begin_traversal();
auto guard = scope_exit{[this] { auto guard = scope_exit{[this] {
+7 -6
View File
@@ -10,6 +10,7 @@
#include <luma/detail/errors.hpp> #include <luma/detail/errors.hpp>
auto luma::router::register_route(std::string name, std::function<std::expected<std::unique_ptr<luma::control>, modulus::error>()> factory) -> std::expected<void, modulus::error> { auto luma::router::register_route(std::string name, std::function<std::expected<std::unique_ptr<luma::control>, modulus::error>()> factory) -> std::expected<void, modulus::error> {
if (name.empty()) { if (name.empty()) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "route name must not be empty"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "route name must not be empty");
@@ -25,7 +26,7 @@ auto luma::router::set_retire_handler(std::function<void(std::unique_ptr<luma::c
retire_handler_ = std::move(handler); retire_handler_ = std::move(handler);
} }
auto luma::router::replace(std::string const& name) -> std::expected<void, modulus::error> { auto luma::router::replace(std::string const &name) -> std::expected<void, modulus::error> {
if (name.empty()) { if (name.empty()) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "route name must not be empty"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "route name must not be empty");
} }
@@ -50,7 +51,7 @@ auto luma::router::replace(std::string const& name) -> std::expected<void, modul
return {}; return {};
} }
auto luma::router::push(std::string const& name) -> std::expected<void, modulus::error> { auto luma::router::push(std::string const &name) -> std::expected<void, modulus::error> {
if (name.empty()) { if (name.empty()) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "route name must not be empty"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, "route name must not be empty");
} }
@@ -90,7 +91,7 @@ auto luma::router::clear_stack() -> void {
retire_stack(); retire_stack();
} }
auto luma::router::current() -> luma::control* { auto luma::router::current() -> luma::control * {
if (stack_.empty()) { if (stack_.empty()) {
return nullptr; return nullptr;
} }
@@ -98,7 +99,7 @@ auto luma::router::current() -> luma::control* {
return stack_.back().view.get(); return stack_.back().view.get();
} }
auto luma::router::current() const -> luma::control const* { auto luma::router::current() const -> luma::control const * {
if (stack_.empty()) { if (stack_.empty()) {
return nullptr; return nullptr;
} }
@@ -106,7 +107,7 @@ auto luma::router::current() const -> luma::control const* {
return stack_.back().view.get(); return stack_.back().view.get();
} }
auto luma::router::current_route() const -> std::string const& { auto luma::router::current_route() const -> std::string const & {
if (stack_.empty()) { if (stack_.empty()) {
return empty_route_; return empty_route_;
} }
@@ -125,7 +126,7 @@ auto luma::router::retire(std::unique_ptr<luma::control> view) -> void {
auto luma::router::retire_stack() -> void { auto luma::router::retire_stack() -> void {
auto removed = std::vector<luma::router::entry>{}; auto removed = std::vector<luma::router::entry>{};
removed.swap(stack_); removed.swap(stack_);
for (auto& entry : removed) { for (auto &entry : removed) {
retire(std::move(entry.view)); retire(std::move(entry.view));
} }
} }
+53 -50
View File
@@ -16,13 +16,13 @@
#include <luma/detail/errors.hpp> #include <luma/detail/errors.hpp>
namespace { namespace {
constexpr auto json_options = modulus::json_config::options{ constexpr auto json_options = modulus::json_config::options{
.category = "luma", .category = "luma",
.invalid_argument_code = luma::detail::error_invalid_argument, .invalid_argument_code = luma::detail::error_invalid_argument,
.not_found_code = luma::detail::error_not_found .not_found_code = luma::detail::error_not_found};
};
auto invalid_color(std::string message) -> std::expected<luma::color, modulus::error> { auto invalid_color(std::string message) -> std::expected<luma::color, modulus::error> {
assert(!message.empty()); assert(!message.empty());
@@ -39,18 +39,18 @@ auto invalid_status(std::string message) -> std::expected<void, modulus::error>
return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::move(message)); return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::move(message));
} }
auto byte_value(boost::json::value const& value, std::char_t const* name) -> std::expected<std::uint8_t, modulus::error> { auto byte_value(boost::json::value const &value, std::char_t const *name) -> std::expected<std::uint8_t, modulus::error> {
assert(name != nullptr); assert(name != nullptr);
auto const* integer = value.if_int64(); auto const *integer = value.if_int64();
if (integer == nullptr || *integer < 0 || *integer > 255) { if (integer == nullptr || *integer < 0 || *integer > 255) {
return luma::detail::fail<std::uint8_t>(luma::detail::error_invalid_argument, std::string{name} + " must be an integer from 0 to 255"); return luma::detail::fail<std::uint8_t>(luma::detail::error_invalid_argument, std::string{name} + " must be an integer from 0 to 255");
} }
return modulus::success(static_cast<std::uint8_t>(*integer)); return modulus::success(static_cast<std::uint8_t>(*integer));
} }
auto apply_color(boost::json::object const& object, std::char_t const* name, luma::color& target) -> std::expected<void, modulus::error> { auto apply_color(boost::json::object const &object, std::char_t const *name, luma::color &target) -> std::expected<void, modulus::error> {
assert(name != nullptr); assert(name != nullptr);
if (auto const* value = object.if_contains(name)) { if (auto const *value = object.if_contains(name)) {
auto parsed = luma::parse_color(*value); auto parsed = luma::parse_color(*value);
if (!parsed) { if (!parsed) {
return modulus::fail<void>(parsed.error()); return modulus::fail<void>(parsed.error());
@@ -60,38 +60,39 @@ auto apply_color(boost::json::object const& object, std::char_t const* name, lum
return {}; return {};
} }
auto number_value(boost::json::value const& value) -> std::optional<std::float64_t>; auto number_value(boost::json::value const &value) -> std::optional<std::float64_t>;
auto apply_text_style(boost::json::object const& object, std::char_t const* name, luma::text_style& target) -> std::expected<void, modulus::error> { auto apply_text_style(boost::json::object const &object, std::char_t const *name, luma::text_style &target) -> std::expected<void, modulus::error> {
assert(name != nullptr); assert(name != nullptr);
if (auto const* value = object.if_contains(name)) { if (auto const *value = object.if_contains(name)) {
auto style = modulus::json_config::require_object(*value, name, json_options); auto style = modulus::json_config::require_object(*value, name, json_options);
if (!style) { if (!style) {
return modulus::fail<void>(style.error()); return modulus::fail<void>(style.error());
} }
auto unknown = modulus::json_config::reject_unknown_fields(**style, { auto unknown = modulus::json_config::reject_unknown_fields(**style, {
"font", "font",
"size", "size",
"line_height", "line_height",
}, name, json_options); },
name, json_options);
if (!unknown) { if (!unknown) {
return modulus::fail<void>(unknown.error()); return modulus::fail<void>(unknown.error());
} }
if (auto const* font = (*style)->if_contains("font")) { if (auto const *font = (*style)->if_contains("font")) {
auto const* text = font->if_string(); auto const *text = font->if_string();
if (text == nullptr) { if (text == nullptr) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::string{name} + ".font must be a string"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::string{name} + ".font must be a string");
} }
target.font = std::string{text->c_str()}; target.font = std::string{text->c_str()};
} }
if (auto const* size = (*style)->if_contains("size")) { if (auto const *size = (*style)->if_contains("size")) {
auto number = number_value(*size); auto number = number_value(*size);
if (!number || *number <= 0.0) { if (!number || *number <= 0.0) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::string{name} + ".size must be positive"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::string{name} + ".size must be positive");
} }
target.size = static_cast<std::float32_t>(*number); target.size = static_cast<std::float32_t>(*number);
} }
if (auto const* line_height = (*style)->if_contains("line_height")) { if (auto const *line_height = (*style)->if_contains("line_height")) {
auto number = number_value(*line_height); auto number = number_value(*line_height);
if (!number || *number <= 0.0) { if (!number || *number <= 0.0) {
return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::string{name} + ".line_height must be positive"); return luma::detail::fail<void>(luma::detail::error_invalid_argument, std::string{name} + ".line_height must be positive");
@@ -102,11 +103,11 @@ auto apply_text_style(boost::json::object const& object, std::char_t const* name
return {}; return {};
} }
auto number_value(boost::json::value const& value) -> std::optional<std::float64_t> { auto number_value(boost::json::value const &value) -> std::optional<std::float64_t> {
if (auto const* number = value.if_double()) { if (auto const *number = value.if_double()) {
return *number; return *number;
} }
if (auto const* integer = value.if_int64()) { if (auto const *integer = value.if_int64()) {
return static_cast<std::float64_t>(*integer); return static_cast<std::float64_t>(*integer);
} }
return std::nullopt; return std::nullopt;
@@ -118,7 +119,7 @@ auto luma::default_theme() -> luma::theme {
return {}; return {};
} }
auto luma::validate(luma::text_style const& style) -> std::expected<void, modulus::error> { auto luma::validate(luma::text_style const &style) -> std::expected<void, modulus::error> {
if (style.font.empty()) { if (style.font.empty()) {
return invalid_status("text style font must not be empty"); return invalid_status("text style font must not be empty");
} }
@@ -131,7 +132,7 @@ auto luma::validate(luma::text_style const& style) -> std::expected<void, modulu
return {}; return {};
} }
auto luma::validate(luma::theme const& theme) -> std::expected<void, modulus::error> { auto luma::validate(luma::theme const &theme) -> std::expected<void, modulus::error> {
if (auto status = luma::validate(theme.body); !status) { if (auto status = luma::validate(theme.body); !status) {
return modulus::fail<void>(status.error()); return modulus::fail<void>(status.error());
} }
@@ -147,23 +148,24 @@ auto luma::validate(luma::theme const& theme) -> std::expected<void, modulus::er
return {}; return {};
} }
auto luma::parse_color(boost::json::value const& value) -> std::expected<luma::color, modulus::error> { auto luma::parse_color(boost::json::value const &value) -> std::expected<luma::color, modulus::error> {
auto object = modulus::json_config::require_object(value, "color", json_options); auto object = modulus::json_config::require_object(value, "color", json_options);
if (!object) { if (!object) {
return invalid_color(std::string{object.error().message()}); return invalid_color(std::string{object.error().message()});
} }
auto unknown = modulus::json_config::reject_unknown_fields(**object, { auto unknown = modulus::json_config::reject_unknown_fields(**object, {
"r", "r",
"g", "g",
"b", "b",
"a", "a",
}, "color", json_options); },
"color", json_options);
if (!unknown) { if (!unknown) {
return invalid_color(std::string{unknown.error().message()}); return invalid_color(std::string{unknown.error().message()});
} }
auto const* red_value = (*object)->if_contains("r"); auto const *red_value = (*object)->if_contains("r");
auto const* green_value = (*object)->if_contains("g"); auto const *green_value = (*object)->if_contains("g");
auto const* blue_value = (*object)->if_contains("b"); auto const *blue_value = (*object)->if_contains("b");
if (red_value == nullptr || green_value == nullptr || blue_value == nullptr) { if (red_value == nullptr || green_value == nullptr || blue_value == nullptr) {
return invalid_color("color requires r, g, and b fields"); return invalid_color("color requires r, g, and b fields");
} }
@@ -180,7 +182,7 @@ auto luma::parse_color(boost::json::value const& value) -> std::expected<luma::c
return invalid_color(std::string{blue.error().message()}); return invalid_color(std::string{blue.error().message()});
} }
auto alpha = std::uint8_t{255}; auto alpha = std::uint8_t{255};
if (auto const* alpha_value = (*object)->if_contains("a")) { if (auto const *alpha_value = (*object)->if_contains("a")) {
auto parsed = byte_value(*alpha_value, "a"); auto parsed = byte_value(*alpha_value, "a");
if (!parsed) { if (!parsed) {
return invalid_color(std::string{parsed.error().message()}); return invalid_color(std::string{parsed.error().message()});
@@ -195,24 +197,25 @@ auto luma::parse_color(boost::json::value const& value) -> std::expected<luma::c
}); });
} }
auto luma::parse_theme(boost::json::value const& value) -> std::expected<luma::theme, modulus::error> { auto luma::parse_theme(boost::json::value const &value) -> std::expected<luma::theme, modulus::error> {
auto object = modulus::json_config::require_object(value, "theme", json_options); auto object = modulus::json_config::require_object(value, "theme", json_options);
if (!object) { if (!object) {
return invalid_theme(std::string{object.error().message()}); return invalid_theme(std::string{object.error().message()});
} }
auto unknown = modulus::json_config::reject_unknown_fields(**object, { auto unknown = modulus::json_config::reject_unknown_fields(**object, {
"background", "background",
"surface", "surface",
"surface_active", "surface_active",
"text", "text",
"text_muted", "text_muted",
"border", "border",
"accent", "accent",
"body", "body",
"title", "title",
"radius", "radius",
"spacing", "spacing",
}, "theme", json_options); },
"theme", json_options);
if (!unknown) { if (!unknown) {
return invalid_theme(std::string{unknown.error().message()}); return invalid_theme(std::string{unknown.error().message()});
} }
@@ -244,14 +247,14 @@ auto luma::parse_theme(boost::json::value const& value) -> std::expected<luma::t
if (auto status = apply_text_style(**object, "title", parsed.title); !status) { if (auto status = apply_text_style(**object, "title", parsed.title); !status) {
return invalid_theme(std::string{status.error().message()}); return invalid_theme(std::string{status.error().message()});
} }
if (auto const* radius = (*object)->if_contains("radius")) { if (auto const *radius = (*object)->if_contains("radius")) {
auto number = number_value(*radius); auto number = number_value(*radius);
if (!number || *number < 0.0) { if (!number || *number < 0.0) {
return invalid_theme("theme.radius must be non-negative"); return invalid_theme("theme.radius must be non-negative");
} }
parsed.radius = static_cast<std::float32_t>(*number); parsed.radius = static_cast<std::float32_t>(*number);
} }
if (auto const* spacing = (*object)->if_contains("spacing")) { if (auto const *spacing = (*object)->if_contains("spacing")) {
auto number = number_value(*spacing); auto number = number_value(*spacing);
if (!number || *number < 0.0) { if (!number || *number < 0.0) {
return invalid_theme("theme.spacing must be non-negative"); return invalid_theme("theme.spacing must be non-negative");
@@ -264,7 +267,7 @@ auto luma::parse_theme(boost::json::value const& value) -> std::expected<luma::t
return modulus::success(parsed); return modulus::success(parsed);
} }
auto luma::to_json(luma::color const& color) -> boost::json::value { auto luma::to_json(luma::color const &color) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"r", "r",
@@ -285,7 +288,7 @@ auto luma::to_json(luma::color const& color) -> boost::json::value {
}; };
} }
auto luma::to_json(luma::text_style const& style) -> boost::json::value { auto luma::to_json(luma::text_style const &style) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"font", "font",
@@ -302,7 +305,7 @@ auto luma::to_json(luma::text_style const& style) -> boost::json::value {
}; };
} }
auto luma::to_json(luma::theme const& theme) -> boost::json::value { auto luma::to_json(luma::theme const &theme) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"background", "background",
+47 -44
View File
@@ -19,13 +19,14 @@
#include <luma/render.hpp> #include <luma/render.hpp>
#include <luma/theme.hpp> #include <luma/theme.hpp>
namespace { namespace {
class scope_exit { class scope_exit {
public: public:
explicit scope_exit(std::function<void()> action); explicit scope_exit(std::function<void()> action);
scope_exit(scope_exit const&) = delete; scope_exit(scope_exit const &) = delete;
auto operator=(scope_exit const&) -> scope_exit& = delete; auto operator=(scope_exit const &) -> scope_exit & = delete;
~scope_exit(); ~scope_exit();
private: private:
std::function<void()> action_{}; std::function<void()> action_{};
@@ -37,7 +38,7 @@ auto finite_rect(luma::rect value) -> std::bool_t {
return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.width) && std::isfinite(value.height) && value.width >= 0.0F && value.height >= 0.0F; return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.width) && std::isfinite(value.height) && value.width >= 0.0F && value.height >= 0.0F;
} }
auto title_height(luma::window_options const& options, std::float32_t line_height) -> std::float32_t { auto title_height(luma::window_options const &options, std::float32_t line_height) -> std::float32_t {
if (!options.decorated) { if (!options.decorated) {
return {}; return {};
} }
@@ -45,7 +46,7 @@ auto title_height(luma::window_options const& options, std::float32_t line_heigh
return std::min(desired, options.bounds.height); return std::min(desired, options.bounds.height);
} }
auto title_bounds(luma::window_options const& options, std::float32_t line_height) -> luma::rect { auto title_bounds(luma::window_options const &options, std::float32_t line_height) -> luma::rect {
return { return {
.x = options.bounds.x, .x = options.bounds.x,
.y = options.bounds.y, .y = options.bounds.y,
@@ -54,7 +55,7 @@ auto title_bounds(luma::window_options const& options, std::float32_t line_heigh
}; };
} }
auto client_bounds(luma::window_options const& options, std::float32_t line_height) -> luma::rect { auto client_bounds(luma::window_options const &options, std::float32_t line_height) -> luma::rect {
if (!options.decorated) { if (!options.decorated) {
return options.bounds; return options.bounds;
} }
@@ -77,14 +78,15 @@ auto unavailable(std::string message) -> std::expected<VALUE, modulus::error> {
return luma::detail::fail<VALUE>(luma::detail::error_unavailable, std::move(message)); return luma::detail::fail<VALUE>(luma::detail::error_unavailable, std::move(message));
} }
auto make_event(luma::touch_event const& event, luma::touch_point point) -> luma::touch_event { auto make_event(luma::touch_event const &event, luma::touch_point point) -> luma::touch_event {
return { return {
.points = {point}, .points = {point},
.time = event.time, .time = event.time,
}; };
} }
scope_exit::scope_exit(std::function<void()> action) : action_{std::move(action)} {} scope_exit::scope_exit(std::function<void()> action)
: action_{std::move(action)} {}
scope_exit::~scope_exit() { scope_exit::~scope_exit() {
if (action_) { if (action_) {
@@ -94,7 +96,8 @@ scope_exit::~scope_exit() {
} }
luma::window::window(std::uint64_t id, luma::window_options options, std::unique_ptr<luma::control> content) : id_{id}, options_{std::move(options)}, content_{std::move(content)} { luma::window::window(std::uint64_t id, luma::window_options options, std::unique_ptr<luma::control> content)
: id_{id}, options_{std::move(options)}, content_{std::move(content)} {
assert(id_ != 0); assert(id_ != 0);
assert(finite_rect(options_.bounds)); assert(finite_rect(options_.bounds));
assert(content_ != nullptr); assert(content_ != nullptr);
@@ -105,7 +108,7 @@ auto luma::window::id() const -> std::uint64_t {
return id_; return id_;
} }
auto luma::window::options() const -> luma::window_options const& { auto luma::window::options() const -> luma::window_options const & {
assert(finite_rect(options_.bounds)); assert(finite_rect(options_.bounds));
return options_; return options_;
} }
@@ -126,12 +129,12 @@ auto luma::window::visible() const -> std::bool_t {
return options_.visible; return options_.visible;
} }
auto luma::window::content() -> luma::control* { auto luma::window::content() -> luma::control * {
assert(content_ != nullptr); assert(content_ != nullptr);
return content_.get(); return content_.get();
} }
auto luma::window::content() const -> luma::control const* { auto luma::window::content() const -> luma::control const * {
assert(content_ != nullptr); assert(content_ != nullptr);
return content_.get(); return content_.get();
} }
@@ -246,11 +249,11 @@ auto luma::window_manager::set_visible(std::uint64_t id, std::bool_t visible) ->
return {}; return {};
} }
auto luma::window_manager::find(std::uint64_t id) -> luma::window* { auto luma::window_manager::find(std::uint64_t id) -> luma::window * {
if (id == 0) { if (id == 0) {
return nullptr; return nullptr;
} }
auto found = std::find_if(windows_.begin(), windows_.end(), [id](auto const& window) { return window && window->id() == id; }); auto found = std::find_if(windows_.begin(), windows_.end(), [id](auto const &window) { return window && window->id() == id; });
return found == windows_.end() ? nullptr : found->get(); return found == windows_.end() ? nullptr : found->get();
} }
@@ -265,7 +268,7 @@ auto luma::window_manager::clear() -> void {
active_touches_.clear(); active_touches_.clear();
} }
auto luma::window_manager::measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size { auto luma::window_manager::measure(luma::draw_context &context, luma::theme const &theme, luma::constraints limits) -> luma::size {
title_line_height_ = theme.body.line_height; title_line_height_ = theme.body.line_height;
++mutation_depth_; ++mutation_depth_;
auto guard = scope_exit{[this] { auto guard = scope_exit{[this] {
@@ -273,7 +276,7 @@ auto luma::window_manager::measure(luma::draw_context& context, luma::theme cons
--mutation_depth_; --mutation_depth_;
apply_pending_operations(); apply_pending_operations();
}}; }};
for (auto& window : windows_) { for (auto &window : windows_) {
assert(window != nullptr); assert(window != nullptr);
if (!window->visible()) { if (!window->visible()) {
continue; continue;
@@ -281,12 +284,12 @@ auto luma::window_manager::measure(luma::draw_context& context, luma::theme cons
assert(window->content() != nullptr); assert(window->content() != nullptr);
auto client = client_bounds(window->options(), title_line_height_); auto client = client_bounds(window->options(), title_line_height_);
window->content()->measure(context, theme, { window->content()->measure(context, theme, {
.min = {}, .min = {},
.max = { .max = {
.width = client.width, .width = client.width,
.height = client.height, .height = client.height,
}, },
}); });
} }
return luma::clamp(limits.max, limits); return luma::clamp(limits.max, limits);
} }
@@ -300,7 +303,7 @@ auto luma::window_manager::arrange(luma::rect bounds) -> void {
apply_pending_operations(); apply_pending_operations();
}}; }};
luma::control::arrange(bounds); luma::control::arrange(bounds);
for (auto& window : windows_) { for (auto &window : windows_) {
assert(window != nullptr); assert(window != nullptr);
if (window->visible()) { if (window->visible()) {
assert(window->content() != nullptr); assert(window->content() != nullptr);
@@ -309,7 +312,7 @@ auto luma::window_manager::arrange(luma::rect bounds) -> void {
} }
} }
auto luma::window_manager::draw(luma::draw_context& context, luma::theme const& theme) const -> void { auto luma::window_manager::draw(luma::draw_context &context, luma::theme const &theme) const -> void {
title_line_height_ = theme.body.line_height; title_line_height_ = theme.body.line_height;
++mutation_depth_; ++mutation_depth_;
auto guard = scope_exit{[this] { auto guard = scope_exit{[this] {
@@ -317,7 +320,7 @@ auto luma::window_manager::draw(luma::draw_context& context, luma::theme const&
--mutation_depth_; --mutation_depth_;
apply_pending_operations(); apply_pending_operations();
}}; }};
for (auto const& window : windows_) { for (auto const &window : windows_) {
assert(window != nullptr); assert(window != nullptr);
if (!window->visible()) { if (!window->visible()) {
continue; continue;
@@ -333,15 +336,16 @@ auto luma::window_manager::draw(luma::draw_context& context, luma::theme const&
auto title = title_bounds(window->options(), title_line_height_); auto title = title_bounds(window->options(), title_line_height_);
context.fill_rect(title, theme.surface_active); context.fill_rect(title, theme.surface_active);
context.draw_text(window->options().title, { context.draw_text(window->options().title, {
.x = title.x + 8.0F, .x = title.x + 8.0F,
.y = title.y + window_title_vertical_padding, .y = title.y + window_title_vertical_padding,
}, theme.body, theme.text); },
theme.body, theme.text);
} }
context.stroke_rect(bounds, 1.0F, theme.border); context.stroke_rect(bounds, 1.0F, theme.border);
} }
} }
auto luma::window_manager::handle_touch(luma::touch_event const& event) -> std::bool_t { auto luma::window_manager::handle_touch(luma::touch_event const &event) -> std::bool_t {
if (event.points.empty()) { if (event.points.empty()) {
return false; return false;
} }
@@ -352,10 +356,10 @@ auto luma::window_manager::handle_touch(luma::touch_event const& event) -> std::
apply_pending_operations(); apply_pending_operations();
}}; }};
auto handled = false; auto handled = false;
for (auto const& point : event.points) { for (auto const &point : event.points) {
if (auto captured = active_touches_.find(point.id); captured != active_touches_.end()) { if (auto captured = active_touches_.find(point.id); captured != active_touches_.end()) {
auto const window_id = captured->second; auto const window_id = captured->second;
auto* captured_window = find(window_id); auto *captured_window = find(window_id);
auto const captured_visible = captured_window != nullptr && captured_window->visible(); auto const captured_visible = captured_window != nullptr && captured_window->visible();
if (captured_visible) { if (captured_visible) {
assert(captured_window->content() != nullptr); assert(captured_window->content() != nullptr);
@@ -368,7 +372,6 @@ auto luma::window_manager::handle_touch(luma::touch_event const& event) -> std::
} }
continue; continue;
} }
for (auto current = windows_.rbegin(); current != windows_.rend(); ++current) { for (auto current = windows_.rbegin(); current != windows_.rend(); ++current) {
assert(*current != nullptr); assert(*current != nullptr);
if (!(*current)->visible()) { if (!(*current)->visible()) {
@@ -400,8 +403,8 @@ auto luma::window_manager::contains_effective(std::uint64_t id) const -> std::bo
if (id == 0) { if (id == 0) {
return false; return false;
} }
auto exists = std::find_if(windows_.begin(), windows_.end(), [id](auto const& window) { return window && window->id() == id; }) != windows_.end(); auto exists = std::find_if(windows_.begin(), windows_.end(), [id](auto const &window) { return window && window->id() == id; }) != windows_.end();
for (auto const& operation : pending_operations_) { for (auto const &operation : pending_operations_) {
switch (operation.kind) { switch (operation.kind) {
case luma::window_manager::pending_kind::OPEN: case luma::window_manager::pending_kind::OPEN:
if (operation.id == id) { if (operation.id == id) {
@@ -426,7 +429,7 @@ auto luma::window_manager::contains_effective(std::uint64_t id) const -> std::bo
} }
auto luma::window_manager::close_now(std::uint64_t id) const -> void { auto luma::window_manager::close_now(std::uint64_t id) const -> void {
auto found = std::find_if(windows_.begin(), windows_.end(), [id](auto const& window) { return window && window->id() == id; }); auto found = std::find_if(windows_.begin(), windows_.end(), [id](auto const &window) { return window && window->id() == id; });
if (found == windows_.end()) { if (found == windows_.end()) {
return; return;
} }
@@ -442,7 +445,7 @@ auto luma::window_manager::close_now(std::uint64_t id) const -> void {
} }
auto luma::window_manager::bring_to_front_now(std::uint64_t id) const -> void { auto luma::window_manager::bring_to_front_now(std::uint64_t id) const -> void {
auto found = std::find_if(windows_.begin(), windows_.end(), [id](auto const& window) { return window && window->id() == id; }); auto found = std::find_if(windows_.begin(), windows_.end(), [id](auto const &window) { return window && window->id() == id; });
if (found == windows_.end()) { if (found == windows_.end()) {
return; return;
} }
@@ -452,14 +455,14 @@ auto luma::window_manager::bring_to_front_now(std::uint64_t id) const -> void {
} }
auto luma::window_manager::set_bounds_now(std::uint64_t id, luma::rect bounds) const -> void { auto luma::window_manager::set_bounds_now(std::uint64_t id, luma::rect bounds) const -> void {
auto* window = const_cast<luma::window_manager*>(this)->find(id); auto *window = const_cast<luma::window_manager *>(this)->find(id);
if (window != nullptr) { if (window != nullptr) {
static_cast<void>(window->set_bounds(bounds)); static_cast<void>(window->set_bounds(bounds));
} }
} }
auto luma::window_manager::set_visible_now(std::uint64_t id, std::bool_t visible) const -> void { auto luma::window_manager::set_visible_now(std::uint64_t id, std::bool_t visible) const -> void {
auto* window = const_cast<luma::window_manager*>(this)->find(id); auto *window = const_cast<luma::window_manager *>(this)->find(id);
if (window != nullptr) { if (window != nullptr) {
window->set_visible(visible); window->set_visible(visible);
} }
@@ -471,7 +474,7 @@ auto luma::window_manager::apply_pending_operations() const -> void {
} }
auto pending = std::vector<luma::window_manager::pending_operation>{}; auto pending = std::vector<luma::window_manager::pending_operation>{};
pending.swap(pending_operations_); pending.swap(pending_operations_);
for (auto& operation : pending) { for (auto &operation : pending) {
switch (operation.kind) { switch (operation.kind) {
case luma::window_manager::pending_kind::OPEN: case luma::window_manager::pending_kind::OPEN:
if (operation.window) { if (operation.window) {
@@ -505,20 +508,20 @@ auto luma::window_manager::invalidate_layout() -> void {
--mutation_depth_; --mutation_depth_;
apply_pending_operations(); apply_pending_operations();
}}; }};
auto controls = std::vector<luma::control*>{}; auto controls = std::vector<luma::control *>{};
controls.reserve(windows_.size() + pending_operations_.size()); controls.reserve(windows_.size() + pending_operations_.size());
for (auto& window : windows_) { for (auto &window : windows_) {
assert(window != nullptr); assert(window != nullptr);
assert(window->content() != nullptr); assert(window->content() != nullptr);
controls.push_back(window->content()); controls.push_back(window->content());
} }
for (auto& operation : pending_operations_) { for (auto &operation : pending_operations_) {
if (operation.window) { if (operation.window) {
assert(operation.window->content() != nullptr); assert(operation.window->content() != nullptr);
controls.push_back(operation.window->content()); controls.push_back(operation.window->content());
} }
} }
for (auto* control : controls) { for (auto *control : controls) {
assert(control != nullptr); assert(control != nullptr);
control->invalidate_layout(); control->invalidate_layout();
} }
@@ -526,12 +529,12 @@ auto luma::window_manager::invalidate_layout() -> void {
auto luma::window_manager::bind_layout_invalidator(std::function<void()> invalidator) -> void { auto luma::window_manager::bind_layout_invalidator(std::function<void()> invalidator) -> void {
luma::control::bind_layout_invalidator(invalidator); luma::control::bind_layout_invalidator(invalidator);
for (auto& window : windows_) { for (auto &window : windows_) {
assert(window != nullptr); assert(window != nullptr);
assert(window->content() != nullptr); assert(window->content() != nullptr);
window->content()->bind_layout_invalidator(invalidator); window->content()->bind_layout_invalidator(invalidator);
} }
for (auto& operation : pending_operations_) { for (auto &operation : pending_operations_) {
if (operation.window) { if (operation.window) {
assert(operation.window->content() != nullptr); assert(operation.window->content() != nullptr);
operation.window->content()->bind_layout_invalidator(invalidator); operation.window->content()->bind_layout_invalidator(invalidator);
+475 -439
View File
File diff suppressed because it is too large Load Diff