changeset 214:e2574aa8301d

client: add basic input text, closes #910
author David Demelier <markand@malikania.fr>
date Sun, 01 Sep 2019 06:53:48 +0200
parents 61580ff3138a
children 268b66d72ec0
files libmlk-client/CMakeLists.txt libmlk-client/malikania/client/button.cpp libmlk-client/malikania/client/button.hpp libmlk-client/malikania/client/input.cpp libmlk-client/malikania/client/input.hpp libmlk-client/malikania/client/pixmap_theme.cpp libmlk-client/malikania/client/pixmap_theme.hpp libmlk-client/malikania/client/texture.cpp libmlk-client/malikania/client/texture.hpp libmlk-client/malikania/client/theme.cpp libmlk-client/malikania/client/theme.hpp libmlk-client/malikania/client/widget.cpp libmlk-client/malikania/client/widget.hpp libmlk-client/malikania/client/window.cpp libmlk-client/malikania/client/window.hpp mlk-client/CMakeLists.txt mlk-client/assets/b.png mlk-client/assets/bl.png mlk-client/assets/br.png mlk-client/assets/cb-bl.png mlk-client/assets/cb-br.png mlk-client/assets/cb-tl.png mlk-client/assets/cb-tr.png mlk-client/assets/center.png mlk-client/assets/l.png mlk-client/assets/r.png mlk-client/assets/t.png mlk-client/assets/tl.png mlk-client/assets/tr.png mlk-client/main.cpp
diffstat 30 files changed, 583 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/libmlk-client/CMakeLists.txt	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/CMakeLists.txt	Sun Sep 01 06:53:48 2019 +0200
@@ -39,6 +39,8 @@
 	${PROJECT_SOURCE_DIR}/malikania/client/font.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/image.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/image.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/input.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/input.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/key.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/label.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/label.hpp
@@ -62,6 +64,8 @@
 	${PROJECT_SOURCE_DIR}/malikania/client/texture.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/theme.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/theme.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/pixmap_theme.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/pixmap_theme.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/widget.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/widget.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/window.cpp
--- a/libmlk-client/malikania/client/button.cpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/button.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -44,7 +44,7 @@
 	on_press_ = std::move(func);
 }
 
