This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <luma/application.hpp>
|
||||
#include <luma/animation.hpp>
|
||||
#include <luma/assets.hpp>
|
||||
#include <luma/config.hpp>
|
||||
#include <luma/control.hpp>
|
||||
#include <luma/controls.hpp>
|
||||
#include <luma/geometry.hpp>
|
||||
#include <luma/input.hpp>
|
||||
#include <luma/overlays.hpp>
|
||||
#include <luma/render.hpp>
|
||||
#include <luma/routing.hpp>
|
||||
#include <luma/theme.hpp>
|
||||
#include <luma/window.hpp>
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
enum class easing {
|
||||
LINEAR,
|
||||
EASE_OUT,
|
||||
EASE_IN_OUT,
|
||||
};
|
||||
|
||||
struct animation {
|
||||
std::float32_t from{};
|
||||
std::float32_t to{};
|
||||
std::chrono::milliseconds duration{200};
|
||||
luma::easing easing{luma::easing::EASE_OUT};
|
||||
std::function<void(std::float32_t)> apply{};
|
||||
std::function<void()> finished{};
|
||||
};
|
||||
|
||||
class animation_graph {
|
||||
public:
|
||||
auto add(luma::animation animation) -> std::expected<void, modulus::error>;
|
||||
auto tick(std::chrono::steady_clock::duration delta) -> std::expected<void, modulus::error>;
|
||||
auto empty() const -> std::bool_t;
|
||||
auto clear() -> void;
|
||||
private:
|
||||
struct running_animation {
|
||||
luma::animation animation{};
|
||||
std::chrono::steady_clock::duration elapsed{};
|
||||
};
|
||||
enum class pending_kind {
|
||||
ADD,
|
||||
CLEAR,
|
||||
};
|
||||
struct pending_operation {
|
||||
luma::animation_graph::pending_kind kind{luma::animation_graph::pending_kind::CLEAR};
|
||||
luma::animation_graph::running_animation animation{};
|
||||
};
|
||||
std::vector<luma::animation_graph::running_animation> animations_{};
|
||||
std::vector<luma::animation_graph::pending_operation> pending_{};
|
||||
std::bool_t ticking_{false};
|
||||
std::bool_t clear_requested_{false};
|
||||
};
|
||||
|
||||
auto interpolate(std::float32_t from, std::float32_t to, std::float32_t progress, luma::easing easing) -> std::expected<std::float32_t, modulus::error>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/animation.hpp>
|
||||
#include <luma/config.hpp>
|
||||
#include <luma/control.hpp>
|
||||
#include <luma/geometry.hpp>
|
||||
#include <luma/overlays.hpp>
|
||||
#include <luma/render.hpp>
|
||||
#include <luma/window.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
namespace detail {
|
||||
struct application_core;
|
||||
}
|
||||
|
||||
struct frame_stats {
|
||||
std::uint64_t frame{};
|
||||
std::chrono::steady_clock::duration delta{};
|
||||
};
|
||||
|
||||
class application {
|
||||
public:
|
||||
explicit application(luma::application_config config = {});
|
||||
application(luma::application const&) = delete;
|
||||
application(luma::application&&) noexcept;
|
||||
~application();
|
||||
auto operator=(luma::application const&) -> luma::application& = delete;
|
||||
auto operator=(luma::application&&) noexcept -> luma::application&;
|
||||
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 replace_route(std::string name) -> std::expected<void, modulus::error>;
|
||||
auto push_route(std::string name) -> std::expected<void, modulus::error>;
|
||||
auto pop_route() -> std::expected<void, modulus::error>;
|
||||
auto current_route() const -> std::expected<std::string, modulus::error>;
|
||||
auto add_animation(luma::animation animation) -> std::expected<void, modulus::error>;
|
||||
auto clear_animations() -> std::expected<void, modulus::error>;
|
||||
auto animations_empty() const -> std::expected<std::bool_t, modulus::error>;
|
||||
auto open_window(luma::window_options options, std::unique_ptr<luma::control> content) -> std::expected<std::uint64_t, modulus::error>;
|
||||
auto close_window(std::uint64_t id) -> std::expected<void, modulus::error>;
|
||||
auto bring_window_to_front(std::uint64_t id) -> std::expected<void, modulus::error>;
|
||||
auto set_window_bounds(std::uint64_t id, luma::rect bounds) -> std::expected<void, modulus::error>;
|
||||
auto set_window_visible(std::uint64_t id, std::bool_t visible) -> std::expected<void, modulus::error>;
|
||||
auto load_texture(std::string path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error>;
|
||||
auto request_layout() -> 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>;
|
||||
template <typename 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 show_dialog(luma::dialog_options options) -> 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 request_stop() -> void;
|
||||
auto should_stop() const -> std::bool_t;
|
||||
auto run() -> std::expected<void, modulus::error>;
|
||||
private:
|
||||
auto core_snapshot() const -> std::shared_ptr<luma::detail::application_core>;
|
||||
mutable std::mutex core_mutex_{};
|
||||
std::shared_ptr<luma::detail::application_core> core_{};
|
||||
};
|
||||
|
||||
template <typename 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> {
|
||||
return set_root(std::unique_ptr<luma::control>{std::move(root)});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/render.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
class renderer_resources {
|
||||
public:
|
||||
virtual ~renderer_resources() = default;
|
||||
virtual auto load_texture(std::string const& path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error> = 0;
|
||||
};
|
||||
|
||||
class asset_cache {
|
||||
public:
|
||||
explicit asset_cache(luma::renderer_resources& resources);
|
||||
auto texture(std::string const& path) -> std::expected<std::shared_ptr<luma::texture>, modulus::error>;
|
||||
auto clear() -> void;
|
||||
private:
|
||||
struct in_flight_load;
|
||||
luma::renderer_resources* resources_{};
|
||||
mutable std::mutex mutex_{};
|
||||
std::uint64_t generation_{};
|
||||
std::unordered_map<std::string, std::shared_ptr<luma::texture>> textures_{};
|
||||
std::unordered_map<std::string, std::shared_ptr<luma::asset_cache::in_flight_load>> loading_{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <string>
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/theme.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
struct display_config {
|
||||
std::int32_t width{1024};
|
||||
std::int32_t height{600};
|
||||
std::int32_t target_fps{60};
|
||||
std::int32_t idle_fps{15};
|
||||
std::bool_t fullscreen{true};
|
||||
};
|
||||
|
||||
struct touch_config {
|
||||
std::int32_t max_points{5};
|
||||
};
|
||||
|
||||
struct application_config {
|
||||
std::string title{"Luma"};
|
||||
luma::display_config display{};
|
||||
luma::touch_config touch{};
|
||||
luma::theme theme{luma::default_theme()};
|
||||
};
|
||||
|
||||
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::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_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 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::touch_config const& config) -> boost::json::value;
|
||||
auto to_json(luma::application_config const& config) -> boost::json::value;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/geometry.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
class draw_context;
|
||||
struct theme;
|
||||
struct touch_event;
|
||||
|
||||
class control {
|
||||
public:
|
||||
virtual ~control();
|
||||
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 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 invalidate_layout() -> void;
|
||||
virtual auto bind_layout_invalidator(std::function<void()> invalidator) -> void;
|
||||
virtual auto focusable() const -> std::bool_t;
|
||||
virtual auto set_focused(std::bool_t value) -> void;
|
||||
auto focused() const -> std::bool_t;
|
||||
auto set_enabled(std::bool_t value) -> void;
|
||||
auto enabled() const -> std::bool_t;
|
||||
auto bounds() const -> luma::rect;
|
||||
protected:
|
||||
auto notify_layout_changed() const -> void;
|
||||
auto layout_invalidator() const -> std::function<void()>;
|
||||
private:
|
||||
luma::rect bounds_{};
|
||||
std::function<void()> layout_invalidator_{};
|
||||
std::bool_t focused_{false};
|
||||
std::bool_t enabled_{true};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/control.hpp>
|
||||
#include <luma/detail/errors.hpp>
|
||||
#include <luma/render.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
enum class direction {
|
||||
ROW,
|
||||
COLUMN,
|
||||
};
|
||||
|
||||
enum class alignment {
|
||||
START,
|
||||
CENTER,
|
||||
END,
|
||||
STRETCH,
|
||||
};
|
||||
|
||||
enum class text_input_mode {
|
||||
TEXT,
|
||||
INTEGER,
|
||||
DECIMAL,
|
||||
};
|
||||
|
||||
enum class image_fit {
|
||||
CONTAIN,
|
||||
COVER,
|
||||
FILL,
|
||||
CENTER,
|
||||
};
|
||||
|
||||
enum class keyboard_key_kind {
|
||||
CHARACTER,
|
||||
BACKSPACE,
|
||||
SPACE,
|
||||
ENTER,
|
||||
};
|
||||
|
||||
struct keyboard_key {
|
||||
std::string label{};
|
||||
std::string value{};
|
||||
luma::keyboard_key_kind kind{luma::keyboard_key_kind::CHARACTER};
|
||||
};
|
||||
|
||||
struct keyboard_layout {
|
||||
std::vector<std::vector<luma::keyboard_key>> rows{};
|
||||
};
|
||||
|
||||
struct text_field_options {
|
||||
std::string text{};
|
||||
std::string placeholder{};
|
||||
luma::text_input_mode mode{luma::text_input_mode::TEXT};
|
||||
std::bool_t password{false};
|
||||
std::size_t max_length{256};
|
||||
};
|
||||
|
||||
class text_input_state {
|
||||
public:
|
||||
struct layout_listener {
|
||||
std::weak_ptr<void> lifetime{};
|
||||
std::function<void()> callback{};
|
||||
};
|
||||
|
||||
text_input_state(luma::text_field_options options, std::function<void(std::string)> on_change);
|
||||
auto snapshot() const -> luma::text_field_options;
|
||||
auto text() const -> std::string;
|
||||
auto focused() const -> std::bool_t;
|
||||
auto set_focused(std::bool_t value) -> void;
|
||||
auto version() const -> std::uint64_t;
|
||||
auto add_layout_listener(std::weak_ptr<void> lifetime, std::function<void()> callback) -> void;
|
||||
auto append(std::string_view value) -> std::expected<void, modulus::error>;
|
||||
auto backspace() -> std::expected<void, modulus::error>;
|
||||
auto set_text(std::string value) -> std::expected<void, modulus::error>;
|
||||
private:
|
||||
mutable std::mutex mutex_{};
|
||||
luma::text_field_options options_{};
|
||||
std::function<void(std::string)> on_change_{};
|
||||
std::vector<luma::text_input_state::layout_listener> layout_listeners_{};
|
||||
std::bool_t focused_{false};
|
||||
std::uint64_t version_{};
|
||||
};
|
||||
|
||||
auto default_text_keyboard() -> luma::keyboard_layout;
|
||||
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>;
|
||||
|
||||
template <typename... CONTROLS>
|
||||
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 button(std::string text, std::function<void()> on_click) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto text_field(luma::text_field_options options, std::function<void(std::string)> on_change) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto text_field(std::shared_ptr<luma::text_input_state> input) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto soft_keyboard(luma::keyboard_layout layout, std::shared_ptr<luma::text_input_state> input, std::function<void()> on_enter = {}) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto soft_keyboard(luma::keyboard_layout layout, std::function<void(std::string)> on_text, std::function<void()> on_enter = {}) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto panel(std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> children = luma::children()) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto panel(std::vector<std::unique_ptr<luma::control>> children) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto spacer(luma::size value) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto progress_bar(std::float32_t value) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto image(std::shared_ptr<luma::texture> texture, luma::image_fit fit = luma::image_fit::CONTAIN) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto row(std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> children, std::float32_t gap = 8.0F, luma::alignment align = luma::alignment::CENTER) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto row(std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap = 8.0F, luma::alignment align = luma::alignment::CENTER) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto column(std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> children, std::float32_t gap = 8.0F, luma::alignment align = luma::alignment::STRETCH) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto column(std::vector<std::unique_ptr<luma::control>> children, std::float32_t gap = 8.0F, luma::alignment align = luma::alignment::STRETCH) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto stack(std::expected<std::vector<std::unique_ptr<luma::control>>, modulus::error> children) -> std::expected<std::unique_ptr<luma::control>, modulus::error>;
|
||||
auto stack(std::vector<std::unique_ptr<luma::control>> children) -> 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>;
|
||||
template <typename 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 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>;
|
||||
|
||||
}
|
||||
|
||||
namespace luma::detail {
|
||||
|
||||
template <typename CONTROL>
|
||||
requires std::is_base_of_v<luma::control, CONTROL>
|
||||
auto normalize_child(std::unique_ptr<CONTROL> child) -> std::expected<std::unique_ptr<luma::control>, modulus::error> {
|
||||
if (child == nullptr) {
|
||||
return luma::detail::fail<std::unique_ptr<luma::control>>(luma::detail::error_invalid_argument, "child control must not be null");
|
||||
}
|
||||
return modulus::success(std::unique_ptr<luma::control>{std::move(child)});
|
||||
}
|
||||
|
||||
template <typename CONTROL>
|
||||
requires std::is_base_of_v<luma::control, CONTROL>
|
||||
auto normalize_child(std::expected<std::unique_ptr<CONTROL>, modulus::error> child) -> std::expected<std::unique_ptr<luma::control>, modulus::error> {
|
||||
if (!child) {
|
||||
return modulus::fail<std::unique_ptr<luma::control>>(child.error());
|
||||
}
|
||||
return luma::detail::normalize_child(std::move(*child));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename 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> {
|
||||
return luma::scroll_view(std::unique_ptr<luma::control>{std::move(child)}, vertical, horizontal);
|
||||
}
|
||||
|
||||
template <typename... CONTROLS>
|
||||
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>>{};
|
||||
result.reserve(sizeof...(controls));
|
||||
|
||||
auto append = [&result](auto&& input) -> std::expected<void, modulus::error> {
|
||||
auto child = luma::detail::normalize_child(std::forward<decltype(input)>(input));
|
||||
if (!child) {
|
||||
return modulus::fail<void>(child.error());
|
||||
}
|
||||
result.push_back(std::move(*child));
|
||||
return {};
|
||||
};
|
||||
|
||||
auto status = std::expected<void, modulus::error>{};
|
||||
([&] {
|
||||
if (status) {
|
||||
status = append(std::forward<CONTROLS>(controls));
|
||||
}
|
||||
}(), ...);
|
||||
if (!status) {
|
||||
return modulus::fail<std::vector<std::unique_ptr<luma::control>>>(status.error());
|
||||
}
|
||||
return modulus::success(std::move(result));
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
namespace luma::detail {
|
||||
|
||||
constexpr std::int32_t error_invalid_argument{1};
|
||||
constexpr std::int32_t error_not_found{2};
|
||||
constexpr std::int32_t error_unavailable{3};
|
||||
constexpr std::int32_t error_timeout{4};
|
||||
constexpr std::int32_t error_internal{5};
|
||||
|
||||
auto make_error(std::int32_t code, std::string message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> modulus::error;
|
||||
|
||||
template <typename VALUE>
|
||||
auto fail(std::int32_t code, std::string message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> std::expected<VALUE, modulus::error>;
|
||||
|
||||
}
|
||||
|
||||
template <typename VALUE>
|
||||
auto luma::detail::fail(std::int32_t code, std::string message, modulus::source_location location) -> std::expected<VALUE, modulus::error> {
|
||||
return modulus::fail<VALUE>(luma::detail::make_error(code, std::move(message), location));
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
struct point {
|
||||
std::float32_t x{};
|
||||
std::float32_t y{};
|
||||
};
|
||||
|
||||
struct size {
|
||||
std::float32_t width{};
|
||||
std::float32_t height{};
|
||||
};
|
||||
|
||||
struct rect {
|
||||
std::float32_t x{};
|
||||
std::float32_t y{};
|
||||
std::float32_t width{};
|
||||
std::float32_t height{};
|
||||
};
|
||||
|
||||
struct edge_insets {
|
||||
std::float32_t left{};
|
||||
std::float32_t top{};
|
||||
std::float32_t right{};
|
||||
std::float32_t bottom{};
|
||||
};
|
||||
|
||||
struct constraints {
|
||||
luma::size min{};
|
||||
luma::size max{.width = std::numeric_limits<std::float32_t>::max(), .height = std::numeric_limits<std::float32_t>::max()};
|
||||
};
|
||||
|
||||
constexpr auto right(luma::rect value) -> std::float32_t;
|
||||
constexpr auto bottom(luma::rect value) -> std::float32_t;
|
||||
constexpr auto contains(luma::rect area, luma::point value) -> std::bool_t;
|
||||
constexpr auto inset(luma::rect area, luma::edge_insets value) -> luma::rect;
|
||||
constexpr auto clamp(luma::size value, luma::constraints limits) -> luma::size;
|
||||
constexpr auto deflate(luma::constraints limits, luma::edge_insets value) -> luma::constraints;
|
||||
|
||||
}
|
||||
|
||||
constexpr auto luma::right(luma::rect value) -> std::float32_t {
|
||||
return value.x + value.width;
|
||||
}
|
||||
|
||||
constexpr auto luma::bottom(luma::rect value) -> std::float32_t {
|
||||
return value.y + value.height;
|
||||
}
|
||||
|
||||
constexpr auto luma::contains(luma::rect area, luma::point value) -> std::bool_t {
|
||||
return value.x >= area.x && value.x <= luma::right(area) && value.y >= area.y && value.y <= luma::bottom(area);
|
||||
}
|
||||
|
||||
constexpr auto luma::inset(luma::rect area, luma::edge_insets value) -> luma::rect {
|
||||
return {.x = area.x + value.left, .y = area.y + value.top, .width = std::max(std::float32_t{}, area.width - value.left - value.right), .height = std::max(std::float32_t{}, area.height - value.top - value.bottom)};
|
||||
}
|
||||
|
||||
constexpr auto luma::clamp(luma::size value, luma::constraints limits) -> luma::size {
|
||||
return {.width = std::clamp(value.width, limits.min.width, limits.max.width), .height = std::clamp(value.height, limits.min.height, limits.max.height)};
|
||||
}
|
||||
|
||||
constexpr auto luma::deflate(luma::constraints limits, luma::edge_insets value) -> luma::constraints {
|
||||
return {.min = {.width = std::max(std::float32_t{}, limits.min.width - value.left - value.right), .height = std::max(std::float32_t{}, limits.min.height - value.top - value.bottom)}, .max = {.width = std::max(std::float32_t{}, limits.max.width - value.left - value.right), .height = std::max(std::float32_t{}, limits.max.height - value.top - value.bottom)}};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <luma/geometry.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
enum class touch_phase {
|
||||
DOWN,
|
||||
MOVE,
|
||||
UP,
|
||||
CANCEL,
|
||||
};
|
||||
|
||||
struct touch_point {
|
||||
std::int32_t id{};
|
||||
luma::point position{};
|
||||
luma::touch_phase phase{luma::touch_phase::MOVE};
|
||||
};
|
||||
|
||||
struct touch_event {
|
||||
std::vector<luma::touch_point> points{};
|
||||
std::chrono::steady_clock::time_point time{std::chrono::steady_clock::now()};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/control.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
enum class dialog_result {
|
||||
NONE,
|
||||
ACCEPTED,
|
||||
CANCELLED,
|
||||
};
|
||||
|
||||
struct dialog_options {
|
||||
std::string title{};
|
||||
std::unique_ptr<luma::control> body{};
|
||||
std::string accept_text{"OK"};
|
||||
std::string cancel_text{"Cancel"};
|
||||
std::function<void(luma::dialog_result)> on_close{};
|
||||
};
|
||||
|
||||
struct toast {
|
||||
std::string message{};
|
||||
std::chrono::milliseconds duration{2500};
|
||||
};
|
||||
|
||||
class overlay_host final : public luma::control {
|
||||
public:
|
||||
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 show_toast(luma::toast toast) -> std::expected<void, modulus::error>;
|
||||
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 arrange(luma::rect bounds) -> 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 invalidate_layout() -> void override;
|
||||
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
|
||||
private:
|
||||
friend auto overlays(std::unique_ptr<luma::control> content) -> std::expected<std::unique_ptr<luma::overlay_host>, modulus::error>;
|
||||
explicit overlay_host(std::unique_ptr<luma::control> content);
|
||||
auto begin_traversal() const -> void;
|
||||
auto end_traversal() const -> void;
|
||||
auto retire_dialog_body(std::unique_ptr<luma::control> body) -> void;
|
||||
auto flush_retired_dialog_bodies() const -> void;
|
||||
std::unique_ptr<luma::control> content_{};
|
||||
std::optional<luma::dialog_options> dialog_{};
|
||||
std::deque<luma::toast> toasts_{};
|
||||
std::chrono::steady_clock::duration toast_elapsed_{};
|
||||
mutable std::vector<std::unique_ptr<luma::control>> retired_dialog_bodies_{};
|
||||
mutable std::uint32_t traversal_depth_{};
|
||||
};
|
||||
|
||||
auto overlays(std::unique_ptr<luma::control> content) -> std::expected<std::unique_ptr<luma::overlay_host>, modulus::error>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/geometry.hpp>
|
||||
#include <luma/theme.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
struct texture_info {
|
||||
std::int32_t width{};
|
||||
std::int32_t height{};
|
||||
};
|
||||
|
||||
class texture {
|
||||
public:
|
||||
virtual ~texture() = default;
|
||||
virtual auto info() const -> luma::texture_info = 0;
|
||||
};
|
||||
|
||||
class draw_context {
|
||||
public:
|
||||
virtual ~draw_context() = default;
|
||||
virtual auto fill_rect(luma::rect bounds, luma::color color) -> void = 0;
|
||||
virtual auto stroke_rect(luma::rect bounds, 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 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_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 pop_clip() -> void = 0;
|
||||
virtual auto measure_text(std::string_view text, luma::text_style const& style) const -> luma::size = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/control.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
class router {
|
||||
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 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 push(std::string const& name) -> std::expected<void, modulus::error>;
|
||||
auto pop() -> std::expected<void, modulus::error>;
|
||||
auto clear_stack() -> void;
|
||||
auto current() -> luma::control*;
|
||||
auto current() const -> luma::control const*;
|
||||
auto current_route() const -> std::string const&;
|
||||
private:
|
||||
struct entry {
|
||||
std::string name{};
|
||||
std::unique_ptr<luma::control> view{};
|
||||
};
|
||||
auto retire(std::unique_ptr<luma::control> view) -> void;
|
||||
auto retire_stack() -> void;
|
||||
std::unordered_map<std::string, std::function<std::expected<std::unique_ptr<luma::control>, modulus::error>()>> routes_{};
|
||||
std::vector<luma::router::entry> stack_{};
|
||||
std::string empty_route_{};
|
||||
std::function<void(std::unique_ptr<luma::control>)> retire_handler_{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <string>
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
struct color {
|
||||
std::uint8_t r{};
|
||||
std::uint8_t g{};
|
||||
std::uint8_t b{};
|
||||
std::uint8_t a{255};
|
||||
};
|
||||
|
||||
struct text_style {
|
||||
std::string font{"default"};
|
||||
std::float32_t size{24.0F};
|
||||
std::float32_t line_height{30.0F};
|
||||
};
|
||||
|
||||
struct theme {
|
||||
luma::color background{18, 19, 22, 255};
|
||||
luma::color surface{32, 34, 38, 255};
|
||||
luma::color surface_active{46, 94, 170, 255};
|
||||
luma::color text{242, 244, 248, 255};
|
||||
luma::color text_muted{170, 176, 186, 255};
|
||||
luma::color border{82, 88, 99, 255};
|
||||
luma::color accent{84, 160, 255, 255};
|
||||
luma::text_style body{};
|
||||
luma::text_style title{.font = "default", .size = 32.0F, .line_height = 40.0F};
|
||||
std::float32_t radius{4.0F};
|
||||
std::float32_t spacing{8.0F};
|
||||
};
|
||||
|
||||
auto default_theme() -> luma::theme;
|
||||
auto validate(luma::text_style const& style) -> 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_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::text_style const& style) -> boost::json::value;
|
||||
auto to_json(luma::theme const& theme) -> boost::json::value;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/control.hpp>
|
||||
#include <luma/geometry.hpp>
|
||||
|
||||
namespace luma {
|
||||
|
||||
class window_manager;
|
||||
|
||||
struct window_options {
|
||||
std::string title{};
|
||||
luma::rect bounds{};
|
||||
std::bool_t modal{false};
|
||||
std::bool_t decorated{true};
|
||||
std::bool_t visible{true};
|
||||
};
|
||||
|
||||
class window {
|
||||
public:
|
||||
window(luma::window const&) = delete;
|
||||
window(luma::window&&) noexcept = default;
|
||||
auto operator=(luma::window const&) -> luma::window& = delete;
|
||||
auto operator=(luma::window&&) noexcept -> luma::window& = default;
|
||||
auto id() const -> std::uint64_t;
|
||||
auto options() const -> luma::window_options const&;
|
||||
auto set_bounds(luma::rect bounds) -> std::expected<void, modulus::error>;
|
||||
auto set_visible(std::bool_t value) -> void;
|
||||
auto visible() const -> std::bool_t;
|
||||
auto content() -> luma::control*;
|
||||
auto content() const -> luma::control const*;
|
||||
private:
|
||||
friend class luma::window_manager;
|
||||
window(std::uint64_t id, luma::window_options options, std::unique_ptr<luma::control> content);
|
||||
std::uint64_t id_{};
|
||||
luma::window_options options_{};
|
||||
std::unique_ptr<luma::control> content_{};
|
||||
};
|
||||
|
||||
class window_manager final : public luma::control {
|
||||
public:
|
||||
auto open(luma::window_options options, std::unique_ptr<luma::control> content) -> std::expected<std::uint64_t, modulus::error>;
|
||||
auto close(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_visible(std::uint64_t id, std::bool_t visible) -> std::expected<void, modulus::error>;
|
||||
auto find(std::uint64_t id) -> luma::window*;
|
||||
auto clear() -> void;
|
||||
auto measure(luma::draw_context& context, luma::theme const& theme, luma::constraints limits) -> luma::size override;
|
||||
auto arrange(luma::rect bounds) -> 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 invalidate_layout() -> void override;
|
||||
auto bind_layout_invalidator(std::function<void()> invalidator) -> void override;
|
||||
private:
|
||||
enum class pending_kind {
|
||||
OPEN,
|
||||
CLOSE,
|
||||
BRING_TO_FRONT,
|
||||
SET_BOUNDS,
|
||||
SET_VISIBLE,
|
||||
CLEAR,
|
||||
};
|
||||
struct pending_operation {
|
||||
luma::window_manager::pending_kind kind{luma::window_manager::pending_kind::CLEAR};
|
||||
std::uint64_t id{};
|
||||
std::unique_ptr<luma::window> window{};
|
||||
luma::rect bounds{};
|
||||
std::bool_t visible{};
|
||||
};
|
||||
auto contains_effective(std::uint64_t id) const -> std::bool_t;
|
||||
auto close_now(std::uint64_t id) const -> void;
|
||||
auto bring_to_front_now(std::uint64_t id) const -> void;
|
||||
auto set_bounds_now(std::uint64_t id, luma::rect bounds) const -> void;
|
||||
auto set_visible_now(std::uint64_t id, std::bool_t visible) const -> void;
|
||||
auto apply_pending_operations() const -> void;
|
||||
std::uint64_t next_id_{1};
|
||||
mutable std::float32_t title_line_height_{30.0F};
|
||||
mutable std::vector<std::unique_ptr<luma::window>> windows_{};
|
||||
mutable std::unordered_map<std::int32_t, std::uint64_t> active_touches_{};
|
||||
mutable std::vector<luma::window_manager::pending_operation> pending_operations_{};
|
||||
mutable std::uint32_t mutation_depth_{};
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user