| @@ -0,0 +1,113 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STDEXT_PACKEDSTORAGE_H | |
| 24 | + | #define STDEXT_PACKEDSTORAGE_H | |
| 25 | + | ||
| 26 | + | #include "types.h" | |
| 27 | + | #include "packed_any.h" | |
| 28 | + | ||
| 29 | + | namespace stdext { | |
| 30 | + | ||
| 31 | + | // disable memory alignment | |
| 32 | + | #pragma pack(push,1) | |
| 33 | + | ||
| 34 | + | // this class was designed to use less memory as possible | |
| 35 | + | template<typename Key, typename SizeType = uint8> | |
| 36 | + | class packed_storage { | |
| 37 | + | struct value_pair { | |
| 38 | + | Key id; | |
| 39 | + | packed_any value; | |
| 40 | + | }; | |
| 41 | + | ||
| 42 | + | public: | |
| 43 | + | packed_storage() : m_values(nullptr), m_size(0) { } | |
| 44 | + | ~packed_storage() { if(m_values) delete[] m_values; } | |
| 45 | + | ||
| 46 | + | template<typename T> | |
| 47 | + | void set(Key id, const T& value) { | |
| 48 | + | for(SizeType i=0;i<m_size;++i) { | |
| 49 | + | if(m_values[i].id == id) { | |
| 50 | + | m_values[i].value = value; | |
| 51 | + | return; | |
| 52 | + | } | |
| 53 | + | } | |
| 54 | + | auto tmp = new value_pair[m_size+1]; | |
| 55 | + | if(m_size > 0) { | |
| 56 | + | std::copy(m_values, m_values + m_size, tmp); | |
| 57 | + | delete[] m_values; | |
| 58 | + | } | |
| 59 | + | m_values = tmp; | |
| 60 | + | m_values[m_size++] = { id, packed_any(value) }; | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | bool remove(Key id) { | |
| 64 | + | auto begin = m_values; | |
| 65 | + | auto end = m_values + m_size; | |
| 66 | + | auto it = std::find_if(begin, end, [=](const value_pair& pair) -> bool { return pair.id == id; } ); | |
| 67 | + | if(it == end) | |
| 68 | + | return false; | |
| 69 | + | int pos = it - begin; | |
| 70 | + | auto tmp = new value_pair[m_size-1]; | |
| 71 | + | std::copy(begin, begin + pos, tmp); | |
| 72 | + | std::copy(begin + pos + 1, end, tmp + pos); | |
| 73 | + | delete[] m_values; | |
| 74 | + | m_values = tmp; | |
| 75 | + | m_size--; | |
| 76 | + | return true; | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | template<typename T> | |
| 80 | + | T get(Key id) const { | |
| 81 | + | for(SizeType i=0;i<m_size;++i) | |
| 82 | + | if(m_values[i].id == id) | |
| 83 | + | return packed_any_cast<T>(m_values[i].value); | |
| 84 | + | return T(); | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | bool has(Key id) const { | |
| 88 | + | for(SizeType i=0;i<m_size;++i) | |
| 89 | + | if(m_values[i].id == id) | |
| 90 | + | return true; | |
| 91 | + | return false; | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | void clear() { | |
| 95 | + | if(m_values) | |
| 96 | + | delete [] m_values; | |
| 97 | + | m_values = nullptr; | |
| 98 | + | m_size = 0; | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | std::size_t size() { return m_size; } | |
| 102 | + | ||
| 103 | + | private: | |
| 104 | + | value_pair *m_values; | |
| 105 | + | SizeType m_size; | |
| 106 | + | }; | |
| 107 | + | ||
| 108 | + | // restore memory alignment | |
| 109 | + | #pragma pack(pop) | |
| 110 | + | ||
| 111 | + | } | |
| 112 | + | ||
| 113 | + | #endif |
| @@ -0,0 +1,132 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STDEXT_SHARED_OBJECT_H | |
| 24 | + | #define STDEXT_SHARED_OBJECT_H | |
| 25 | + | ||
| 26 | + | #include "types.h" | |
| 27 | + | #include <type_traits> | |
| 28 | + | #include <functional> | |
| 29 | + | #include <ostream> | |
| 30 | + | #include <atomic> | |
| 31 | + | ||
| 32 | + | namespace stdext { | |
| 33 | + | ||
| 34 | + | template<class T> | |
| 35 | + | class shared_object_ptr; | |
| 36 | + | ||
| 37 | + | class shared_object | |
| 38 | + | { | |
| 39 | + | public: | |
| 40 | + | shared_object() : refs(0) { } | |
| 41 | + | virtual ~shared_object() { } | |
| 42 | + | void add_ref() { ++refs; } | |
| 43 | + | void dec_ref() { if(--refs == 0) delete this; } | |
| 44 | + | refcount_t ref_count() { return refs; } | |
| 45 | + | ||
| 46 | + | template<typename T> stdext::shared_object_ptr<T> static_self_cast() { return stdext::shared_object_ptr<T>(static_cast<T*>(this)); } | |
| 47 | + | template<typename T> stdext::shared_object_ptr<T> dynamic_self_cast() { return stdext::shared_object_ptr<T>(dynamic_cast<T*>(this)); } | |
| 48 | + | template<typename T> stdext::shared_object_ptr<T> const_self_cast() { return stdext::shared_object_ptr<T>(const_cast<T*>(this)); } | |
| 49 | + | ||
| 50 | + | private: | |
| 51 | + | std::atomic<refcount_t> refs; | |
| 52 | + | }; | |
| 53 | + | ||
| 54 | + | template<class T> | |
| 55 | + | class shared_object_ptr | |
| 56 | + | { | |
| 57 | + | public: | |
| 58 | + | typedef T element_type; | |
| 59 | + | ||
| 60 | + | shared_object_ptr(): px(nullptr) { } | |
| 61 | + | shared_object_ptr(T* p, bool add_ref = true) : px(p) { | |
| 62 | + | static_assert(std::is_base_of<shared_object, T>::value, "classes using shared_object_ptr must be a derived of stdext::shared_object"); | |
| 63 | + | if(px != nullptr && add_ref) | |
| 64 | + | this->add_ref(); | |
| 65 | + | } | |
| 66 | + | shared_object_ptr(shared_object_ptr const& rhs): px(rhs.px) { if(px != nullptr) add_ref(); } | |
| 67 | + | template<class U> | |
| 68 | + | shared_object_ptr(shared_object_ptr<U> const& rhs, typename std::enable_if<std::is_convertible<U*,T*>::value, U*>::type = nullptr) : px(rhs.get()) { if(px != nullptr) add_ref(); } | |
| 69 | + | ~shared_object_ptr() { if(px != nullptr) dec_ref(); } | |
| 70 | + | ||
| 71 | + | void reset() { shared_object_ptr().swap(*this); } | |
| 72 | + | void reset(T* rhs) { shared_object_ptr(rhs).swap(*this); } | |
| 73 | + | void swap(shared_object_ptr& rhs) { std::swap(px, rhs.px); } | |
| 74 | + | T* get() const { return px; } | |
| 75 | + | ||
| 76 | + | refcount_t use_count() const { return ((shared_object*)px)->ref_count(); } | |
| 77 | + | bool is_unique() const { return use_count() == 1; } | |
| 78 | + | ||
| 79 | + | template<class U> shared_object_ptr& operator=(shared_object_ptr<U> const& rhs) { shared_object_ptr(rhs).swap(*this); return *this; } | |
| 80 | + | ||
| 81 | + | T& operator*() const { VALIDATE(px != nullptr); return *px; } | |
| 82 | + | T* operator->() const { VALIDATE(px != nullptr); return px; } | |
| 83 | + | ||
| 84 | + | shared_object_ptr& operator=(shared_object_ptr const& rhs) { shared_object_ptr(rhs).swap(*this); return *this; } | |
| 85 | + | shared_object_ptr& operator=(T* rhs) { shared_object_ptr(rhs).swap(*this); return *this; } | |
| 86 | + | ||
| 87 | + | // implicit conversion to bool | |
| 88 | + | typedef T* shared_object_ptr::*unspecified_bool_type; | |
| 89 | + | operator unspecified_bool_type() const { return px == nullptr ? nullptr : &shared_object_ptr::px; } | |
| 90 | + | bool operator!() const { return px == nullptr; } | |
| 91 | + | ||
| 92 | + | // std::move support | |
| 93 | + | shared_object_ptr(shared_object_ptr&& rhs): px(rhs.px) { rhs.px = nullptr; } | |
| 94 | + | shared_object_ptr& operator=(shared_object_ptr&& rhs) { shared_object_ptr(static_cast<shared_object_ptr&&>(rhs)).swap(*this); return *this; } | |
| 95 | + | ||
| 96 | + | private: | |
| 97 | + | void add_ref() { ((shared_object*)px)->add_ref(); } | |
| 98 | + | void dec_ref() { ((shared_object*)px)->dec_ref(); } | |
| 99 | + | ||
| 100 | + | T* px; | |
| 101 | + | }; | |
| 102 | + | ||
| 103 | + | template<class T, class U> bool operator==(shared_object_ptr<T> const& a, shared_object_ptr<U> const& b) { return a.get() == b.get(); } | |
| 104 | + | template<class T, class U> bool operator!=(shared_object_ptr<T> const& a, shared_object_ptr<U> const& b) { return a.get() != b.get(); } | |
| 105 | + | template<class T, class U> bool operator==(shared_object_ptr<T> const& a, U* b) { return a.get() == b; } | |
| 106 | + | template<class T, class U> bool operator!=(shared_object_ptr<T> const& a, U* b) { return a.get() != b; } | |
| 107 | + | template<class T, class U> bool operator==(T * a, shared_object_ptr<U> const& b) { return a == b.get(); } | |
| 108 | + | template<class T, class U> bool operator!=(T * a, shared_object_ptr<U> const& b) { return a != b.get(); } | |
| 109 | + | template<class T> bool operator<(shared_object_ptr<T> const& a, shared_object_ptr<T> const& b) { return std::less<T*>()(a.get(), b.get()); } | |
| 110 | + | ||
| 111 | + | template<class T> T* get_pointer(shared_object_ptr<T> const& p) { return p.get(); } | |
| 112 | + | template<class T, class U> shared_object_ptr<T> static_pointer_cast(shared_object_ptr<U> const& p) { return static_cast<T*>(p.get()); } | |
| 113 | + | template<class T, class U> shared_object_ptr<T> const_pointer_cast(shared_object_ptr<U> const& p) { return const_cast<T*>(p.get()); } | |
| 114 | + | template<class T, class U> shared_object_ptr<T> dynamic_pointer_cast(shared_object_ptr<U> const& p) { return dynamic_cast<T*>(p.get()); } | |
| 115 | + | template<class T, typename... Args> stdext::shared_object_ptr<T> make_shared_object(Args... args) { return stdext::shared_object_ptr<T>(new T(args...)); } | |
| 116 | + | ||
| 117 | + | // operator<< support | |
| 118 | + | template<class E, class T, class Y> std::basic_ostream<E, T>& operator<<(std::basic_ostream<E, T>& os, shared_object_ptr<Y> const& p) { os << p.get(); return os; } | |
| 119 | + | ||
| 120 | + | } | |
| 121 | + | ||
| 122 | + | namespace std { | |
| 123 | + | ||
| 124 | + | // hash, for unordered_map support | |
| 125 | + | template<typename T> struct hash<stdext::shared_object_ptr<T>> { size_t operator()(const stdext::shared_object_ptr<T>& p) const { return std::hash<T*>()(p.get()); } }; | |
| 126 | + | ||
| 127 | + | // swap support | |
| 128 | + | template<class T> void swap(stdext::shared_object_ptr<T>& lhs, stdext::shared_object_ptr<T>& rhs) { lhs.swap(rhs); } | |
| 129 | + | ||
| 130 | + | } | |
| 131 | + | ||
| 132 | + | #endif |
| @@ -0,0 +1,43 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STDEXT_H | |
| 24 | + | #define STDEXT_H | |
| 25 | + | ||
| 26 | + | #include "compiler.h" | |
| 27 | + | #include "dumper.h" | |
| 28 | + | #include "types.h" | |
| 29 | + | #include "exception.h" | |
| 30 | + | #include "demangle.h" | |
| 31 | + | #include "cast.h" | |
| 32 | + | #include "math.h" | |
| 33 | + | #include "string.h" | |
| 34 | + | #include "time.h" | |
| 35 | + | #include "boolean.h" | |
| 36 | + | #include "shared_object.h" | |
| 37 | + | #include "any.h" | |
| 38 | + | #include "packed_any.h" | |
| 39 | + | #include "dynamic_storage.h" | |
| 40 | + | #include "packed_storage.h" | |
| 41 | + | #include "format.h" | |
| 42 | + | ||
| 43 | + | #endif |
| @@ -0,0 +1,301 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include <framework/global.h> | |
| 24 | + | ||
| 25 | + | #include "string.h" | |
| 26 | + | #include "format.h" | |
| 27 | + | #include <boost/algorithm/string.hpp> | |
| 28 | + | #include <ctype.h> | |
| 29 | + | #include <physfs.h> | |
| 30 | + | ||
| 31 | + | #ifdef _MSC_VER | |
| 32 | + | #pragma warning(disable:4267) // '?' : conversion from 'A' to 'B', possible loss of data | |
| 33 | + | #endif | |
| 34 | + | ||
| 35 | + | namespace stdext { | |
| 36 | + | ||
| 37 | + | std::string resolve_path(const std::string& filePath, std::string sourcePath) | |
| 38 | + | { | |
| 39 | + | if (filePath.empty()) | |
| 40 | + | return ""; | |
| 41 | + | if(stdext::starts_with(filePath, "/")) | |
| 42 | + | return filePath; | |
| 43 | + | if(!stdext::ends_with(sourcePath, "/")) { | |
| 44 | + | std::size_t slashPos = sourcePath.find_last_of("/"); | |
| 45 | + | if(slashPos == std::string::npos) | |
| 46 | + | throw_exception(format("invalid source path '%s', for file '%s'", sourcePath, filePath)); | |
| 47 | + | sourcePath = sourcePath.substr(0, slashPos + 1); | |
| 48 | + | } | |
| 49 | + | return sourcePath + filePath; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | std::string date_time_string() | |
| 53 | + | { | |
| 54 | + | char date[32]; | |
| 55 | + | std::time_t tnow; | |
| 56 | + | std::time(&tnow); | |
| 57 | + | std::tm* ts = std::localtime(&tnow); | |
| 58 | + | std::strftime(date, 32, "%b %d %Y %H:%M:%S", ts); | |
| 59 | + | return std::string(date); | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | std::string timestamp_to_date(time_t tnow) | |
| 63 | + | { | |
| 64 | + | char date[32]; | |
| 65 | + | std::tm* ts = std::localtime(&tnow); | |
| 66 | + | std::strftime(date, 32, "%b %d %Y %H:%M:%S", ts); | |
| 67 | + | return std::string(date); | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | std::string dec_to_hex(uint32_t num) | |
| 71 | + | { | |
| 72 | + | std::string str; | |
| 73 | + | std::ostringstream o; | |
| 74 | + | o << std::setw(8) << std::setfill('0') << std::hex << num; | |
| 75 | + | str = o.str(); | |
| 76 | + | return str; | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | std::string dec_to_hex(uint64_t num) | |
| 80 | + | { | |
| 81 | + | std::string str; | |
| 82 | + | std::ostringstream o; | |
| 83 | + | o << std::setw(16) << std::setfill('0') << std::hex << num; | |
| 84 | + | str = o.str(); | |
| 85 | + | return str; | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | uint64_t hex_to_dec(const std::string& str) | |
| 89 | + | { | |
| 90 | + | uint64_t num; | |
| 91 | + | std::istringstream i(str); | |
| 92 | + | i >> std::hex >> num; | |
| 93 | + | return num; | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | bool is_valid_utf8(const std::string& src) | |
| 97 | + | { | |
| 98 | + | const unsigned char *bytes = (const unsigned char *)src.c_str(); | |
| 99 | + | while(*bytes) { | |
| 100 | + | if( (// ASCII | |
| 101 | + | // use bytes[0] <= 0x7F to allow ASCII control characters | |
| 102 | + | bytes[0] == 0x09 || | |
| 103 | + | bytes[0] == 0x0A || | |
| 104 | + | bytes[0] == 0x0D || | |
| 105 | + | (0x20 <= bytes[0] && bytes[0] <= 0x7E) | |
| 106 | + | ) | |
| 107 | + | ) { | |
| 108 | + | bytes += 1; | |
| 109 | + | continue; | |
| 110 | + | } | |
| 111 | + | if( (// non-overlong 2-byte | |
| 112 | + | (0xC2 <= bytes[0] && bytes[0] <= 0xDF) && | |
| 113 | + | (0x80 <= bytes[1] && bytes[1] <= 0xBF) | |
| 114 | + | ) | |
| 115 | + | ) { | |
| 116 | + | bytes += 2; | |
| 117 | + | continue; | |
| 118 | + | } | |
| 119 | + | if( (// excluding overlongs | |
| 120 | + | bytes[0] == 0xE0 && | |
| 121 | + | (0xA0 <= bytes[1] && bytes[1] <= 0xBF) && | |
| 122 | + | (0x80 <= bytes[2] && bytes[2] <= 0xBF) | |
| 123 | + | ) || | |
| 124 | + | (// straight 3-byte | |
| 125 | + | ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) || | |
| 126 | + | bytes[0] == 0xEE || | |
| 127 | + | bytes[0] == 0xEF) && | |
| 128 | + | (0x80 <= bytes[1] && bytes[1] <= 0xBF) && | |
| 129 | + | (0x80 <= bytes[2] && bytes[2] <= 0xBF) | |
| 130 | + | ) || | |
| 131 | + | (// excluding surrogates | |
| 132 | + | bytes[0] == 0xED && | |
| 133 | + | (0x80 <= bytes[1] && bytes[1] <= 0x9F) && | |
| 134 | + | (0x80 <= bytes[2] && bytes[2] <= 0xBF) | |
| 135 | + | ) | |
| 136 | + | ) { | |
| 137 | + | bytes += 3; | |
| 138 | + | continue; | |
| 139 | + | } | |
| 140 | + | if( (// planes 1-3 | |
| 141 | + | bytes[0] == 0xF0 && | |
| 142 | + | (0x90 <= bytes[1] && bytes[1] <= 0xBF) && | |
| 143 | + | (0x80 <= bytes[2] && bytes[2] <= 0xBF) && | |
| 144 | + | (0x80 <= bytes[3] && bytes[3] <= 0xBF) | |
| 145 | + | ) || | |
| 146 | + | (// planes 4-15 | |
| 147 | + | (0xF1 <= bytes[0] && bytes[0] <= 0xF3) && | |
| 148 | + | (0x80 <= bytes[1] && bytes[1] <= 0xBF) && | |
| 149 | + | (0x80 <= bytes[2] && bytes[2] <= 0xBF) && | |
| 150 | + | (0x80 <= bytes[3] && bytes[3] <= 0xBF) | |
| 151 | + | ) || | |
| 152 | + | (// plane 16 | |
| 153 | + | bytes[0] == 0xF4 && | |
| 154 | + | (0x80 <= bytes[1] && bytes[1] <= 0x8F) && | |
| 155 | + | (0x80 <= bytes[2] && bytes[2] <= 0xBF) && | |
| 156 | + | (0x80 <= bytes[3] && bytes[3] <= 0xBF) | |
| 157 | + | ) | |
| 158 | + | ) { | |
| 159 | + | bytes += 4; | |
| 160 | + | continue; | |
| 161 | + | } | |
| 162 | + | return false; | |
| 163 | + | } | |
| 164 | + | return true; | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | std::string utf8_to_latin1(const std::string& src) | |
| 168 | + | { | |
| 169 | + | std::string out; | |
| 170 | + | for(uint i=0;i<src.length();) { | |
| 171 | + | uchar c = src[i++]; | |
| 172 | + | if((c >= 32 && c < 128) || c == 0x0d || c == 0x0a || c == 0x09) | |
| 173 | + | out += c; | |
| 174 | + | else if(c == 0xc2 || c == 0xc3) { | |
| 175 | + | uchar c2 = src[i++]; | |
| 176 | + | if(c == 0xc2) { | |
| 177 | + | if(c2 > 0xa1 && c2 < 0xbb) | |
| 178 | + | out += c2; | |
| 179 | + | } else if(c == 0xc3) | |
| 180 | + | out += 64 + c2; | |
| 181 | + | } else if(c >= 0xc4 && c <= 0xdf) | |
| 182 | + | i += 1; | |
| 183 | + | else if(c >= 0xe0 && c <= 0xed) | |
| 184 | + | i += 2; | |
| 185 | + | else if(c >= 0xf0 && c <= 0xf4) | |
| 186 | + | i += 3; | |
| 187 | + | } | |
| 188 | + | return out; | |
| 189 | + | } | |
| 190 | + | ||
| 191 | + | std::string latin1_to_utf8(const std::string& src) | |
| 192 | + | { | |
| 193 | + | std::string out; | |
| 194 | + | for(uchar c : src) { | |
| 195 | + | if((c >= 32 && c < 128) || c == 0x0d || c == 0x0a || c == 0x09) | |
| 196 | + | out += c; | |
| 197 | + | else { | |
| 198 | + | out += 0xc2 + (c > 0xbf); | |
| 199 | + | out += 0x80 + (c & 0x3f); | |
| 200 | + | } | |
| 201 | + | } | |
| 202 | + | return out; | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | #ifdef WIN32 | |
| 206 | + | #include <winsock2.h> | |
| 207 | + | #include <windows.h> | |
| 208 | + | std::wstring utf8_to_utf16(const std::string& src) | |
| 209 | + | { | |
| 210 | + | std::wstring res; | |
| 211 | + | wchar_t out[65536]; | |
| 212 | + | if(MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, out, 65536)) | |
| 213 | + | res = out; | |
| 214 | + | return res; | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | std::string utf16_to_utf8(const std::wstring& src) | |
| 218 | + | { | |
| 219 | + | std::string res; | |
| 220 | + | char out[65536]; | |
| 221 | + | if(WideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, out, 65536, NULL, NULL)) | |
| 222 | + | res = out; | |
| 223 | + | return res; | |
| 224 | + | } | |
| 225 | + | ||
| 226 | + | std::wstring latin1_to_utf16(const std::string& src) | |
| 227 | + | { | |
| 228 | + | return utf8_to_utf16(latin1_to_utf8(src)); | |
| 229 | + | } | |
| 230 | + | ||
| 231 | + | std::string utf16_to_latin1(const std::wstring& src) | |
| 232 | + | { | |
| 233 | + | return utf8_to_latin1(utf16_to_utf8(src)); | |
| 234 | + | } | |
| 235 | + | #endif | |
| 236 | + | ||
| 237 | + | void tolower(std::string& str) | |
| 238 | + | { | |
| 239 | + | std::transform(str.begin(), str.end(), str.begin(), lochar); | |
| 240 | + | } | |
| 241 | + | ||
| 242 | + | void toupper(std::string& str) | |
| 243 | + | { | |
| 244 | + | std::transform(str.begin(), str.end(), str.begin(), upchar); | |
| 245 | + | } | |
| 246 | + | ||
| 247 | + | void trim(std::string& str) | |
| 248 | + | { | |
| 249 | + | boost::trim(str); | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | char upchar(char c) | |
| 253 | + | { | |
| 254 | + | if((c >= 97 && c <= 122) || (uchar)c >= 224) | |
| 255 | + | c -= 32; | |
| 256 | + | return c; | |
| 257 | + | } | |
| 258 | + | ||
| 259 | + | char lochar(char c) | |
| 260 | + | { | |
| 261 | + | if((c >= 65 && c <= 90) || ((uchar)c >= 192 && (uchar)c <= 223)) | |
| 262 | + | c += 32; | |
| 263 | + | return c; | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | void ucwords(std::string& str) | |
| 267 | + | { | |
| 268 | + | uint32 strLen = str.length(); | |
| 269 | + | if(strLen == 0) | |
| 270 | + | return; | |
| 271 | + | ||
| 272 | + | str[0] = upchar(str[0]); | |
| 273 | + | for(uint32 i = 1; i < strLen; ++i) { | |
| 274 | + | if(str[i - 1] == ' ') | |
| 275 | + | str[i] = upchar(str[i]); | |
| 276 | + | } | |
| 277 | + | } | |
| 278 | + | ||
| 279 | + | bool ends_with(const std::string& str, const std::string& test) | |
| 280 | + | { | |
| 281 | + | return boost::ends_with(str, test); | |
| 282 | + | } | |
| 283 | + | ||
| 284 | + | bool starts_with(const std::string& str, const std::string& test) | |
| 285 | + | { | |
| 286 | + | return boost::starts_with(str, test); | |
| 287 | + | } | |
| 288 | + | ||
| 289 | + | void replace_all(std::string& str, const std::string& search, const std::string& replacement) | |
| 290 | + | { | |
| 291 | + | return boost::replace_all(str, search, replacement); | |
| 292 | + | } | |
| 293 | + | ||
| 294 | + | std::vector<std::string> split(const std::string& str, const std::string& separators) | |
| 295 | + | { | |
| 296 | + | std::vector<std::string> splitted; | |
| 297 | + | boost::split(splitted, str, boost::is_any_of(std::string(separators))); | |
| 298 | + | return splitted; | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | } |
| @@ -0,0 +1,79 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STDEXT_STRING_H | |
| 24 | + | #define STDEXT_STRING_H | |
| 25 | + | ||
| 26 | + | #include <string> | |
| 27 | + | #include <cstring> | |
| 28 | + | #include <vector> | |
| 29 | + | ||
| 30 | + | #include "types.h" | |
| 31 | + | #include "cast.h" | |
| 32 | + | ||
| 33 | + | namespace stdext { | |
| 34 | + | ||
| 35 | + | template<typename T> std::string to_string(const T& t) { return unsafe_cast<std::string, T>(t); } | |
| 36 | + | template<typename T> T from_string(const std::string& str, T def = T()) { return unsafe_cast<T, std::string>(str, def); } | |
| 37 | + | ||
| 38 | + | /// Resolve a file path by combining sourcePath with filePath | |
| 39 | + | std::string resolve_path(const std::string& filePath, std::string sourcePath); | |
| 40 | + | /// Get current date and time in a std::string | |
| 41 | + | std::string date_time_string(); | |
| 42 | + | std::string timestamp_to_date(time_t tnow); | |
| 43 | + | ||
| 44 | + | std::string dec_to_hex(uint32_t num); | |
| 45 | + | std::string dec_to_hex(uint64_t num); | |
| 46 | + | uint64_t hex_to_dec(const std::string& str); | |
| 47 | + | void tolower(std::string& str); | |
| 48 | + | void toupper(std::string& str); | |
| 49 | + | void trim(std::string& str); | |
| 50 | + | void ucwords(std::string& str); | |
| 51 | + | char upchar(char c); | |
| 52 | + | char lochar(char c); | |
| 53 | + | bool ends_with(const std::string& str, const std::string& test); | |
| 54 | + | bool starts_with(const std::string& str, const std::string& test); | |
| 55 | + | void replace_all(std::string& str, const std::string& search, const std::string& replacement); | |
| 56 | + | ||
| 57 | + | bool is_valid_utf8(const std::string& src); | |
| 58 | + | std::string utf8_to_latin1(const std::string& src); | |
| 59 | + | std::string latin1_to_utf8(const std::string& src); | |
| 60 | + | ||
| 61 | + | #ifdef WIN32 | |
| 62 | + | std::wstring utf8_to_utf16(const std::string& src); | |
| 63 | + | std::string utf16_to_utf8(const std::wstring& src); | |
| 64 | + | std::string utf16_to_latin1(const std::wstring& src); | |
| 65 | + | std::wstring latin1_to_utf16(const std::string& src); | |
| 66 | + | #endif | |
| 67 | + | ||
| 68 | + | std::vector<std::string> split(const std::string& str, const std::string& separators = " "); | |
| 69 | + | template<typename T> std::vector<T> split(const std::string& str, const std::string& separators = " ") { | |
| 70 | + | std::vector<std::string> splitted = split(str, separators); | |
| 71 | + | std::vector<T> results(splitted.size()); | |
| 72 | + | for(uint i=0;i<splitted.size();++i) | |
| 73 | + | results[i] = safe_cast<T>(splitted[i]); | |
| 74 | + | return results; | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | } | |
| 78 | + | ||
| 79 | + | #endif |
| @@ -0,0 +1,31 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef THREAD_H | |
| 24 | + | #define THREAD_H | |
| 25 | + | ||
| 26 | + | #include <thread> | |
| 27 | + | #include <condition_variable> | |
| 28 | + | #include <mutex> | |
| 29 | + | ||
| 30 | + | #endif | |
| 31 | + |
| @@ -0,0 +1,55 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "time.h" | |
| 24 | + | #include <chrono> | |
| 25 | + | #include <ctime> | |
| 26 | + | #include <thread> | |
| 27 | + | ||
| 28 | + | namespace stdext { | |
| 29 | + | ||
| 30 | + | const static auto startup_time = std::chrono::high_resolution_clock::now(); | |
| 31 | + | ||
| 32 | + | ticks_t time() { | |
| 33 | + | return std::time(NULL); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | ticks_t millis() | |
| 37 | + | { | |
| 38 | + | return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startup_time).count(); | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | ticks_t micros() { | |
| 42 | + | return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - startup_time).count(); | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | void millisleep(size_t ms) | |
| 46 | + | { | |
| 47 | + | std::this_thread::sleep_for(std::chrono::milliseconds(ms)); | |
| 48 | + | }; | |
| 49 | + | ||
| 50 | + | void microsleep(size_t us) | |
| 51 | + | { | |
| 52 | + | std::this_thread::sleep_for(std::chrono::microseconds(us)); | |
| 53 | + | }; | |
| 54 | + | ||
| 55 | + | } |
| @@ -0,0 +1,50 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STDEXT_TIME_H | |
| 24 | + | #define STDEXT_TIME_H | |
| 25 | + | ||
| 26 | + | #include "types.h" | |
| 27 | + | ||
| 28 | + | namespace stdext { | |
| 29 | + | ||
| 30 | + | ticks_t time(); | |
| 31 | + | ticks_t millis(); | |
| 32 | + | ticks_t micros(); | |
| 33 | + | void millisleep(size_t ms); | |
| 34 | + | void microsleep(size_t us); | |
| 35 | + | ||
| 36 | + | struct timer { | |
| 37 | + | public: | |
| 38 | + | timer() { restart(); } | |
| 39 | + | float elapsed_seconds() { return (float)((stdext::micros() - m_start)/1000000.0); } | |
| 40 | + | ticks_t elapsed_millis() { return (stdext::micros() - m_start)/1000; } | |
| 41 | + | ticks_t elapsed_micros() { return stdext::micros() - m_start; } | |
| 42 | + | void restart(int shift = 0) { m_start = stdext::micros() - shift; } | |
| 43 | + | private: | |
| 44 | + | ticks_t m_start; | |
| 45 | + | }; | |
| 46 | + | ||
| 47 | + | } | |
| 48 | + | ||
| 49 | + | #endif | |
| 50 | + |
| @@ -0,0 +1,37 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STDEXT_TRAITS_H | |
| 24 | + | #define STDEXT_TRAITS_H | |
| 25 | + | ||
| 26 | + | #include <type_traits> | |
| 27 | + | ||
| 28 | + | namespace stdext { | |
| 29 | + | ||
| 30 | + | template<class T> struct replace_extent { typedef T type; }; | |
| 31 | + | template<class T> struct replace_extent<T[]> { typedef const T* type; }; | |
| 32 | + | template<class T, unsigned long N> struct replace_extent<T[N]> { typedef const T* type;}; | |
| 33 | + | template<typename T> struct remove_const_ref { typedef typename std::remove_const<typename std::remove_reference<T>::type>::type type; }; | |
| 34 | + | ||
| 35 | + | }; | |
| 36 | + | ||
| 37 | + | #endif |
| @@ -0,0 +1,49 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STDEXT_TYPES_H | |
| 24 | + | #define STDEXT_TYPES_H | |
| 25 | + | ||
| 26 | + | #include <cstdint> | |
| 27 | + | #include <cstddef> | |
| 28 | + | ||
| 29 | + | typedef unsigned char uchar; | |
| 30 | + | typedef unsigned short ushort; | |
| 31 | + | typedef unsigned int uint; | |
| 32 | + | typedef unsigned long ulong; | |
| 33 | + | ||
| 34 | + | typedef uint64_t uint64; | |
| 35 | + | typedef uint32_t uint32; | |
| 36 | + | typedef uint16_t uint16; | |
| 37 | + | typedef uint8_t uint8; | |
| 38 | + | typedef int64_t int64; | |
| 39 | + | typedef int32_t int32; | |
| 40 | + | typedef int16_t int16; | |
| 41 | + | typedef int8_t int8; | |
| 42 | + | ||
| 43 | + | typedef int64 ticks_t; | |
| 44 | + | typedef uint_fast32_t refcount_t; | |
| 45 | + | ||
| 46 | + | using std::size_t; | |
| 47 | + | using std::ptrdiff_t; | |
| 48 | + | ||
| 49 | + | #endif |
| @@ -0,0 +1,24 @@ | |||
| 1 | + | #include <boost/algorithm/string.hpp> | |
| 2 | + | #include <regex> | |
| 3 | + | ||
| 4 | + | #include "uri.h" | |
| 5 | + | ||
| 6 | + | ParsedURI parseURI(const std::string& url) | |
| 7 | + | { | |
| 8 | + | ParsedURI result; | |
| 9 | + | auto value_or = [](const std::string& value, std::string&& deflt) -> std::string { | |
| 10 | + | return (value.empty() ? deflt : value); | |
| 11 | + | }; | |
| 12 | + | // Note: only "http", "https", "ws", and "wss" protocols are supported | |
| 13 | + | static const std::regex PARSE_URL{ R"((([httpsw]{2,5})://)?([^/ :]+)(:(\d+))?(/(.+)?))", | |
| 14 | + | std::regex_constants::ECMAScript | std::regex_constants::icase }; | |
| 15 | + | std::smatch match; | |
| 16 | + | if (std::regex_match(url, match, PARSE_URL) && match.size() == 8) { | |
| 17 | + | result.protocol = value_or(boost::algorithm::to_lower_copy(std::string(match[2])), "http"); | |
| 18 | + | result.domain = match[3]; | |
| 19 | + | const bool is_sequre_protocol = (result.protocol == "https" || result.protocol == "wss"); | |
| 20 | + | result.port = value_or(match[5], (is_sequre_protocol) ? "443" : "80"); | |
| 21 | + | result.query = value_or(match[6], "/"); | |
| 22 | + | } | |
| 23 | + | return result; | |
| 24 | + | } |
| @@ -0,0 +1,10 @@ | |||
| 1 | + | #include <string> | |
| 2 | + | ||
| 3 | + | struct ParsedURI { | |
| 4 | + | std::string protocol; | |
| 5 | + | std::string domain; // only domain must be present | |
| 6 | + | std::string port; | |
| 7 | + | std::string query; // everything after '?', possibly nothing | |
| 8 | + | }; | |
| 9 | + | ||
| 10 | + | ParsedURI parseURI(const std::string& url); |
| @@ -0,0 +1,54 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef FRAMEWORK_UI_DECLARATIONS_H | |
| 24 | + | #define FRAMEWORK_UI_DECLARATIONS_H | |
| 25 | + | ||
| 26 | + | #include <framework/global.h> | |
| 27 | + | ||
| 28 | + | class UIManager; | |
| 29 | + | class UIWidget; | |
| 30 | + | class UITextEdit; | |
| 31 | + | class UILayout; | |
| 32 | + | class UIBoxLayout; | |
| 33 | + | class UIHorizontalLayout; | |
| 34 | + | class UIVerticalLayout; | |
| 35 | + | class UIGridLayout; | |
| 36 | + | class UIAnchor; | |
| 37 | + | class UIAnchorGroup; | |
| 38 | + | class UIAnchorLayout; | |
| 39 | + | ||
| 40 | + | typedef stdext::shared_object_ptr<UIWidget> UIWidgetPtr; | |
| 41 | + | typedef stdext::shared_object_ptr<UITextEdit> UITextEditPtr; | |
| 42 | + | typedef stdext::shared_object_ptr<UILayout> UILayoutPtr; | |
| 43 | + | typedef stdext::shared_object_ptr<UIBoxLayout> UIBoxLayoutPtr; | |
| 44 | + | typedef stdext::shared_object_ptr<UIHorizontalLayout> UIHorizontalLayoutPtr; | |
| 45 | + | typedef stdext::shared_object_ptr<UIVerticalLayout> UIVerticalLayoutPtr; | |
| 46 | + | typedef stdext::shared_object_ptr<UIGridLayout> UIGridLayoutPtr; | |
| 47 | + | typedef stdext::shared_object_ptr<UIAnchor> UIAnchorPtr; | |
| 48 | + | typedef stdext::shared_object_ptr<UIAnchorGroup> UIAnchorGroupPtr; | |
| 49 | + | typedef stdext::shared_object_ptr<UIAnchorLayout> UIAnchorLayoutPtr; | |
| 50 | + | ||
| 51 | + | typedef std::deque<UIWidgetPtr> UIWidgetList; | |
| 52 | + | typedef std::vector<UIAnchorPtr> UIAnchorList; | |
| 53 | + | ||
| 54 | + | #endif |
| @@ -0,0 +1,35 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef UI_H | |
| 24 | + | #define UI_H | |
| 25 | + | ||
| 26 | + | #include "uimanager.h" | |
| 27 | + | #include "uiwidget.h" | |
| 28 | + | #include "uitextedit.h" | |
| 29 | + | #include "uilayout.h" | |
| 30 | + | #include "uihorizontallayout.h" | |
| 31 | + | #include "uiverticallayout.h" | |
| 32 | + | #include "uigridlayout.h" | |
| 33 | + | #include "uianchorlayout.h" | |
| 34 | + | ||
| 35 | + | #endif |
| @@ -0,0 +1,275 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "uianchorlayout.h" | |
| 24 | + | #include "uiwidget.h" | |
| 25 | + | ||
| 26 | + | UIWidgetPtr UIAnchor::getHookedWidget(const UIWidgetPtr& widget, const UIWidgetPtr& parentWidget) | |
| 27 | + | { | |
| 28 | + | // determine hooked widget | |
| 29 | + | UIWidgetPtr hookedWidget; | |
| 30 | + | if(parentWidget) { | |
| 31 | + | if(m_hookedWidgetId == "parent") | |
| 32 | + | hookedWidget = parentWidget; | |
| 33 | + | else if(m_hookedWidgetId == "next") | |
| 34 | + | hookedWidget = parentWidget->getChildAfter(widget); | |
| 35 | + | else if(m_hookedWidgetId == "prev") | |
| 36 | + | hookedWidget = parentWidget->getChildBefore(widget); | |
| 37 | + | else | |
| 38 | + | hookedWidget = parentWidget->getChildById(m_hookedWidgetId); | |
| 39 | + | } | |
| 40 | + | return hookedWidget; | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | int UIAnchor::getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget) | |
| 44 | + | { | |
| 45 | + | // determine hooked widget edge point | |
| 46 | + | Rect hookedWidgetRect = hookedWidget->getRect(); | |
| 47 | + | if(hookedWidget == parentWidget) | |
| 48 | + | hookedWidgetRect = parentWidget->getPaddingRect(); | |
| 49 | + | ||
| 50 | + | int point = 0; | |
| 51 | + | switch(m_hookedEdge) { | |
| 52 | + | case Fw::AnchorLeft: | |
| 53 | + | point = hookedWidgetRect.left(); | |
| 54 | + | break; | |
| 55 | + | case Fw::AnchorRight: | |
| 56 | + | point = hookedWidgetRect.right(); | |
| 57 | + | break; | |
| 58 | + | case Fw::AnchorTop: | |
| 59 | + | point = hookedWidgetRect.top(); | |
| 60 | + | break; | |
| 61 | + | case Fw::AnchorBottom: | |
| 62 | + | point = hookedWidgetRect.bottom(); | |
| 63 | + | break; | |
| 64 | + | case Fw::AnchorHorizontalCenter: | |
| 65 | + | point = hookedWidgetRect.horizontalCenter(); | |
| 66 | + | break; | |
| 67 | + | case Fw::AnchorVerticalCenter: | |
| 68 | + | point = hookedWidgetRect.verticalCenter(); | |
| 69 | + | break; | |
| 70 | + | default: | |
| 71 | + | // must never happens | |
| 72 | + | VALIDATE(false); | |
| 73 | + | break; | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | if(hookedWidget == parentWidget) { | |
| 77 | + | switch(m_hookedEdge) { | |
| 78 | + | case Fw::AnchorLeft: | |
| 79 | + | case Fw::AnchorRight: | |
| 80 | + | case Fw::AnchorHorizontalCenter: | |
| 81 | + | point -= parentWidget->getVirtualOffset().x; | |
| 82 | + | break; | |
| 83 | + | case Fw::AnchorBottom: | |
| 84 | + | case Fw::AnchorTop: | |
| 85 | + | case Fw::AnchorVerticalCenter: | |
| 86 | + | point -= parentWidget->getVirtualOffset().y; | |
| 87 | + | break; | |
| 88 | + | default: | |
| 89 | + | break; | |
| 90 | + | } | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | return point; | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | void UIAnchorGroup::addAnchor(const UIAnchorPtr& anchor) | |
| 97 | + | { | |
| 98 | + | // duplicated anchors must be replaced | |
| 99 | + | for(UIAnchorPtr& other : m_anchors) { | |
| 100 | + | if(other->getAnchoredEdge() == anchor->getAnchoredEdge()) { | |
| 101 | + | other = anchor; | |
| 102 | + | return; | |
| 103 | + | } | |
| 104 | + | } | |
| 105 | + | m_anchors.push_back(anchor); | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | void UIAnchorLayout::addAnchor(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge, | |
| 109 | + | const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge) | |
| 110 | + | { | |
| 111 | + | if(!anchoredWidget) | |
| 112 | + | return; | |
| 113 | + | ||
| 114 | + | VALIDATE(anchoredWidget != getParentWidget()); | |
| 115 | + | ||
| 116 | + | UIAnchorPtr anchor(new UIAnchor(anchoredEdge, hookedWidgetId, hookedEdge)); | |
| 117 | + | UIAnchorGroupPtr& anchorGroup = m_anchorsGroups[anchoredWidget]; | |
| 118 | + | if(!anchorGroup) | |
| 119 | + | anchorGroup = UIAnchorGroupPtr(new UIAnchorGroup); | |
| 120 | + | ||
| 121 | + | anchorGroup->addAnchor(anchor); | |
| 122 | + | ||
| 123 | + | // layout must be updated because a new anchor got in | |
| 124 | + | update(); | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | void UIAnchorLayout::removeAnchors(const UIWidgetPtr& anchoredWidget) | |
| 128 | + | { | |
| 129 | + | m_anchorsGroups.erase(anchoredWidget); | |
| 130 | + | update(); | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | bool UIAnchorLayout::hasAnchors(const UIWidgetPtr& anchoredWidget) | |
| 134 | + | { | |
| 135 | + | return m_anchorsGroups.find(anchoredWidget) != m_anchorsGroups.end(); | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | void UIAnchorLayout::centerIn(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId) | |
| 139 | + | { | |
| 140 | + | addAnchor(anchoredWidget, Fw::AnchorHorizontalCenter, hookedWidgetId, Fw::AnchorHorizontalCenter); | |
| 141 | + | addAnchor(anchoredWidget, Fw::AnchorVerticalCenter, hookedWidgetId, Fw::AnchorVerticalCenter); | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | void UIAnchorLayout::fill(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId) | |
| 145 | + | { | |
| 146 | + | addAnchor(anchoredWidget, Fw::AnchorLeft, hookedWidgetId, Fw::AnchorLeft); | |
| 147 | + | addAnchor(anchoredWidget, Fw::AnchorRight, hookedWidgetId, Fw::AnchorRight); | |
| 148 | + | addAnchor(anchoredWidget, Fw::AnchorTop, hookedWidgetId, Fw::AnchorTop); | |
| 149 | + | addAnchor(anchoredWidget, Fw::AnchorBottom, hookedWidgetId, Fw::AnchorBottom); | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | void UIAnchorLayout::addWidget(const UIWidgetPtr& widget) | |
| 153 | + | { | |
| 154 | + | update(); | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | void UIAnchorLayout::removeWidget(const UIWidgetPtr& widget) | |
| 158 | + | { | |
| 159 | + | removeAnchors(widget); | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, const UIAnchorGroupPtr& anchorGroup, UIWidgetPtr first) | |
| 163 | + | { | |
| 164 | + | UIWidgetPtr parentWidget = getParentWidget(); | |
| 165 | + | if(!parentWidget) | |
| 166 | + | return false; | |
| 167 | + | ||
| 168 | + | if(first == widget) { | |
| 169 | + | g_logger.error(stdext::format("child '%s' of parent widget '%s' is recursively anchored to itself, please fix this", widget->getId(), parentWidget->getId())); | |
| 170 | + | return false; | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | if(!first) | |
| 174 | + | first = widget; | |
| 175 | + | ||
| 176 | + | Rect newRect = widget->getRect(); | |
| 177 | + | bool verticalMoved = false; | |
| 178 | + | bool horizontalMoved = false; | |
| 179 | + | ||
| 180 | + | // calculates new rect based on anchors | |
| 181 | + | for(const UIAnchorPtr& anchor : anchorGroup->getAnchors()) { | |
| 182 | + | // skip invalid anchors | |
| 183 | + | if(anchor->getHookedEdge() == Fw::AnchorNone) | |
| 184 | + | continue; | |
| 185 | + | ||
| 186 | + | // determine hooked widget | |
| 187 | + | UIWidgetPtr hookedWidget = anchor->getHookedWidget(widget, parentWidget); | |
| 188 | + | ||
| 189 | + | // skip invalid anchors | |
| 190 | + | if(!hookedWidget) | |
| 191 | + | continue; | |
| 192 | + | ||
| 193 | + | if(hookedWidget != getParentWidget()) { | |
| 194 | + | // update this hooked widget anchors | |
| 195 | + | auto it = m_anchorsGroups.find(hookedWidget); | |
| 196 | + | if(it != m_anchorsGroups.end()) { | |
| 197 | + | const UIAnchorGroupPtr& hookedAnchorGroup = it->second; | |
| 198 | + | if(!hookedAnchorGroup->isUpdated()) | |
| 199 | + | updateWidget(hookedWidget, hookedAnchorGroup, first); | |
| 200 | + | } | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | int point = anchor->getHookedPoint(hookedWidget, parentWidget); | |
| 204 | + | ||
| 205 | + | switch(anchor->getAnchoredEdge()) { | |
| 206 | + | case Fw::AnchorHorizontalCenter: | |
| 207 | + | newRect.moveHorizontalCenter(point + widget->getMarginLeft() - widget->getMarginRight()); | |
| 208 | + | horizontalMoved = true; | |
| 209 | + | break; | |
| 210 | + | case Fw::AnchorLeft: | |
| 211 | + | if(!horizontalMoved) { | |
| 212 | + | newRect.moveLeft(point + widget->getMarginLeft()); | |
| 213 | + | horizontalMoved = true; | |
| 214 | + | } else | |
| 215 | + | newRect.setLeft(point + widget->getMarginLeft()); | |
| 216 | + | break; | |
| 217 | + | case Fw::AnchorRight: | |
| 218 | + | if(!horizontalMoved) { | |
| 219 | + | newRect.moveRight(point - widget->getMarginRight()); | |
| 220 | + | horizontalMoved = true; | |
| 221 | + | } else | |
| 222 | + | newRect.setRight(point - widget->getMarginRight()); | |
| 223 | + | break; | |
| 224 | + | case Fw::AnchorVerticalCenter: | |
| 225 | + | newRect.moveVerticalCenter(point + widget->getMarginTop() - widget->getMarginBottom()); | |
| 226 | + | verticalMoved = true; | |
| 227 | + | break; | |
| 228 | + | case Fw::AnchorTop: | |
| 229 | + | if(!verticalMoved) { | |
| 230 | + | newRect.moveTop(point + widget->getMarginTop()); | |
| 231 | + | verticalMoved = true; | |
| 232 | + | } else | |
| 233 | + | newRect.setTop(point + widget->getMarginTop()); | |
| 234 | + | break; | |
| 235 | + | case Fw::AnchorBottom: | |
| 236 | + | if(!verticalMoved) { | |
| 237 | + | newRect.moveBottom(point - widget->getMarginBottom()); | |
| 238 | + | verticalMoved = true; | |
| 239 | + | } else | |
| 240 | + | newRect.setBottom(point - widget->getMarginBottom()); | |
| 241 | + | break; | |
| 242 | + | default: | |
| 243 | + | break; | |
| 244 | + | } | |
| 245 | + | } | |
| 246 | + | ||
| 247 | + | bool changed = false; | |
| 248 | + | if(widget->setRect(newRect)) | |
| 249 | + | changed = true; | |
| 250 | + | anchorGroup->setUpdated(true); | |
| 251 | + | return changed; | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | bool UIAnchorLayout::internalUpdate() | |
| 255 | + | { | |
| 256 | + | bool changed = false; | |
| 257 | + | ||
| 258 | + | // reset all anchors groups update state | |
| 259 | + | for(auto& it : m_anchorsGroups) { | |
| 260 | + | const UIAnchorGroupPtr& anchorGroup = it.second; | |
| 261 | + | anchorGroup->setUpdated(false); | |
| 262 | + | } | |
| 263 | + | ||
| 264 | + | // update all anchors | |
| 265 | + | for(auto& it : m_anchorsGroups) { | |
| 266 | + | const UIWidgetPtr& widget = it.first; | |
| 267 | + | const UIAnchorGroupPtr& anchorGroup = it.second; | |
| 268 | + | if(!anchorGroup->isUpdated()) { | |
| 269 | + | if(updateWidget(widget, anchorGroup)) | |
| 270 | + | changed = true; | |
| 271 | + | } | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | return changed; | |
| 275 | + | } |
| @@ -0,0 +1,85 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef UIANCHORLAYOUT_H | |
| 24 | + | #define UIANCHORLAYOUT_H | |
| 25 | + | ||
| 26 | + | #include "uilayout.h" | |
| 27 | + | ||
| 28 | + | class UIAnchor : public stdext::shared_object | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | UIAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge) : | |
| 32 | + | m_anchoredEdge(anchoredEdge), m_hookedEdge(hookedEdge), m_hookedWidgetId(hookedWidgetId) { } | |
| 33 | + | ||
| 34 | + | Fw::AnchorEdge getAnchoredEdge() const { return m_anchoredEdge; } | |
| 35 | + | Fw::AnchorEdge getHookedEdge() const { return m_hookedEdge; } | |
| 36 | + | ||
| 37 | + | virtual UIWidgetPtr getHookedWidget(const UIWidgetPtr& widget, const UIWidgetPtr& parentWidget); | |
| 38 | + | virtual int getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget); | |
| 39 | + | ||
| 40 | + | protected: | |
| 41 | + | Fw::AnchorEdge m_anchoredEdge; | |
| 42 | + | Fw::AnchorEdge m_hookedEdge; | |
| 43 | + | std::string m_hookedWidgetId; | |
| 44 | + | }; | |
| 45 | + | ||
| 46 | + | class UIAnchorGroup : public stdext::shared_object | |
| 47 | + | { | |
| 48 | + | public: | |
| 49 | + | UIAnchorGroup() : m_updated(true) { } | |
| 50 | + | ||
| 51 | + | void addAnchor(const UIAnchorPtr& anchor); | |
| 52 | + | const UIAnchorList& getAnchors() { return m_anchors; } | |
| 53 | + | bool isUpdated() { return m_updated; } | |
| 54 | + | void setUpdated(bool updated) { m_updated = updated; } | |
| 55 | + | ||
| 56 | + | private: | |
| 57 | + | UIAnchorList m_anchors; | |
| 58 | + | bool m_updated; | |
| 59 | + | }; | |
| 60 | + | ||
| 61 | + | // @bindclass | |
| 62 | + | class UIAnchorLayout : public UILayout | |
| 63 | + | { | |
| 64 | + | public: | |
| 65 | + | UIAnchorLayout(UIWidgetPtr parentWidget) : UILayout(parentWidget) { } | |
| 66 | + | ||
| 67 | + | void addAnchor(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge, | |
| 68 | + | const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge); | |
| 69 | + | void removeAnchors(const UIWidgetPtr& anchoredWidget); | |
| 70 | + | bool hasAnchors(const UIWidgetPtr& anchoredWidget); | |
| 71 | + | void centerIn(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId); | |
| 72 | + | void fill(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId); | |
| 73 | + | ||
| 74 | + | void addWidget(const UIWidgetPtr& widget); | |
| 75 | + | void removeWidget(const UIWidgetPtr& widget); | |
| 76 | + | ||
| 77 | + | bool isUIAnchorLayout() { return true; } | |
| 78 | + | ||
| 79 | + | protected: | |
| 80 | + | virtual bool internalUpdate(); | |
| 81 | + | virtual bool updateWidget(const UIWidgetPtr& widget, const UIAnchorGroupPtr& anchorGroup, UIWidgetPtr first = nullptr); | |
| 82 | + | std::unordered_map<UIWidgetPtr, UIAnchorGroupPtr> m_anchorsGroups; | |
| 83 | + | }; | |
| 84 | + | ||
| 85 | + | #endif |
| @@ -0,0 +1,42 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "uiboxlayout.h" | |
| 24 | + | #include "uiwidget.h" | |
| 25 | + | ||
| 26 | + | UIBoxLayout::UIBoxLayout(UIWidgetPtr parentWidget) : UILayout(parentWidget) | |
| 27 | + | { | |
| 28 | + | m_spacing = 0; | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | void UIBoxLayout::applyStyle(const OTMLNodePtr& styleNode) | |
| 32 | + | { | |
| 33 | + | UILayout::applyStyle(styleNode); | |
| 34 | + | ||
| 35 | + | for(const OTMLNodePtr& node : styleNode->children()) { | |
| 36 | + | if(node->tag() == "spacing") | |
| 37 | + | setSpacing(node->value<int>()); | |
| 38 | + | else if(node->tag() == "fit-children") | |
| 39 | + | setFitChildren(node->value<bool>()); | |
| 40 | + | } | |
| 41 | + | } | |
| 42 | + |
| @@ -0,0 +1,48 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef UIBOXLAYOUT_H | |
| 24 | + | #define UIBOXLAYOUT_H | |
| 25 | + | ||
| 26 | + | #include "uilayout.h" | |
| 27 | + | ||
| 28 | + | // @bindclass | |
| 29 | + | class UIBoxLayout : public UILayout | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | UIBoxLayout(UIWidgetPtr parentWidget); | |
| 33 | + | ||
| 34 | + | void applyStyle(const OTMLNodePtr& styleNode); | |
| 35 | + | void addWidget(const UIWidgetPtr& widget) { update(); } | |
| 36 | + | void removeWidget(const UIWidgetPtr& widget) { update(); } | |
| 37 | + | ||
| 38 | + | void setSpacing(int spacing) { m_spacing = spacing; update(); } | |
| 39 | + | void setFitChildren(bool fitParent) { m_fitChildren = fitParent; update(); } | |
| 40 | + | ||
| 41 | + | bool isUIBoxLayout() { return true; } | |
| 42 | + | ||
| 43 | + | protected: | |
| 44 | + | stdext::boolean<false> m_fitChildren; | |
| 45 | + | int m_spacing; | |
| 46 | + | }; | |
| 47 | + | ||
| 48 | + | #endif |
| @@ -0,0 +1,132 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "uigridlayout.h" | |
| 24 | + | #include "uiwidget.h" | |
| 25 | + | ||
| 26 | + | #include <framework/core/eventdispatcher.h> | |
| 27 | + | ||
| 28 | + | UIGridLayout::UIGridLayout(UIWidgetPtr parentWidget): UILayout(parentWidget) | |
| 29 | + | { | |
| 30 | + | m_cellSize = Size(16,16); | |
| 31 | + | m_cellSpacing = 0; | |
| 32 | + | m_numColumns = 1; | |
| 33 | + | m_numLines = 0; | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | void UIGridLayout::applyStyle(const OTMLNodePtr& styleNode) | |
| 37 | + | { | |
| 38 | + | UILayout::applyStyle(styleNode); | |
| 39 | + | ||
| 40 | + | for(const OTMLNodePtr& node : styleNode->children()) { | |
| 41 | + | if(node->tag() == "cell-size") | |
| 42 | + | setCellSize(node->value<Size>()); | |
| 43 | + | else if(node->tag() == "cell-width") | |
| 44 | + | setCellWidth(node->value<int>()); | |
| 45 | + | else if(node->tag() == "cell-height") | |
| 46 | + | setCellHeight(node->value<int>()); | |
| 47 | + | else if(node->tag() == "cell-spacing") | |
| 48 | + | setCellSpacing(node->value<int>()); | |
| 49 | + | else if(node->tag() == "num-columns") | |
| 50 | + | setNumColumns(node->value<int>()); | |
| 51 | + | else if(node->tag() == "num-lines") | |
| 52 | + | setNumLines(node->value<int>()); | |
| 53 | + | else if(node->tag() == "fit-children") | |
| 54 | + | setFitChildren(node->value<bool>()); | |
| 55 | + | else if(node->tag() == "auto-spacing") | |
| 56 | + | setAutoSpacing(node->value<bool>()); | |
| 57 | + | else if(node->tag() == "flow") | |
| 58 | + | setFlow(node->value<bool>()); | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | void UIGridLayout::removeWidget(const UIWidgetPtr& widget) | |
| 63 | + | { | |
| 64 | + | update(); | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | void UIGridLayout::addWidget(const UIWidgetPtr& widget) | |
| 68 | + | { | |
| 69 | + | update(); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | bool UIGridLayout::internalUpdate() | |
| 73 | + | { | |
| 74 | + | bool changed = false; | |
| 75 | + | UIWidgetPtr parentWidget = getParentWidget(); | |
| 76 | + | if(!parentWidget) | |
| 77 | + | return false; | |
| 78 | + | ||
| 79 | + | UIWidgetList widgets = parentWidget->getChildren(); | |
| 80 | + | ||
| 81 | + | Rect clippingRect = parentWidget->getPaddingRect(); | |
| 82 | + | Point topLeft = clippingRect.topLeft(); | |
| 83 | + | ||
| 84 | + | int numColumns = m_numColumns; | |
| 85 | + | if(m_flow && m_cellSize.width() > 0) { | |
| 86 | + | numColumns = (clippingRect.width() + m_cellSpacing) / (m_cellSize.width() + m_cellSpacing); | |
| 87 | + | if(numColumns > 0) { | |
| 88 | + | m_numColumns = numColumns; | |
| 89 | + | m_numLines = std::ceil(widgets.size() / (float)numColumns); | |
| 90 | + | } | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | if(numColumns <= 0) | |
| 94 | + | numColumns = 1; | |
| 95 | + | ||
| 96 | + | int cellSpacing = m_cellSpacing; | |
| 97 | + | if(m_autoSpacing && numColumns > 1) | |
| 98 | + | cellSpacing = (clippingRect.width() - numColumns * m_cellSize.width()) / (numColumns - 1); | |
| 99 | + | ||
| 100 | + | int index = 0; | |
| 101 | + | int preferredHeight = 0; | |
| 102 | + | for(const UIWidgetPtr& widget : widgets) { | |
| 103 | + | if(!widget->isExplicitlyVisible()) | |
| 104 | + | continue; | |
| 105 | + | ||
| 106 | + | int line = index / numColumns; | |
| 107 | + | int column = index % numColumns; | |
| 108 | + | Point virtualPos = Point(column * (m_cellSize.width() + cellSpacing), line * (m_cellSize.height() + cellSpacing)); | |
| 109 | + | preferredHeight = virtualPos.y + m_cellSize.height(); | |
| 110 | + | Point pos = topLeft + virtualPos - parentWidget->getVirtualOffset(); | |
| 111 | + | Rect dest = Rect(pos, m_cellSize); | |
| 112 | + | dest.expand(-widget->getMarginTop(), -widget->getMarginRight(), -widget->getMarginBottom(), -widget->getMarginLeft()); | |
| 113 | + | ||
| 114 | + | if(widget->setRect(dest)) | |
| 115 | + | changed = true; | |
| 116 | + | ||
| 117 | + | index++; | |
| 118 | + | ||
| 119 | + | if(m_numLines > 0 && index >= m_numColumns * m_numLines) | |
| 120 | + | break; | |
| 121 | + | } | |
| 122 | + | preferredHeight += parentWidget->getPaddingTop() + parentWidget->getPaddingBottom(); | |
| 123 | + | ||
| 124 | + | if(m_fitChildren && preferredHeight != parentWidget->getHeight()) { | |
| 125 | + | // must set the preferred height later | |
| 126 | + | g_dispatcher.addEvent([=] { | |
| 127 | + | parentWidget->setHeight(preferredHeight); | |
| 128 | + | }); | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | return changed; | |
| 132 | + | } |
| @@ -0,0 +1,68 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef UIGRIDLAYOUT_H | |
| 24 | + | #define UIGRIDLAYOUT_H | |
| 25 | + | ||
| 26 | + | #include "uilayout.h" | |
| 27 | + | ||
| 28 | + | // @bindclass | |
| 29 | + | class UIGridLayout : public UILayout | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | UIGridLayout(UIWidgetPtr parentWidget); | |
| 33 | + | ||
| 34 | + | void applyStyle(const OTMLNodePtr& styleNode); | |
| 35 | + | void removeWidget(const UIWidgetPtr& widget); | |
| 36 | + | void addWidget(const UIWidgetPtr& widget); | |
| 37 | + | ||
| 38 | + | void setCellSize(const Size& size) { m_cellSize = size; update(); } | |
| 39 | + | void setCellWidth(int width) { m_cellSize.setWidth(width); update(); } | |
| 40 | + | void setCellHeight(int height) { m_cellSize.setHeight(height); update(); } | |
| 41 | + | void setCellSpacing(int spacing) { m_cellSpacing = spacing; update(); } | |
| 42 | + | void setNumColumns(int columns) { m_numColumns = columns; update(); } | |
| 43 | + | void setNumLines(int lines) { m_numLines = lines; update(); } | |
| 44 | + | void setAutoSpacing(bool enable) { m_autoSpacing = enable; update(); } | |
| 45 | + | void setFitChildren(bool enable) { m_fitChildren = enable; update(); } | |
| 46 | + | void setFlow(bool enable) { m_flow = enable; update(); } | |
| 47 | + | ||
| 48 | + | Size getCellSize() { return m_cellSize; } | |
| 49 | + | int getCellSpacing() { return m_cellSpacing; } | |
| 50 | + | int getNumColumns() { return m_numColumns; } | |
| 51 | + | int getNumLines() { return m_numLines; } | |
| 52 | + | ||
| 53 | + | virtual bool isUIGridLayout() { return true; } | |
| 54 | + | ||
| 55 | + | protected: | |
| 56 | + | bool internalUpdate(); | |
| 57 | + | ||
| 58 | + | private: | |
| 59 | + | Size m_cellSize; | |
| 60 | + | int m_cellSpacing; | |
| 61 | + | int m_numColumns; | |
| 62 | + | int m_numLines; | |
| 63 | + | stdext::boolean<false> m_autoSpacing; | |
| 64 | + | stdext::boolean<false> m_fitChildren; | |
| 65 | + | stdext::boolean<false> m_flow; | |
| 66 | + | }; | |
| 67 | + | ||
| 68 | + | #endif |
| @@ -0,0 +1,100 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "uihorizontallayout.h" | |
| 24 | + | #include "uiwidget.h" | |
| 25 | + | #include <framework/core/eventdispatcher.h> | |
| 26 | + | ||
| 27 | + | ||
| 28 | + | void UIHorizontalLayout::applyStyle(const OTMLNodePtr& styleNode) | |
| 29 | + | { | |
| 30 | + | UIBoxLayout::applyStyle(styleNode); | |
| 31 | + | ||
| 32 | + | for(const OTMLNodePtr& node : styleNode->children()) { | |
| 33 | + | if(node->tag() == "align-right") | |
| 34 | + | setAlignRight(node->value<bool>()); | |
| 35 | + | } | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | bool UIHorizontalLayout::internalUpdate() | |
| 39 | + | { | |
| 40 | + | UIWidgetPtr parentWidget = getParentWidget(); | |
| 41 | + | if(!parentWidget) | |
| 42 | + | return false; | |
| 43 | + | UIWidgetList widgets = parentWidget->getChildren(); | |
| 44 | + | ||
| 45 | + | if(m_alignRight) | |
| 46 | + | std::reverse(widgets.begin(), widgets.end()); | |
| 47 | + | ||
| 48 | + | bool changed = false; | |
| 49 | + | Rect paddingRect = parentWidget->getPaddingRect(); | |
| 50 | + | Point pos = (m_alignRight) ? paddingRect.topRight() : paddingRect.topLeft(); | |
| 51 | + | int preferredWidth = 0; | |
| 52 | + | int gap; | |
| 53 | + | ||
| 54 | + | for(const UIWidgetPtr& widget : widgets) { | |
| 55 | + | if(!widget->isExplicitlyVisible()) | |
| 56 | + | continue; | |
| 57 | + | ||
| 58 | + | Size size = widget->getSize(); | |
| 59 | + | ||
| 60 | + | gap = (m_alignRight) ? -(widget->getMarginRight()+widget->getWidth()) : widget->getMarginLeft(); | |
| 61 | + | pos.x += gap; | |
| 62 | + | preferredWidth += gap; | |
| 63 | + | ||
| 64 | + | if(widget->isFixedSize()) { | |
| 65 | + | if(widget->getTextAlign() & Fw::AlignTop) { | |
| 66 | + | pos.y = paddingRect.top() + widget->getMarginTop(); | |
| 67 | + | } else if(widget->getTextAlign() & Fw::AlignBottom) { | |
| 68 | + | pos.y = paddingRect.bottom() - widget->getHeight() - widget->getMarginBottom(); | |
| 69 | + | pos.y = std::max<int>(pos.y, paddingRect.top()); | |
| 70 | + | } else { // center it | |
| 71 | + | pos.y = paddingRect.top() + (paddingRect.height() - (widget->getMarginTop() + widget->getHeight() + widget->getMarginBottom()))/2; | |
| 72 | + | pos.y = std::max<int>(pos.y, paddingRect.top()); | |
| 73 | + | } | |
| 74 | + | } else { | |
| 75 | + | // expand height | |
| 76 | + | size.setHeight(paddingRect.height() - (widget->getMarginTop() + widget->getMarginBottom())); | |
| 77 | + | pos.y = paddingRect.top() + (paddingRect.height() - size.height())/2; | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | if(widget->setRect(Rect(pos - parentWidget->getVirtualOffset(), size))) | |
| 81 | + | changed = true; | |
| 82 | + | ||
| 83 | + | gap = (m_alignRight) ? -widget->getMarginLeft() : (widget->getWidth() + widget->getMarginRight()); | |
| 84 | + | gap += m_spacing; | |
| 85 | + | pos.x += gap; | |
| 86 | + | preferredWidth += gap; | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | preferredWidth -= m_spacing; | |
| 90 | + | preferredWidth += parentWidget->getPaddingLeft() + parentWidget->getPaddingRight(); | |
| 91 | + | ||
| 92 | + | if(m_fitChildren && preferredWidth != parentWidget->getWidth()) { | |
| 93 | + | // must set the preferred width later | |
| 94 | + | g_dispatcher.addEvent([=] { | |
| 95 | + | parentWidget->setWidth(preferredWidth); | |
| 96 | + | }); | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | return changed; | |
| 100 | + | } |
| @@ -0,0 +1,46 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef UIHORIZONTALLAYOUT_H | |
| 24 | + | #define UIHORIZONTALLAYOUT_H | |
| 25 | + | ||
| 26 | + | #include "uiboxlayout.h" | |
| 27 | + | ||
| 28 | + | class UIHorizontalLayout : public UIBoxLayout | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | UIHorizontalLayout(UIWidgetPtr parentWidget) : UIBoxLayout(parentWidget) { } | |
| 32 | + | ||
| 33 | + | void applyStyle(const OTMLNodePtr& styleNode); | |
| 34 | + | ||
| 35 | + | void setAlignRight(bool aliginRight) { m_alignRight = aliginRight; update(); } | |
| 36 | + | ||
| 37 | + | bool isUIHorizontalLayout() { return true; } | |
| 38 | + | ||
| 39 | + | protected: | |
| 40 | + | bool internalUpdate(); | |
| 41 | + | ||
| 42 | + | Fw::AlignmentFlag m_alignChidren; | |
| 43 | + | stdext::boolean<false> m_alignRight; | |
| 44 | + | }; | |
| 45 | + | ||
| 46 | + | #endif |
| @@ -0,0 +1,72 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "uilayout.h" | |
| 24 | + | #include "uiwidget.h" | |
| 25 | + | ||
| 26 | + | #include <framework/core/eventdispatcher.h> | |
| 27 | + | ||
| 28 | + | void UILayout::update() | |
| 29 | + | { | |
| 30 | + | //logTraceCounter(); | |
| 31 | + | if(!m_parentWidget) | |
| 32 | + | return; | |
| 33 | + | ||
| 34 | + | /* | |
| 35 | + | UIWidgetPtr parent = parentWidget; | |
| 36 | + | do { | |
| 37 | + | UILayoutPtr ownerLayout = parent->getLayout(); | |
| 38 | + | if(ownerLayout && ownerLayout->isUpdateDisabled()) | |
| 39 | + | return; | |
| 40 | + | parent = parent->getParent(); | |
| 41 | + | } while(parent); | |
| 42 | + | */ | |
| 43 | + | ||
| 44 | + | if(m_updateDisabled) | |
| 45 | + | return; | |
| 46 | + | ||
| 47 | + | if(m_updating) { | |
| 48 | + | updateLater(); | |
| 49 | + | return; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | m_updating = true; | |
| 53 | + | internalUpdate(); | |
| 54 | + | m_parentWidget->onLayoutUpdate(); | |
| 55 | + | m_updating = false; | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | void UILayout::updateLater() | |
| 59 | + | { | |
| 60 | + | if(m_updateDisabled || m_updateScheduled) | |
| 61 | + | return; | |
| 62 | + | ||
| 63 | + | if(!getParentWidget()) | |
| 64 | + | return; | |
| 65 | + | ||
| 66 | + | auto self = static_self_cast<UILayout>(); | |
| 67 | + | g_dispatcher.addEvent([self] { | |
| 68 | + | self->m_updateScheduled = false; | |
| 69 | + | self->update(); | |
| 70 | + | }); | |
| 71 | + | m_updateScheduled = true; | |
| 72 | + | } |
| @@ -0,0 +1,66 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef UILAYOUT_H | |
| 24 | + | #define UILAYOUT_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include <framework/luaengine/luaobject.h> | |
| 28 | + | #include <framework/otml/otml.h> | |
| 29 | + | ||
| 30 | + | // @bindclass | |
| 31 | + | class UILayout : public LuaObject | |
| 32 | + | { | |
| 33 | + | public: | |
| 34 | + | UILayout(UIWidgetPtr parentWidget) : m_parentWidget(parentWidget) { m_updateDisabled = 0; } | |
| 35 | + | ||
| 36 | + | void update(); | |
| 37 | + | void updateLater(); | |
| 38 | + | ||
| 39 | + | virtual void applyStyle(const OTMLNodePtr& styleNode) { } | |
| 40 | + | virtual void addWidget(const UIWidgetPtr& widget) { } | |
| 41 | + | virtual void removeWidget(const UIWidgetPtr& widget) { } | |
| 42 | + | void disableUpdates() { m_updateDisabled++; } | |
| 43 | + | void enableUpdates() { m_updateDisabled = std::max<int>(m_updateDisabled-1,0); } | |
| 44 | + | ||
| 45 | + | void setParent(UIWidgetPtr parentWidget) { m_parentWidget = parentWidget; } | |
| 46 | + | UIWidgetPtr getParentWidget() { return m_parentWidget; } | |
| 47 | + | ||
| 48 | + | bool isUpdateDisabled() { return m_updateDisabled > 0; } | |
| 49 | + | bool isUpdating() { return m_updating; } | |
| 50 | + | ||
| 51 | + | virtual bool isUIAnchorLayout() { return false; } | |
| 52 | + | virtual bool isUIBoxLayout() { return false; } | |
| 53 | + | virtual bool isUIHorizontalLayout() { return false; } | |
| 54 | + | virtual bool isUIVerticalLayout() { return false; } | |
| 55 | + | virtual bool isUIGridLayout() { return false; } | |
| 56 | + | ||
| 57 | + | protected: | |
| 58 | + | virtual bool internalUpdate() { return false; } | |
| 59 | + | ||
| 60 | + | int m_updateDisabled; | |
| 61 | + | stdext::boolean<false> m_updating; | |
| 62 | + | stdext::boolean<false> m_updateScheduled; | |
| 63 | + | UIWidgetPtr m_parentWidget; | |
| 64 | + | }; | |
| 65 | + | ||
| 66 | + | #endif |
| @@ -0,0 +1,541 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "uimanager.h" | |
| 24 | + | #include "ui.h" | |
| 25 | + | ||
| 26 | + | #include <framework/otml/otml.h> | |
| 27 | + | #include <framework/graphics/graphics.h> | |
| 28 | + | #include <framework/platform/platformwindow.h> | |
| 29 | + | #include <framework/core/eventdispatcher.h> | |
| 30 | + | #include <framework/core/application.h> | |
| 31 | + | #include <framework/core/resourcemanager.h> | |
| 32 | + | #include <framework/util/extras.h> | |
| 33 | + | ||
| 34 | + | UIManager g_ui; | |
| 35 | + | ||
| 36 | + | void UIManager::init() | |
| 37 | + | { | |
| 38 | + | // creates root widget | |
| 39 | + | m_rootWidget = UIWidgetPtr(new UIWidget); | |
| 40 | + | m_rootWidget->setId("root"); | |
| 41 | + | m_mouseReceiver = m_rootWidget; | |
| 42 | + | m_keyboardReceiver = m_rootWidget; | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | void UIManager::terminate() | |
| 46 | + | { | |
| 47 | + | // destroy root widget and its children | |
| 48 | + | m_rootWidget->destroy(); | |
| 49 | + | m_mouseReceiver = nullptr; | |
| 50 | + | m_keyboardReceiver = nullptr; | |
| 51 | + | m_rootWidget = nullptr; | |
| 52 | + | m_draggingWidget = nullptr; | |
| 53 | + | m_hoveredWidget = nullptr; | |
| 54 | + | for(auto& widget : m_pressedWidget) | |
| 55 | + | widget = nullptr; | |
| 56 | + | m_styles.clear(); | |
| 57 | + | m_destroyedWidgets.clear(); | |
| 58 | + | m_checkEvent = nullptr; | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | void UIManager::render(Fw::DrawPane drawPane) | |
| 62 | + | { | |
| 63 | + | m_rootWidget->draw(m_rootWidget->getRect(), drawPane); | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | void UIManager::resize(const Size& size) | |
| 67 | + | { | |
| 68 | + | m_rootWidget->setSize(size); | |
| 69 | + | m_moveTimer.restart(); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | void UIManager::inputEvent(const InputEvent& event) | |
| 73 | + | { | |
| 74 | + | UIWidgetList widgetList; | |
| 75 | + | switch(event.type) { | |
| 76 | + | case Fw::KeyTextInputEvent: | |
| 77 | + | g_lua.callGlobalField("g_ui", "onKeyText", event.keyText); | |
| 78 | + | m_keyboardReceiver->propagateOnKeyText(event.keyText); | |
| 79 | + | break; | |
| 80 | + | case Fw::KeyDownInputEvent: | |
| 81 | + | g_lua.callGlobalField("g_ui", "onKeyDown", event.keyCode, event.keyboardModifiers); | |
| 82 | + | m_keyboardReceiver->propagateOnKeyDown(event.keyCode, event.keyboardModifiers); | |
| 83 | + | break; | |
| 84 | + | case Fw::KeyPressInputEvent: | |
| 85 | + | g_lua.callGlobalField("g_ui", "onKeyPress", event.keyCode, event.keyboardModifiers, event.autoRepeatTicks); | |
| 86 | + | m_keyboardReceiver->propagateOnKeyPress(event.keyCode, event.keyboardModifiers, event.autoRepeatTicks); | |
| 87 | + | break; | |
| 88 | + | case Fw::KeyUpInputEvent: | |
| 89 | + | g_lua.callGlobalField("g_ui", "onKeyUp", event.keyCode, event.keyboardModifiers); | |
| 90 | + | m_keyboardReceiver->propagateOnKeyUp(event.keyCode, event.keyboardModifiers); | |
| 91 | + | break; | |
| 92 | + | case Fw::MousePressInputEvent: | |
| 93 | + | g_lua.callGlobalField("g_ui", "onMousePress", event.mousePos, event.mouseButton); | |
| 94 | + | ||
| 95 | + | if(m_mouseReceiver->isVisible() && (event.mouseButton == Fw::MouseLeftButton || event.mouseButton == Fw::MouseTouch2 || event.mouseButton == Fw::MouseTouch3)) { | |
| 96 | + | UIWidgetPtr pressedWidget = m_mouseReceiver->recursiveGetChildByPos(event.mousePos, false); | |
| 97 | + | if(pressedWidget && !pressedWidget->isEnabled()) | |
| 98 | + | pressedWidget = nullptr; | |
| 99 | + | updatePressedWidget(event.mouseButton, pressedWidget, event.mousePos); | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList); | |
| 103 | + | for(const UIWidgetPtr& widget : widgetList) { | |
| 104 | + | widget->recursiveFocus(Fw::MouseFocusReason); | |
| 105 | + | if(widget->onMousePress(event.mousePos, event.mouseButton)) | |
| 106 | + | break; | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | break; | |
| 110 | + | case Fw::MouseReleaseInputEvent: { | |
| 111 | + | g_lua.callGlobalField("g_ui", "onMouseRelease", event.mousePos, event.mouseButton); | |
| 112 | + | ||
| 113 | + | // release dragging widget | |
| 114 | + | bool accepted = false; | |
| 115 | + | if(m_draggingWidget && event.mouseButton == Fw::MouseLeftButton) | |
| 116 | + | accepted = updateDraggingWidget(nullptr, event.mousePos); | |
| 117 | + | ||
| 118 | + | if(!accepted) { | |
| 119 | + | m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList); | |
| 120 | + | ||
| 121 | + | // mouse release is always fired first on the pressed widget | |
| 122 | + | if(m_pressedWidget[event.mouseButton]) { | |
| 123 | + | auto it = std::find(widgetList.begin(), widgetList.end(), m_pressedWidget[event.mouseButton]); | |
| 124 | + | if(it != widgetList.end()) | |
| 125 | + | widgetList.erase(it); | |
| 126 | + | widgetList.push_front(m_pressedWidget[event.mouseButton]); | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | for(const UIWidgetPtr& widget : widgetList) { | |
| 130 | + | if(widget->onMouseRelease(event.mousePos, event.mouseButton)) | |
| 131 | + | break; | |
| 132 | + | } | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | if (event.mouseButton == Fw::MouseLeftButton || event.mouseButton == Fw::MouseTouch2 || event.mouseButton == Fw::MouseTouch3) { | |
| 136 | + | if(m_pressedWidget[event.mouseButton]) { | |
| 137 | + | updatePressedWidget(event.mouseButton, nullptr, event.mousePos, !accepted); | |
| 138 | + | } | |
| 139 | + | } | |
| 140 | + | break; | |
| 141 | + | } | |
| 142 | + | case Fw::MouseMoveInputEvent: { | |
| 143 | + | g_lua.callGlobalField("g_ui", "onMouseMove", event.mousePos, event.mouseMoved); | |
| 144 | + | ||
| 145 | + | // start dragging when moving a pressed widget | |
| 146 | + | if(m_pressedWidget[Fw::MouseLeftButton] && m_pressedWidget[Fw::MouseLeftButton]->isDraggable() && m_draggingWidget != m_pressedWidget[Fw::MouseLeftButton]) { | |
| 147 | + | // only drags when moving more than 4 pixels | |
| 148 | + | if((event.mousePos - m_pressedWidget[Fw::MouseLeftButton]->getLastClickPosition()).length() >= 4) | |
| 149 | + | updateDraggingWidget(m_pressedWidget[Fw::MouseLeftButton], event.mousePos - event.mouseMoved); | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | // mouse move can change hovered widgets | |
| 153 | + | updateHoveredWidget(true); | |
| 154 | + | ||
| 155 | + | // first fire dragging move | |
| 156 | + | if(m_draggingWidget) { | |
| 157 | + | if(m_draggingWidget->onDragMove(event.mousePos, event.mouseMoved)) | |
| 158 | + | break; | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | if (m_pressedWidget[Fw::MouseLeftButton]) { | |
| 162 | + | if (m_pressedWidget[Fw::MouseLeftButton]->onMouseMove(event.mousePos, event.mouseMoved)) { | |
| 163 | + | break; | |
| 164 | + | } | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | //m_mouseReceiver->propagateOnMouseMove(event.mousePos, event.mouseMoved, widgetList); | |
| 168 | + | m_rootWidget->propagateOnMouseMove(event.mousePos, event.mouseMoved, widgetList); | |
| 169 | + | for(const UIWidgetPtr& widget : widgetList) { | |
| 170 | + | if(widget->onMouseMove(event.mousePos, event.mouseMoved)) | |
| 171 | + | break; | |
| 172 | + | } | |
| 173 | + | break; | |
| 174 | + | } | |
| 175 | + | case Fw::MouseWheelInputEvent: | |
| 176 | + | g_lua.callGlobalField("g_ui", "onMouseWheel", event.mousePos, event.wheelDirection); | |
| 177 | + | ||
| 178 | + | m_rootWidget->propagateOnMouseEvent(event.mousePos, widgetList); | |
| 179 | + | for(const UIWidgetPtr& widget : widgetList) { | |
| 180 | + | if(widget->onMouseWheel(event.mousePos, event.wheelDirection)) | |
| 181 | + | break; | |
| 182 | + | } | |
| 183 | + | break; | |
| 184 | + | default: | |
| 185 | + | break; | |
| 186 | + | }; | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | void UIManager::updatePressedWidget(Fw::MouseButton button, const UIWidgetPtr& newPressedWidget, const Point& clickedPos, bool fireClicks) | |
| 190 | + | { | |
| 191 | + | UIWidgetPtr oldPressedWidget = m_pressedWidget[button]; | |
| 192 | + | m_pressedWidget[button] = newPressedWidget; | |
| 193 | + | ||
| 194 | + | // when releasing mouse inside pressed widget area send onClick event | |
| 195 | + | if (fireClicks && oldPressedWidget && oldPressedWidget->isEnabled() && oldPressedWidget->containsPoint(clickedPos)) | |
| 196 | + | oldPressedWidget->onClick(clickedPos); | |
| 197 | + | ||
| 198 | + | if(newPressedWidget) | |
| 199 | + | newPressedWidget->updateState(Fw::PressedState); | |
| 200 | + | ||
| 201 | + | if(oldPressedWidget) | |
| 202 | + | oldPressedWidget->updateState(Fw::PressedState); | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | bool UIManager::updateDraggingWidget(const UIWidgetPtr& draggingWidget, const Point& clickedPos) | |
| 206 | + | { | |
| 207 | + | bool accepted = false; | |
| 208 | + | ||
| 209 | + | UIWidgetPtr oldDraggingWidget = m_draggingWidget; | |
| 210 | + | m_draggingWidget = nullptr; | |
| 211 | + | if(oldDraggingWidget) { | |
| 212 | + | UIWidgetPtr droppedWidget; | |
| 213 | + | if(!clickedPos.isNull()) { | |
| 214 | + | auto clickedChildren = m_rootWidget->recursiveGetChildrenByPos(clickedPos); | |
| 215 | + | for(const UIWidgetPtr& child : clickedChildren) { | |
| 216 | + | if(child->onDrop(oldDraggingWidget, clickedPos)) { | |
| 217 | + | droppedWidget = child; | |
| 218 | + | break; | |
| 219 | + | } | |
| 220 | + | } | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | accepted = oldDraggingWidget->onDragLeave(droppedWidget, clickedPos); | |
| 224 | + | oldDraggingWidget->updateState(Fw::DraggingState); | |
| 225 | + | } | |
| 226 | + | ||
| 227 | + | if(draggingWidget) { | |
| 228 | + | if(draggingWidget->onDragEnter(clickedPos)) { | |
| 229 | + | m_draggingWidget = draggingWidget; | |
| 230 | + | draggingWidget->updateState(Fw::DraggingState); | |
| 231 | + | accepted = true; | |
| 232 | + | } | |
| 233 | + | } | |
| 234 | + | ||
| 235 | + | return accepted; | |
| 236 | + | } | |
| 237 | + | ||
| 238 | + | void UIManager::updateHoveredWidget(bool now) | |
| 239 | + | { | |
| 240 | + | if(m_hoverUpdateScheduled && !now) | |
| 241 | + | return; | |
| 242 | + | ||
| 243 | + | auto func = [this] { | |
| 244 | + | if(!m_rootWidget) | |
| 245 | + | return; | |
| 246 | + | ||
| 247 | + | m_hoverUpdateScheduled = false; | |
| 248 | + | UIWidgetPtr hoveredWidget; | |
| 249 | + | //if(!g_window.isMouseButtonPressed(Fw::MouseLeftButton) && !g_window.isMouseButtonPressed(Fw::MouseRightButton)) { | |
| 250 | + | hoveredWidget = m_rootWidget->recursiveGetChildByPos(g_window.getMousePosition(), false); | |
| 251 | + | if(hoveredWidget && !hoveredWidget->isEnabled()) | |
| 252 | + | hoveredWidget = nullptr; | |
| 253 | + | //} | |
| 254 | + | ||
| 255 | + | if(hoveredWidget != m_hoveredWidget) { | |
| 256 | + | UIWidgetPtr oldHovered = m_hoveredWidget; | |
| 257 | + | m_hoveredWidget = hoveredWidget; | |
| 258 | + | if(oldHovered) { | |
| 259 | + | oldHovered->updateState(Fw::HoverState); | |
| 260 | + | oldHovered->onHoverChange(false); | |
| 261 | + | } | |
| 262 | + | if(hoveredWidget) { | |
| 263 | + | hoveredWidget->updateState(Fw::HoverState); | |
| 264 | + | hoveredWidget->onHoverChange(true); | |
| 265 | + | } | |
| 266 | + | } | |
| 267 | + | }; | |
| 268 | + | ||
| 269 | + | if(now) | |
| 270 | + | func(); | |
| 271 | + | else { | |
| 272 | + | m_hoverUpdateScheduled = true; | |
| 273 | + | g_dispatcher.addEvent(func); | |
| 274 | + | } | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | void UIManager::onWidgetAppear(const UIWidgetPtr& widget) | |
| 278 | + | { | |
| 279 | + | if(widget->containsPoint(g_window.getMousePosition())) | |
| 280 | + | updateHoveredWidget(); | |
| 281 | + | } | |
| 282 | + | ||
| 283 | + | void UIManager::onWidgetDisappear(const UIWidgetPtr& widget) | |
| 284 | + | { | |
| 285 | + | if(widget->containsPoint(g_window.getMousePosition())) | |
| 286 | + | updateHoveredWidget(); | |
| 287 | + | } | |
| 288 | + | ||
| 289 | + | void UIManager::onWidgetDestroy(const UIWidgetPtr& widget) | |
| 290 | + | { | |
| 291 | + | AutoStat s(STATS_MAIN, "UIManager::onWidgetDestroy", stdext::format("%s (%s)", widget->getId(), widget->getParent() ? widget->getParent()->getId() : "")); | |
| 292 | + | ||
| 293 | + | // release input grabs | |
| 294 | + | if(m_keyboardReceiver == widget) | |
| 295 | + | resetKeyboardReceiver(); | |
| 296 | + | ||
| 297 | + | if(m_mouseReceiver == widget) | |
| 298 | + | resetMouseReceiver(); | |
| 299 | + | ||
| 300 | + | if(m_hoveredWidget == widget) | |
| 301 | + | updateHoveredWidget(); | |
| 302 | + | ||
| 303 | + | for (int i = 0; i <= Fw::MouseButtonLast + 1; ++i) { | |
| 304 | + | if (m_pressedWidget[i] == widget) { | |
| 305 | + | updatePressedWidget((Fw::MouseButton)i, nullptr); | |
| 306 | + | } | |
| 307 | + | } | |
| 308 | + | ||
| 309 | + | if(m_draggingWidget == widget) | |
| 310 | + | updateDraggingWidget(nullptr); | |
| 311 | + | ||
| 312 | + | if (!g_extras.debugWidgets) | |
| 313 | + | return; | |
| 314 | + | ||
| 315 | + | if(widget == m_rootWidget || !m_rootWidget) | |
| 316 | + | return; | |
| 317 | + | ||
| 318 | + | m_destroyedWidgets.push_back(widget); | |
| 319 | + | ||
| 320 | + | if(m_checkEvent && !m_checkEvent->isExecuted()) | |
| 321 | + | return; | |
| 322 | + | ||
| 323 | + | m_checkEvent = g_dispatcher.scheduleEvent([this] { | |
| 324 | + | g_lua.collectGarbage(); | |
| 325 | + | UIWidgetList backupList(std::move(m_destroyedWidgets)); | |
| 326 | + | m_destroyedWidgets.clear(); | |
| 327 | + | g_dispatcher.scheduleEvent([backupList] { | |
| 328 | + | g_lua.collectGarbage(); | |
| 329 | + | for(const UIWidgetPtr& widget : backupList) { | |
| 330 | + | if(widget->ref_count() != 1) | |
| 331 | + | g_logger.warning(stdext::format("widget '%s' (parent: '%s' (%s), source: '%s') destroyed but still have %d reference(s) left", widget->getId(), widget->getParent() ? widget->getParent()->getId() : "", widget->getParentId(), widget->getSource(), widget->getUseCount()-1)); | |
| 332 | + | } | |
| 333 | + | }, 1); | |
| 334 | + | }, 1000); | |
| 335 | + | } | |
| 336 | + | ||
| 337 | + | void UIManager::clearStyles() | |
| 338 | + | { | |
| 339 | + | m_styles.clear(); | |
| 340 | + | } | |
| 341 | + | ||
| 342 | + | bool UIManager::importStyle(std::string file) | |
| 343 | + | { | |
| 344 | + | try { | |
| 345 | + | file = g_resources.guessFilePath(file, "otui"); | |
| 346 | + | ||
| 347 | + | OTMLDocumentPtr doc = OTMLDocument::parse(file); | |
| 348 | + | ||
| 349 | + | for(const OTMLNodePtr& styleNode : doc->children()) | |
| 350 | + | importStyleFromOTML(styleNode); | |
| 351 | + | return true; | |
| 352 | + | } catch(stdext::exception& e) { | |
| 353 | + | g_logger.error(stdext::format("Failed to import UI styles from '%s': %s", file, e.what())); | |
| 354 | + | return false; | |
| 355 | + | } | |
| 356 | + | } | |
| 357 | + | ||
| 358 | + | bool UIManager::importStyleFromString(std::string data) | |
| 359 | + | { | |
| 360 | + | try { | |
| 361 | + | OTMLDocumentPtr doc = OTMLDocument::parseString(data, g_lua.getCurrentSourcePath()); | |
| 362 | + | for(const OTMLNodePtr& styleNode : doc->children()) | |
| 363 | + | importStyleFromOTML(styleNode); | |
| 364 | + | return true; | |
| 365 | + | } catch(stdext::exception& e) { | |
| 366 | + | g_logger.error(stdext::format("Failed to import UI styles from '%s': %s", g_lua.getCurrentSourcePath(), e.what())); | |
| 367 | + | return false; | |
| 368 | + | } | |
| 369 | + | } | |
| 370 | + | ||
| 371 | + | void UIManager::importStyleFromOTML(const OTMLNodePtr& styleNode) | |
| 372 | + | { | |
| 373 | + | std::string tag = styleNode->tag(); | |
| 374 | + | std::vector<std::string> split = stdext::split(tag, "<"); | |
| 375 | + | if(split.size() != 2) | |
| 376 | + | throw OTMLException(styleNode, "not a valid style declaration"); | |
| 377 | + | ||
| 378 | + | std::string name = split[0]; | |
| 379 | + | std::string base = split[1]; | |
| 380 | + | bool unique = false; | |
| 381 | + | ||
| 382 | + | stdext::trim(name); | |
| 383 | + | stdext::trim(base); | |
| 384 | + | ||
| 385 | + | if(name[0] == '#') { | |
| 386 | + | name = name.substr(1); | |
| 387 | + | unique = true; | |
| 388 | + | ||
| 389 | + | styleNode->setTag(name); | |
| 390 | + | styleNode->writeAt("__unique", true); | |
| 391 | + | } | |
| 392 | + | ||
| 393 | + | OTMLNodePtr oldStyle = m_styles[name]; | |
| 394 | + | ||
| 395 | + | // Warn about redefined styles | |
| 396 | + | /* | |
| 397 | + | if(!g_app.isRunning() && (oldStyle && !oldStyle->valueAt("__unique", false))) { | |
| 398 | + | auto it = m_styles.find(name); | |
| 399 | + | if(it != m_styles.end()) | |
| 400 | + | g_logger.warning(stdext::format("style '%s' is being redefined", name)); | |
| 401 | + | } | |
| 402 | + | */ | |
| 403 | + | ||
| 404 | + | if(!oldStyle || !oldStyle->valueAt("__unique", false) || unique) { | |
| 405 | + | OTMLNodePtr originalStyle = getStyle(base); | |
| 406 | + | if(!originalStyle) | |
| 407 | + | stdext::throw_exception(stdext::format("base style '%s', is not defined", base)); | |
| 408 | + | OTMLNodePtr style = originalStyle->clone(); | |
| 409 | + | style->merge(styleNode); | |
| 410 | + | style->setTag(name); | |
| 411 | + | m_styles[name] = style; | |
| 412 | + | } | |
| 413 | + | } | |
| 414 | + | ||
| 415 | + | OTMLNodePtr UIManager::getStyle(const std::string& styleName) | |
| 416 | + | { | |
| 417 | + | auto it = m_styles.find(styleName); | |
| 418 | + | if(it != m_styles.end()) | |
| 419 | + | return m_styles[styleName]; | |
| 420 | + | ||
| 421 | + | // styles starting with UI are automatically defined | |
| 422 | + | if(stdext::starts_with(styleName, "UI")) { | |
| 423 | + | OTMLNodePtr node = OTMLNode::create(styleName); | |
| 424 | + | node->writeAt("__class", styleName); | |
| 425 | + | m_styles[styleName] = node; | |
| 426 | + | return node; | |
| 427 | + | } | |
| 428 | + | ||
| 429 | + | return nullptr; | |
| 430 | + | } | |
| 431 | + | ||
| 432 | + | std::string UIManager::getStyleClass(const std::string& styleName) | |
| 433 | + | { | |
| 434 | + | OTMLNodePtr style = getStyle(styleName); | |
| 435 | + | if(style && style->get("__class")) | |
| 436 | + | return style->valueAt("__class"); | |
| 437 | + | return ""; | |
| 438 | + | } | |
| 439 | + | ||
| 440 | + | ||
| 441 | + | UIWidgetPtr UIManager::loadUIFromString(const std::string& data, const UIWidgetPtr& parent) | |
| 442 | + | { | |
| 443 | + | try { | |
| 444 | + | std::stringstream sstream; | |
| 445 | + | sstream.clear(std::ios::goodbit); | |
| 446 | + | sstream.write(&data[0], data.length()); | |
| 447 | + | sstream.seekg(0, std::ios::beg); | |
| 448 | + | OTMLDocumentPtr doc = OTMLDocument::parse(sstream, "(string)"); | |
| 449 | + | UIWidgetPtr widget; | |
| 450 | + | for (const OTMLNodePtr& node : doc->children()) { | |
| 451 | + | std::string tag = node->tag(); | |
| 452 | + | ||
| 453 | + | // import styles in these files too | |
| 454 | + | if (tag.find("<") != std::string::npos) | |
| 455 | + | importStyleFromOTML(node); | |
| 456 | + | else { | |
| 457 | + | if (widget) | |
| 458 | + | stdext::throw_exception("cannot have multiple main widgets in otui files"); | |
| 459 | + | widget = createWidgetFromOTML(node, parent); | |
| 460 | + | } | |
| 461 | + | } | |
| 462 | + | ||
| 463 | + | return widget; | |
| 464 | + | } catch (stdext::exception& e) { | |
| 465 | + | g_logger.error(stdext::format("failed to load UI from string: %s", e.what())); | |
| 466 | + | return nullptr; | |
| 467 | + | } | |
| 468 | + | } | |
| 469 | + | ||
| 470 | + | UIWidgetPtr UIManager::loadUI(std::string file, const UIWidgetPtr& parent) | |
| 471 | + | { | |
| 472 | + | try { | |
| 473 | + | file = g_resources.guessFilePath(file, "otui"); | |
| 474 | + | ||
| 475 | + | OTMLDocumentPtr doc = OTMLDocument::parse(file); | |
| 476 | + | UIWidgetPtr widget; | |
| 477 | + | for(const OTMLNodePtr& node : doc->children()) { | |
| 478 | + | std::string tag = node->tag(); | |
| 479 | + | ||
| 480 | + | // import styles in these files too | |
| 481 | + | if(tag.find("<") != std::string::npos) | |
| 482 | + | importStyleFromOTML(node); | |
| 483 | + | else { | |
| 484 | + | if(widget) | |
| 485 | + | stdext::throw_exception("cannot have multiple main widgets in otui files"); | |
| 486 | + | widget = createWidgetFromOTML(node, parent); | |
| 487 | + | } | |
| 488 | + | } | |
| 489 | + | ||
| 490 | + | return widget; | |
| 491 | + | } catch(stdext::exception& e) { | |
| 492 | + | g_logger.error(stdext::format("failed to load UI from '%s': %s", file, e.what())); | |
| 493 | + | return nullptr; | |
| 494 | + | } | |
| 495 | + | } | |
| 496 | + | ||
| 497 | + | UIWidgetPtr UIManager::createWidget(const std::string& styleName, const UIWidgetPtr& parent) | |
| 498 | + | { | |
| 499 | + | OTMLNodePtr node = OTMLNode::create(styleName); | |
| 500 | + | try { | |
| 501 | + | return createWidgetFromOTML(node, parent); | |
| 502 | + | } catch(stdext::exception& e) { | |
| 503 | + | g_logger.error(stdext::format("failed to create widget from style '%s': %s", styleName, e.what())); | |
| 504 | + | return nullptr; | |
| 505 | + | } | |
| 506 | + | } | |
| 507 | + | ||
| 508 | + | UIWidgetPtr UIManager::createWidgetFromOTML(const OTMLNodePtr& widgetNode, const UIWidgetPtr& parent) | |
| 509 | + | { | |
| 510 | + | OTMLNodePtr originalStyleNode = getStyle(widgetNode->tag()); | |
| 511 | + | if(!originalStyleNode) | |
| 512 | + | stdext::throw_exception(stdext::format("'%s' is not a defined style", widgetNode->tag())); | |
| 513 | + | ||
| 514 | + | OTMLNodePtr styleNode = originalStyleNode->clone(); | |
| 515 | + | styleNode->merge(widgetNode); | |
| 516 | + | ||
| 517 | + | std::string widgetType = styleNode->valueAt("__class"); | |
| 518 | + | ||
| 519 | + | // call widget creation from lua | |
| 520 | + | UIWidgetPtr widget = g_lua.callGlobalField<UIWidgetPtr>(widgetType, "create"); | |
| 521 | + | if(parent) | |
| 522 | + | parent->addChild(widget); | |
| 523 | + | ||
| 524 | + | if(widget) { | |
| 525 | + | widget->callLuaField("onCreate"); | |
| 526 | + | ||
| 527 | + | widget->setStyleFromNode(styleNode); | |
| 528 | + | ||
| 529 | + | for(const OTMLNodePtr& childNode : styleNode->children()) { | |
| 530 | + | if(!childNode->isUnique()) { | |
| 531 | + | createWidgetFromOTML(childNode, widget); | |
| 532 | + | styleNode->removeChild(childNode); | |
| 533 | + | } | |
| 534 | + | } | |
| 535 | + | } else | |
| 536 | + | stdext::throw_exception(stdext::format("unable to create widget of type '%s'", widgetType)); | |
| 537 | + | ||
| 538 | + | widget->callLuaField("onSetup"); | |
| 539 | + | ||
| 540 | + | return widget; | |
| 541 | + | } |