-void button::handle_event(const event& ev)
+void button::handle(const event& ev)
 {
 	const auto mev = std::get_if<mouse_click_event>(&ev);
 
--- a/libmlk-client/malikania/client/button.hpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/button.hpp	Sun Sep 01 06:53:48 2019 +0200
@@ -78,7 +78,7 @@
 	/**
 	 * \copydoc widget::handle_event
 	 */
-	void handle_event(const event& ev);
+	void handle(const event& ev);
 
 	/**
 	 * \copydoc widget::draw
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/input.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -0,0 +1,91 @@
+/*
+ * input.cpp -- basic text input
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <iostream>
+
+#include <malikania/unicode.hpp>
+
+#include "input.hpp"
+#include "theme.hpp"
+#include "window.hpp"
+
+namespace mlk::client {
+
+input::input(std::string text, std::string placeholder) noexcept
+	: text_(unicode::to_utf32(text))
+	, placeholder_(std::move(placeholder))
+{
+}
+
+auto input::get_text() const noexcept -> const std::u32string&
+{
+	return text_;
+}
+
+void input::set_text(std::u32string text) noexcept
+{
+	text_ = std::move(text);
+}
+
+void input::focus() noexcept
+{
+	widget::focus();
+	window::start_edit();
+}
+
+void input::unfocus() noexcept
+{
+	widget::unfocus();
+	window::stop_edit();
+}
+
+void input::handle_text(const text_event& ev)
+{
+	text_ += ev.text;
+	texture_ = nullptr;
+}
+
+void input::handle_key(const key_event& ev)
+{
+	switch (ev.keycode) {
+	case key::backspace:
+		if (!text_.empty()) {
+			text_.pop_back();
+			texture_ = nullptr;
+		}
+
+		break;
+	default:
+		break;
+	}
+}
+
+void input::handle(const event& event)
+{
+	if (auto ev = std::get_if<text_event>(&event); ev)
+		handle_text(*ev);
+	else if (auto ev = std::get_if<key_event>(&event); ev)
+		handle_key(*ev);
+}
+
+void input::draw(painter& painter)
+{
+	get_theme().draw_input(painter, *this);
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/input.hpp	Sun Sep 01 06:53:48 2019 +0200
@@ -0,0 +1,60 @@
+/*
+ * input.hpp -- basic text input
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MALIKANIA_CLIENT_INPUT_HPP
+#define MALIKANIA_CLIENT_INPUT_HPP
+
+#include <optional>
+#include <string>
+
+#include "widget.hpp"
+#include "texture.hpp"
+
+namespace mlk::client {
+
+struct text_event;
+struct key_event;
+
+class input : public widget {
+private:
+	std::optional<texture> texture_;
+	std::u32string text_;
+	std::string placeholder_;
+
+	void handle_text(const text_event&);
+	void handle_key(const key_event&);
+
+public:
+	input(std::string text = "", std::string placeholder = "") noexcept;
+
+	auto get_text() const noexcept -> const std::u32string&;
+
+	void set_text(std::u32string text) noexcept;
+
+	void focus() noexcept override;
+
+	void unfocus() noexcept override;
+
+	void handle(const event&) override;
+
+	void draw(painter& painter) override;
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_INPUT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/pixmap_theme.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -0,0 +1,167 @@
+/*
+ * pixmap_theme.cpp -- theme based on images
+ *
+ * Copyright (c) 2013-2019 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <algorithm>
+#include <cassert>
+
+#include <malikania/unicode.hpp>
+
+#include "input.hpp"
+#include "pixmap_theme.hpp"
+#include "painter.hpp"
+
+namespace mlk::client {
+
+#if !defined(NDEBUG)
+
+bool pixmap_theme::contains(std::initializer_list<component> components)
+{
+	for (auto c : components)
+		if (images_.find(c) == images_.end())
+			return false;
+
+	return true;
+}
+
+#endif
+
+pixmap_theme::pixmap_theme(images images) noexcept
+	: images_(std::move(images))
+{
+}
+
+void pixmap_theme::set_images(images images) noexcept
+{
+	images_ = std::move(images);
+}
+
+auto pixmap_theme::get_images() const noexcept -> const images&
+{
+	return images_;
+}
+
+auto pixmap_theme::get_images() noexcept -> images&
+{
+	return images_;
+}
+
+void pixmap_theme::draw_input(painter& painter, const input& input)
+{
+	assert(contains({
+		component::input_bottom,
+		component::input_bottom_left,
+		component::input_bottom_right,
+		component::input_center,
+		component::input_left,
+		component::input_right,
+		component::input_top,
+		component::input_top_left,
+		component::input_top_right
+	}));
+
+	const auto [x, y] = input.get_position();
+	const auto [w, h] = input.get_size();
+
+	// TOP-LEFT / TOP-RIGHT
+	auto& itl = images_.at(component::input_top_left);
+	auto& itr = images_.at(component::input_top_right);
+
+	itl.draw(painter, {x, y});
+	itr.draw(painter, {static_cast<int>(x + w - itr.get_size().width), y});
+
+	// BOTTOM-LEFT / BOTTOM-RIGHT
+	auto& ibl = images_.at(component::input_bottom_left);
+	auto& ibr = images_.at(component::input_bottom_right);
+
+	ibl.draw(painter, {x , static_cast<int>(y + h - itl.get_size().height)});
+	ibr.draw(painter, {
+		static_cast<int>(x + w - ibr.get_size().width),
+		static_cast<int>(y + h - ibl.get_size().height)
+	});
+
+	// LEFT / TOP / RIGHT / BOTTOM
+	auto& il = images_.at(component::input_left);
+	auto& it = images_.at(component::input_top);
+	auto& ir = images_.at(component::input_right);
+	auto& ib = images_.at(component::input_bottom);
+
+	il.draw(painter, {}, {
+		static_cast<int>(x),
+		static_cast<int>(y + itl.get_size().height),
+		static_cast<unsigned>(il.get_size().width),
+		static_cast<unsigned>(h - itl.get_size().height - ibl.get_size().height)
+	});
+
+	it.draw(painter, {}, {
+		static_cast<int>(x + itl.get_size().width),
+		static_cast<int>(y),
+		static_cast<unsigned>(w - itl.get_size().width - itr.get_size().height),
+		static_cast<unsigned>(it.get_size().height)
+	});
+
+	ir.draw(painter, {}, {
+		static_cast<int>(x + w - ir.get_size().width),
+		static_cast<int>(y + itr.get_size().height),
+		static_cast<unsigned>(ir.get_size().width),
+		static_cast<unsigned>(h - itr.get_size().height - ibr.get_size().height)
+	});
+
+	ib.draw(painter, {}, {
+		static_cast<int>(x + ibl.get_size().width),
+		static_cast<int>(y + h - ib.get_size().height),
+		static_cast<unsigned>(w - ibl.get_size().width - ibr.get_size().height),
+		static_cast<unsigned>(ib.get_size().height)
+	});
+
+	// CENTER
+	auto& ic = images_.at(component::input_center);
+
+	ic.draw(painter, {}, {
+		static_cast<int>(x + itl.get_size().width),
+		static_cast<int>(y + itl.get_size().height),
+		static_cast<unsigned>(w - itl.get_size().width - itr.get_size().width),
+		static_cast<unsigned>(h - ibl.get_size().height - ibr.get_size().height)
+	});
+
+	// The text.
+	const auto& text = unicode::to_utf8(input.get_text());
+
+	if (text.empty())
+		return;
+
+	auto texture = get_font().render(painter, text);
+	const auto [tw, th] = texture.get_size();
+
+	// This code should live in texture.
+	const rectangle src{
+		static_cast<int>(0 + std::max(0, static_cast<int>(tw - w))),
+		static_cast<int>(0),
+		static_cast<unsigned>(std::min(w, tw)),
+		static_cast<unsigned>(th)
+	};
+	const rectangle dst{
+		static_cast<int>(x),
+		static_cast<int>(y + (h / 2) - (th / 2)),
+		src.width,
+		src.height
+	};
+
+	texture.draw(painter, src, dst);
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/pixmap_theme.hpp	Sun Sep 01 06:53:48 2019 +0200
@@ -0,0 +1,68 @@
+/*
+ * pixmap_theme.hpp -- theme based on images
+ *
+ * Copyright (c) 2013-2019 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MALIKANIA_CLIENT_PIXMAP_THEME_HPP
+#define MALIKANIA_CLIENT_PIXMAP_THEME_HPP
+
+#include <initializer_list>
+#include <string>
+#include <unordered_map>
+
+#include "theme.hpp"
+#include "image.hpp"
+
+namespace mlk::client {
+
+class pixmap_theme : public theme {
+public:
+	enum class component {
+		input_bottom,
+		input_bottom_left,
+		input_bottom_right,
+		input_center,
+		input_left,
+		input_right,
+		input_top,
+		input_top_left,
+		input_top_right
+	};
+
+	using images = std::unordered_map<component, image>;
+
+private:
+	images images_;
+
+#if !defined(NDEBUG)
+	bool contains(std::initializer_list<component>);
+#endif
+
+public:
+	pixmap_theme(images images) noexcept;
+
+	void set_images(images images) noexcept;
+
+	auto get_images() const noexcept -> const images&;
+
+	auto get_images() noexcept -> images&;
+
+	void draw_input(painter& painter, const input& input) override;
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_PIXMAP_THEME_HPP
--- a/libmlk-client/malikania/client/texture.cpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/texture.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -80,6 +80,16 @@
 	return texture_.get();
 }
 
+auto texture::get_size() const noexcept -> const size&
+{
+	return size_;
+}
+
+auto texture::get_size() noexcept -> size&
+{
+	return size_;
+}
+
 void texture::draw(painter& painter, const point& position)
 {
 	const rectangle source{
--- a/libmlk-client/malikania/client/texture.hpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/texture.hpp	Sun Sep 01 06:53:48 2019 +0200
@@ -95,6 +95,21 @@
 	auto get_texture() noexcept -> SDL_Texture*;
 
 	/**
+	 * Get the texture size.
+	 *
+	 * \return the size
+	 */
+	auto get_size() const noexcept -> const size&;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the size
+	 */
+	auto get_size() noexcept -> size&;
+
+
+	/**
 	 * Copy the texture to the painter.
 	 *
 	 * \param painter the painter
--- a/libmlk-client/malikania/client/theme.cpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/theme.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -18,10 +18,13 @@
 
 #include <malikania/point.hpp>
 #include <malikania/rectangle.hpp>
+#include <malikania/unicode.hpp>
 
 #include "button.hpp"
 #include "color.hpp"
+#include "input.hpp"
 #include "label.hpp"
+#include "painter.hpp"
 #include "theme.hpp"
 #include "window.hpp"
 
@@ -33,23 +36,25 @@
 
 } // !namespace
 
-std::unique_ptr<theme> theme::global;
+theme* theme::global;
 
 auto theme::get_default() noexcept -> theme&
 {
+	static theme def;
+
 	if (!global)
-		global = std::make_unique<theme>();
+		return def;
 
 	return *global;
 }
 
-void theme::set_default(std::unique_ptr<theme> theme) noexcept
+void theme::set_default(theme& theme) noexcept
 {
-	global = std::move(theme);
+	global = std::addressof(theme);
 }
 
 theme::theme()
-	: font_(std::string(dejavu_sans, sizeof (dejavu_sans)), 10)
+	: font_(std::string(dejavu_sans, sizeof (dejavu_sans)), 20)
 	, background_color_{228, 228, 228, 255}
 	, border_color_{102, 102, 102, 255}
 	, text_color_{50, 50, 50, 255}
@@ -81,7 +86,7 @@
 	return text_color_;
 }
 
-void theme::draw_button(painter& painter, const button& button, const rectangle& destination)
+void theme::draw_button(painter& painter, const button& button)
 {
 #if 0
 	// Border.
@@ -99,7 +104,7 @@
 #endif
 }
 
-void theme::draw_label(painter& painter, const label& label, const rectangle& destination)
+void theme::draw_label(painter& painter, const label& label)
 {
 #if 0
 	w.set_drawing_color(text_color_);
@@ -107,4 +112,29 @@
 #endif
 }
 
+void theme::draw_input(painter& painter, const input& input)
+{
+	const auto position = input.get_position();
+	const auto size = input.get_size();
+	const auto utf8 = unicode::to_utf8(input.get_text());
+
+	painter.set_drawing_color(color{0, 0, 0, 0});
+	painter.draw_rectangle({position.x, position.y, size.width, size.height});
+
+	if (utf8.size() > 0) {
+		auto texture = font_.render(painter, utf8);
+		auto [x, y] = texture.get_size();
+#if 0
+		const auto [tw, th] = font_.clip(utf8);
+		const auto y = position.y + static_cast<int>(size.height) / 2 - static_cast<int>(th) / 2;
+		const auto x = position.x + 2;
+
+		auto texture = font_.render(painter, utf8);
+
+		painter.set_drawing_color(color{0, 0, 0, 0});
+		texture.render(
+#endif
+	}
+}
+
 } // !mlk::client
--- a/libmlk-client/malikania/client/theme.hpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/theme.hpp	Sun Sep 01 06:53:48 2019 +0200
@@ -38,6 +38,7 @@
 namespace client {
 
 class button;
+class input;
 class label;
 class painter;
 
@@ -46,7 +47,7 @@
  */
 class theme {
 private:
-	static std::unique_ptr<theme> global;
+	static theme* global;
 
 	font font_;
 	color background_color_;
@@ -66,7 +67,7 @@
 	 *
 	 * \param theme the theme
 	 */
-	static void set_default(std::unique_ptr<theme> theme) noexcept;
+	static void set_default(theme& theme) noexcept;
 
 	/**
 	 * Default constructor.
@@ -118,18 +119,24 @@
 	 *
 	 * \param painter the painter
 	 * \param button the button
-	 * \param destination the destination
 	 */
-	virtual void draw_button(painter& painter, const button& button, const rectangle& destination);
+	virtual void draw_button(painter& painter, const button& button);
 
 	/**
 	 * Draw a label.
 	 *
 	 * \param painter the painter
 	 * \param label the label
-	 * \param destination the destination
 	 */
-	virtual void draw_label(painter& painter, const label& label, const rectangle& destination);
+	virtual void draw_label(painter& painter, const label& label);
+
+	/**
+	 * Draw an input text.
+	 *
+	 * \param painter the painter
+	 * \param input the input
+	 */
+	virtual void draw_input(painter& painter, const input& input);
 };
 
 } // !client
