New UI library wip
This commit is contained in:
parent
e21692743c
commit
1246985763
18 changed files with 508 additions and 0 deletions
6
libs/ui/CMakeLists.txt
Normal file
6
libs/ui/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
file(GLOB_RECURSE PSEMEK_UI_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_UI_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
psemek_add_library(psemek-ui ${PSEMEK_UI_HEADERS} ${PSEMEK_UI_SOURCES})
|
||||
target_include_directories(psemek-ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(psemek-ui PUBLIC psemek-util psemek-react psemek-geom psemek-gfx)
|
||||
20
libs/ui/include/psemek/ui/alignment.hpp
Normal file
20
libs/ui/include/psemek/ui/alignment.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
enum class halignment
|
||||
{
|
||||
left,
|
||||
center,
|
||||
right,
|
||||
};
|
||||
|
||||
enum class valignment
|
||||
{
|
||||
top,
|
||||
center,
|
||||
bottom,
|
||||
};
|
||||
|
||||
}
|
||||
17
libs/ui/include/psemek/ui/button.hpp
Normal file
17
libs/ui/include/psemek/ui/button.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/react/value.hpp>
|
||||
#include <psemek/react/source.hpp>
|
||||
|
||||
#include <any>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct button
|
||||
{
|
||||
react::source<void> on_click = {};
|
||||
react::value<std::any> child = {};
|
||||
};
|
||||
|
||||
}
|
||||
15
libs/ui/include/psemek/ui/frame.hpp
Normal file
15
libs/ui/include/psemek/ui/frame.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/react/value.hpp>
|
||||
|
||||
#include <any>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct frame
|
||||
{
|
||||
react::value<std::any> child = {};
|
||||
};
|
||||
|
||||
}
|
||||
46
libs/ui/include/psemek/ui/grid_layout.hpp
Normal file
46
libs/ui/include/psemek/ui/grid_layout.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/key.hpp>
|
||||
#include <psemek/react/value.hpp>
|
||||
|
||||
#include <any>
|
||||
#include <variant>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
namespace grid_layout
|
||||
{
|
||||
|
||||
struct minimized
|
||||
{};
|
||||
|
||||
struct weight
|
||||
{
|
||||
float value = 1.f;
|
||||
};
|
||||
|
||||
using size_policy = std::variant<weight, minimized>;
|
||||
|
||||
struct element
|
||||
{
|
||||
react::value<std::any> element;
|
||||
react::value<size_policy> policy = {};
|
||||
std::optional<ui::key> key = {};
|
||||
};
|
||||
|
||||
struct horizontal
|
||||
{
|
||||
using element = grid_layout::element;
|
||||
react::value<std::vector<element>> children = {};
|
||||
};
|
||||
|
||||
struct vertical
|
||||
{
|
||||
using element = grid_layout::element;
|
||||
react::value<std::vector<element>> children = {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
libs/ui/include/psemek/ui/impl/component.hpp
Normal file
11
libs/ui/include/psemek/ui/impl/component.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct component
|
||||
{
|
||||
virtual ~component() {}
|
||||
};
|
||||
|
||||
}
|
||||
18
libs/ui/include/psemek/ui/impl/component_factory.hpp
Normal file
18
libs/ui/include/psemek/ui/impl/component_factory.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/component.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <any>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct component_factory
|
||||
{
|
||||
virtual std::unique_ptr<component> reconciliate(std::unique_ptr<component> root, std::any const & value) = 0;
|
||||
|
||||
virtual ~component_factory() {}
|
||||
};
|
||||
|
||||
}
|
||||
141
libs/ui/include/psemek/ui/impl/component_factory_base.hpp
Normal file
141
libs/ui/include/psemek/ui/impl/component_factory_base.hpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/component_factory.hpp>
|
||||
#include <psemek/ui/impl/single_container.hpp>
|
||||
#include <psemek/ui/impl/container.hpp>
|
||||
#include <psemek/react/value.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
#include <psemek/util/type_name.hpp>
|
||||
#include <psemek/util/function.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct component_not_supported_exception
|
||||
: std::exception
|
||||
{
|
||||
component_not_supported_exception(std::type_index type)
|
||||
: type_(type)
|
||||
, message_(util::to_string("UI component ", util::type_name(type_), " is not supported"))
|
||||
{}
|
||||
|
||||
const char * what() const noexcept override
|
||||
{
|
||||
return message_.data();
|
||||
}
|
||||
|
||||
private:
|
||||
std::type_index type_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
struct component_factory_base
|
||||
: component_factory
|
||||
{
|
||||
std::unique_ptr<component> reconciliate(std::unique_ptr<component> root, std::any const & value) override
|
||||
{
|
||||
std::type_index type(value.type());
|
||||
if (auto it = type_factories_.find(type); it != type_factories_.end())
|
||||
return it->second(std::move(root), value);
|
||||
throw component_not_supported_exception(type);
|
||||
}
|
||||
|
||||
template <typename Type, typename Factory>
|
||||
void register_factory(Factory factory)
|
||||
{
|
||||
type_factories_[typeid(Type)] = [factory = std::move(factory)](std::unique_ptr<component> root, std::any const & value)
|
||||
{
|
||||
return factory(std::move(root), *std::any_cast<Type>(&value));
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Type, typename ImplType>
|
||||
void register_type()
|
||||
{
|
||||
type_factories_[typeid(Type)] = [this](std::unique_ptr<component> root, std::any const & value)
|
||||
{
|
||||
std::unique_ptr<ImplType> impl;
|
||||
if (root)
|
||||
{
|
||||
if (auto impl_raw = dynamic_cast<ImplType *>(root.get()))
|
||||
{
|
||||
impl.reset(impl_raw);
|
||||
root.release();
|
||||
}
|
||||
}
|
||||
|
||||
root.reset();
|
||||
|
||||
if (!impl)
|
||||
impl = std::make_unique<ImplType>();
|
||||
|
||||
auto const & typed_value = *std::any_cast<Type>(&value);
|
||||
|
||||
impl->update(typed_value);
|
||||
|
||||
if constexpr (std::is_base_of_v<single_container, ImplType>)
|
||||
{
|
||||
impl->release_child_token();
|
||||
impl->set_child_token(typed_value.child.subscribe([this, impl = impl.get()](std::any const & child){
|
||||
impl->set_child(reconciliate(impl->release_child(), child));
|
||||
}, true));
|
||||
}
|
||||
else if constexpr (std::is_base_of_v<container, ImplType>)
|
||||
{
|
||||
impl->set_children_token(typed_value.children.subscribe([this, impl = impl.get()](std::vector<typename Type::element> const & children_values){
|
||||
impl->release_child_tokens();
|
||||
|
||||
std::unordered_map<key, std::unique_ptr<component>> child_by_key;
|
||||
{
|
||||
auto children = impl->release_children();
|
||||
auto keys = impl->release_child_keys();
|
||||
|
||||
for (std::size_t i = 0; i < children.size(); ++i)
|
||||
if (keys[i])
|
||||
child_by_key[std::move(*keys[i])] = std::move(children[i]);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<component>> children(children_values.size());
|
||||
std::vector<std::optional<key>> child_keys(children_values.size());
|
||||
|
||||
for (std::size_t i = 0; i < children_values.size(); ++i)
|
||||
{
|
||||
if (auto const & key = children_values[i].key)
|
||||
{
|
||||
if (auto it = child_by_key.find(*key); it != child_by_key.end())
|
||||
children[i] = std::move(it->second);
|
||||
child_keys[i] = *key;
|
||||
}
|
||||
}
|
||||
|
||||
impl->set_children(std::move(children));
|
||||
impl->set_child_keys(std::move(child_keys));
|
||||
|
||||
std::vector<util::signal<std::any>::subscription_token> child_tokens(children_values.size());
|
||||
|
||||
for (std::size_t i = 0; i < children_values.size(); ++i)
|
||||
{
|
||||
child_tokens[i] = children_values[i].element.subscribe([this, impl, i](std::any const & value){
|
||||
auto children = impl->release_children();
|
||||
children[i] = reconciliate(std::move(children[i]), value);
|
||||
impl->set_children(std::move(children));
|
||||
}, true);
|
||||
}
|
||||
|
||||
impl->set_child_tokens(std::move(child_tokens));
|
||||
}, true));
|
||||
}
|
||||
|
||||
return impl;
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::type_index, util::function<std::unique_ptr<component>(std::unique_ptr<component>, std::any const &)>> type_factories_;
|
||||
};
|
||||
|
||||
}
|
||||
63
libs/ui/include/psemek/ui/impl/container.hpp
Normal file
63
libs/ui/include/psemek/ui/impl/container.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/component.hpp>
|
||||
#include <psemek/ui/key.hpp>
|
||||
#include <psemek/util/signal.hpp>
|
||||
|
||||
#include <any>
|
||||
#include <optional>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct container
|
||||
: component
|
||||
{
|
||||
void set_children(std::vector<std::unique_ptr<component>> children)
|
||||
{
|
||||
children_ = std::move(children);
|
||||
}
|
||||
|
||||
void set_children_token(util::signal<std::vector<std::any>>::subscription_token token)
|
||||
{
|
||||
children_token_ = std::move(token);
|
||||
}
|
||||
|
||||
void set_child_tokens(std::vector<util::signal<std::any>::subscription_token> tokens)
|
||||
{
|
||||
child_tokens_ = std::move(tokens);
|
||||
}
|
||||
|
||||
void set_child_keys(std::vector<std::optional<key>> keys)
|
||||
{
|
||||
child_keys_ = std::move(keys);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<component>> release_children()
|
||||
{
|
||||
return std::move(children_);
|
||||
}
|
||||
|
||||
util::signal<void>::subscription_token release_children_token()
|
||||
{
|
||||
return std::move(children_token_);
|
||||
}
|
||||
|
||||
std::vector<util::signal<void>::subscription_token> release_child_tokens()
|
||||
{
|
||||
return std::move(child_tokens_);
|
||||
}
|
||||
|
||||
std::vector<std::optional<key>> release_child_keys()
|
||||
{
|
||||
return std::move(child_keys_);
|
||||
}
|
||||
|
||||
private:
|
||||
util::signal<void>::subscription_token children_token_;
|
||||
std::vector<std::unique_ptr<component>> children_;
|
||||
std::vector<util::signal<void>::subscription_token> child_tokens_;
|
||||
std::vector<std::optional<key>> child_keys_;
|
||||
};
|
||||
|
||||
}
|
||||
23
libs/ui/include/psemek/ui/impl/controller.hpp
Normal file
23
libs/ui/include/psemek/ui/impl/controller.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/component_factory.hpp>
|
||||
#include <psemek/react/value.hpp>
|
||||
#include <psemek/util/pimpl.hpp>
|
||||
|
||||
#include <any>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct controller
|
||||
{
|
||||
controller(component_factory & factory);
|
||||
~controller();
|
||||
|
||||
void set_ui(react::value<std::any> ui);
|
||||
|
||||
private:
|
||||
psemek_declare_pimpl
|
||||
};
|
||||
|
||||
}
|
||||
40
libs/ui/include/psemek/ui/impl/single_container.hpp
Normal file
40
libs/ui/include/psemek/ui/impl/single_container.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/component.hpp>
|
||||
#include <psemek/ui/key.hpp>
|
||||
#include <psemek/util/signal.hpp>
|
||||
|
||||
#include <any>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct single_container
|
||||
: component
|
||||
{
|
||||
void set_child(std::unique_ptr<component> child)
|
||||
{
|
||||
child_ = std::move(child);
|
||||
}
|
||||
|
||||
void set_child_token(util::signal<void>::subscription_token token)
|
||||
{
|
||||
child_token_ = std::move(token);
|
||||
}
|
||||
|
||||
std::unique_ptr<component> release_child()
|
||||
{
|
||||
return std::move(child_);
|
||||
}
|
||||
|
||||
util::signal<void>::subscription_token release_child_token()
|
||||
{
|
||||
return std::move(child_token_);
|
||||
}
|
||||
|
||||
private:
|
||||
util::signal<void>::subscription_token child_token_;
|
||||
std::unique_ptr<component> child_;
|
||||
};
|
||||
|
||||
}
|
||||
10
libs/ui/include/psemek/ui/key.hpp
Normal file
10
libs/ui/include/psemek/ui/key.hpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
using key = std::string;
|
||||
|
||||
}
|
||||
19
libs/ui/include/psemek/ui/label.hpp
Normal file
19
libs/ui/include/psemek/ui/label.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/alignment.hpp>
|
||||
#include <psemek/react/value.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct label
|
||||
{
|
||||
react::value<std::string> text = {};
|
||||
react::value<halignment> halign = {};
|
||||
react::value<valignment> valign = {};
|
||||
react::value<std::string> must_fit = {};
|
||||
};
|
||||
|
||||
}
|
||||
15
libs/ui/include/psemek/ui/rectangle.hpp
Normal file
15
libs/ui/include/psemek/ui/rectangle.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/react/value.hpp>
|
||||
#include <psemek/gfx/color.hpp>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct rectagle
|
||||
{
|
||||
react::value<gfx::color_rgba> color = {};
|
||||
react::value<bool> square = {};
|
||||
};
|
||||
|
||||
}
|
||||
16
libs/ui/include/psemek/ui/slider.hpp
Normal file
16
libs/ui/include/psemek/ui/slider.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/react/value.hpp>
|
||||
#include <psemek/react/source.hpp>
|
||||
#include <psemek/geom/interval.hpp>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct slider
|
||||
{
|
||||
react::value<geom::interval<int>> range = {};
|
||||
react::source<int> value = {};
|
||||
};
|
||||
|
||||
}
|
||||
22
libs/ui/include/psemek/ui/stack_layout.hpp
Normal file
22
libs/ui/include/psemek/ui/stack_layout.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/key.hpp>
|
||||
#include <psemek/react/value.hpp>
|
||||
|
||||
#include <any>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct stack_layout
|
||||
{
|
||||
struct element
|
||||
{
|
||||
react::value<std::any> element;
|
||||
std::optional<ui::key> key = {};
|
||||
};
|
||||
|
||||
react::value<std::vector<element>> children = {};
|
||||
};
|
||||
|
||||
}
|
||||
26
libs/ui/source/impl/controller.cpp
Normal file
26
libs/ui/source/impl/controller.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include <psemek/ui/impl/controller.hpp>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct controller::impl
|
||||
{
|
||||
component_factory & factory;
|
||||
|
||||
impl(component_factory & factory)
|
||||
: factory(factory)
|
||||
{}
|
||||
};
|
||||
|
||||
controller::controller(component_factory & factory)
|
||||
: pimpl_(make_impl(factory))
|
||||
{}
|
||||
|
||||
controller::~controller() = default;
|
||||
|
||||
void controller::set_ui(react::value<std::any> ui)
|
||||
{
|
||||
(void)ui;
|
||||
}
|
||||
|
||||
}
|
||||
0
libs/ui/source/impl/reconciliator.cpp
Normal file
0
libs/ui/source/impl/reconciliator.cpp
Normal file
Loading…
Add table
Reference in a new issue