--- a/libmlk-client/malikania/client/widget.cpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/widget.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -94,4 +94,8 @@
 	state_ = state::normal;
 }
 
+void widget::handle(const event&)
+{
+}
+
 } // !mlk::client
--- a/libmlk-client/malikania/client/widget.hpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/widget.hpp	Sun Sep 01 06:53:48 2019 +0200
@@ -31,6 +31,7 @@
 
 namespace client {
 
+class event;
 class painter;
 class theme;
 
@@ -151,6 +152,13 @@
 	virtual void show() noexcept;
 
 	/**
+	 * Handle event.
+	 *
+	 * \param event the event
+	 */
+	virtual void handle(const event& event);
+
+	/**
 	 * Draw the widget.
 	 *
 	 * \param painter the painter
--- a/libmlk-client/malikania/client/window.cpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/window.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -356,8 +356,17 @@
 	return mev;
 }
 
+auto create_text_input(const SDL_Event& ev) -> event
+{
+	assert(ev.type == SDL_TEXTINPUT);
+
+	return text_event{unicode::to_utf32(ev.text.text)};
+}
+
 } // !namespace
 
+bool window::is_editing_{false};
+
 void window::init()
 {
 	static bool is_initialized{false};
@@ -374,6 +383,23 @@
 	}
 }
 
+void window::start_edit()
+{
+	if (!is_editing_) {
+		SDL_StartTextInput();
+		is_editing_ = true;
+	}
+}
+
+void window::stop_edit()
+{
+	if (is_editing_) {
+		SDL_StopTextInput();
+		is_editing_ = false;
+	}
+}
+
+
 window::window(unsigned width, unsigned height, const std::string& title)
 {
 	init();
@@ -412,22 +438,6 @@
 	return renderer_.get();
 }
 
-void window::start_edit()
-{
-	if (!is_editing_) {
-		SDL_StartTextInput();
-		is_editing_ = true;
-	}
-}
-
-void window::stop_edit()
-{
-	if (is_editing_) {
-		SDL_StopTextInput();
-		is_editing_ = false;
-	}
-}
-
 auto window::poll() -> event
 {
 	SDL_Event event;
@@ -442,6 +452,8 @@
 			return create_mouse_event(event);
 		case SDL_QUIT:
 			return quit_event();
+		case SDL_TEXTINPUT:
+			return create_text_input(event);
 		default:
 			// Skip non-managed events.
 			break;
--- a/libmlk-client/malikania/client/window.hpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/libmlk-client/malikania/client/window.hpp	Sun Sep 01 06:53:48 2019 +0200
@@ -40,15 +40,31 @@
  */
 class window {
 private:
+	static bool is_editing_;
+
 	std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window_{nullptr, nullptr};
 	std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> renderer_{nullptr, nullptr};
 
-	bool is_editing_{false};
-
 	void init();
 
 public:
 	/**
+	 * Start editing input.
+	 *
+	 * \note may open a visual keyboard on some platforms
+	 * \note can safely be called multiple times
+	 */
+	static void start_edit();
+
+	/**
+	 * Stop editing input.
+	 *
+	 * \note may close the visual keyboard on some platforms
+	 * \note can safely be called multiple times
+	 */
+	static void stop_edit();
+
+	/**
 	 * Create a window.
 	 *
 	 * \param width the initial width
@@ -78,22 +94,6 @@
 	auto get_renderer() noexcept -> SDL_Renderer*;
 
 	/**
-	 * Start editing input.
-	 *
-	 * \note may open a visual keyboard on some platforms
-	 * \note can safely be called multiple times
-	 */
-	void start_edit();
-
-	/**
-	 * Stop editing input.
-	 *
-	 * \note may close the visual keyboard on some platforms
-	 * \note can safely be called multiple times
-	 */
-	void stop_edit();
-
-	/**
 	 * Poll the next event and return it.
 	 *
 	 * \return the event or empty one if not available
--- a/mlk-client/CMakeLists.txt	Wed Feb 27 13:39:12 2019 +0100
+++ b/mlk-client/CMakeLists.txt	Sun Sep 01 06:53:48 2019 +0200
@@ -26,7 +26,16 @@
 find_package(Threads REQUIRED)
 
 malikania_define_executable(
-	ASSETS ${mlk-client_SOURCE_DIR}/assets/ui.png
+	ASSETS 
+		${mlk-client_SOURCE_DIR}/assets/center.png
+		${mlk-client_SOURCE_DIR}/assets/b.png
+		${mlk-client_SOURCE_DIR}/assets/bl.png
+		${mlk-client_SOURCE_DIR}/assets/br.png
+		${mlk-client_SOURCE_DIR}/assets/l.png
+		${mlk-client_SOURCE_DIR}/assets/r.png
+		${mlk-client_SOURCE_DIR}/assets/t.png
+		${mlk-client_SOURCE_DIR}/assets/tl.png
+		${mlk-client_SOURCE_DIR}/assets/tr.png
 	TARGET mlk-client
 	SOURCES ${SOURCES}
 	LIBRARIES
Binary file mlk-client/assets/b.png has changed
Binary file mlk-client/assets/bl.png has changed
Binary file mlk-client/assets/br.png has changed
Binary file mlk-client/assets/cb-bl.png has changed
Binary file mlk-client/assets/cb-br.png has changed
Binary file mlk-client/assets/cb-tl.png has changed
Binary file mlk-client/assets/cb-tr.png has changed
Binary file mlk-client/assets/center.png has changed
Binary file mlk-client/assets/l.png has changed
Binary file mlk-client/assets/r.png has changed
Binary file mlk-client/assets/t.png has changed
Binary file mlk-client/assets/tl.png has changed
Binary file mlk-client/assets/tr.png has changed
--- a/mlk-client/main.cpp	Wed Feb 27 13:39:12 2019 +0100
+++ b/mlk-client/main.cpp	Sun Sep 01 06:53:48 2019 +0200
@@ -21,55 +21,70 @@
 #include <thread>
 
 #include <malikania/client/color.hpp>
-#include <malikania/client/button.hpp>
 #include <malikania/client/window.hpp>
 #include <malikania/client/painter.hpp>
-#include <malikania/client/texture.hpp>
+#include <malikania/client/input.hpp>
+#include <malikania/client/image.hpp>
+#include <malikania/client/pixmap_theme.hpp>
+
 #include <malikania/rectangle.hpp>
 
+namespace assets {
+
+#include <assets/b.hpp>
+#include <assets/center.hpp>
+#include <assets/bl.hpp>
+#include <assets/br.hpp>
+#include <assets/l.hpp>
+#include <assets/r.hpp>
+#include <assets/t.hpp>
+#include <assets/tl.hpp>
+#include <assets/tr.hpp>
+
+} // !assets
+
 using namespace std::chrono_literals;
 
 int main(int argc, char** argv)
 {
-	mlk::client::window win{500, 500};
+	mlk::client::window win{1920, 1080};
+	mlk::client::pixmap_theme mytheme({});
+	mlk::client::theme::set_default(mytheme);
 	mlk::client::painter painter{win};
-
-	// global window painter
-	painter.set_drawing_color({255, 255, 255, 255});
-	painter.clear();
+	mlk::client::input input;
 
-	mlk::client::texture red{painter, 100, 100};
-	mlk::client::texture blue{painter, 50, 50};
-	mlk::client::texture green{painter, 25, 25};
+	input.set_position({100, 150});
+	input.set_size({300, 64});
+	input.focus();
 
-	{
-		mlk::client::painter pred{win, red};
+	using mlk::client::pixmap_theme;
+	using mlk::client::image;
+
+	pixmap_theme::images images;
 
-		pred.set_drawing_color({255, 0, 0, 255});
-		pred.clear();
-
-		{
-			mlk::client::painter pblue{win, blue};
-
-			pblue.set_drawing_color({0, 0, 255, 255});
-			pblue.clear();
+	images.emplace(pixmap_theme::component::input_bottom, std::string(assets::b, sizeof (assets::b)));
+	images.emplace(pixmap_theme::component::input_bottom_left, std::string(assets::bl, sizeof (assets::bl)));
+	images.emplace(pixmap_theme::component::input_bottom_right, std::string(assets::br, sizeof (assets::br)));
+	images.emplace(pixmap_theme::component::input_center, std::string(assets::center, sizeof (assets::center)));
+	images.emplace(pixmap_theme::component::input_left, std::string(assets::l, sizeof (assets::l)));
+	images.emplace(pixmap_theme::component::input_right, std::string(assets::r, sizeof (assets::r)));
+	images.emplace(pixmap_theme::component::input_top, std::string(assets::t, sizeof (assets::t)));
+	images.emplace(pixmap_theme::component::input_top_left, std::string(assets::tl, sizeof (assets::tl)));
+	images.emplace(pixmap_theme::component::input_top_right, std::string(assets::tr, sizeof (assets::tr)));
 
-			{
-				mlk::client::painter pgreen{win, green};
+	mytheme.set_images(std::move(images));
 
-				pgreen.set_drawing_color({0, 255, 0, 255});
-				pgreen.clear();
+	for (;;) {
+		while (auto ev = win.poll()) {
+			if (std::get_if<mlk::client::quit_event>(&ev))
+				return 0;
 
-			}
-
-			green.draw(pblue, {25/2, 25/2});
+			input.handle(ev);
 		}
 
-		blue.draw(pred, {25, 25});
+		painter.set_drawing_color({255, 255, 255, 255});
+		painter.clear();
+                input.draw(painter);
+		painter.present();
 	}
-
-	red.draw(painter, {200, 200});
-	painter.present();
-
-	std::this_thread::sleep_for(5s);
 }