Client Upload Part 2

RD Core / Commit #5

Commit Client Upload Part 2

Alex Alex committed 19/10/2026 00:06 main Patch
1,244 files +271,215 -0
A RD_Client/src/framework/stdext/packed_storage.h +113-0 View file
@@ -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
A RD_Client/src/framework/stdext/shared_object.h +132-0 View file
@@ -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
A RD_Client/src/framework/stdext/stdext.h +43-0 View file
@@ -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
A RD_Client/src/framework/stdext/string.cpp +301-0 View file
@@ -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+}
A RD_Client/src/framework/stdext/string.h +79-0 View file
@@ -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
A RD_Client/src/framework/stdext/thread.h +31-0 View file
@@ -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+
A RD_Client/src/framework/stdext/time.cpp +55-0 View file
@@ -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+}
A RD_Client/src/framework/stdext/time.h +50-0 View file
@@ -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+
A RD_Client/src/framework/stdext/traits.h +37-0 View file
@@ -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
A RD_Client/src/framework/stdext/types.h +49-0 View file
@@ -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
A RD_Client/src/framework/stdext/uri.cpp +24-0 View file
@@ -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+}
A RD_Client/src/framework/stdext/uri.h +10-0 View file
@@ -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);
A RD_Client/src/framework/ui/declarations.h +54-0 View file
@@ -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
A RD_Client/src/framework/ui/ui.h +35-0 View file
@@ -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
A RD_Client/src/framework/ui/uianchorlayout.cpp +275-0 View file
@@ -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+}
A RD_Client/src/framework/ui/uianchorlayout.h +85-0 View file
@@ -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
A RD_Client/src/framework/ui/uiboxlayout.cpp +42-0 View file
@@ -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+
A RD_Client/src/framework/ui/uiboxlayout.h +48-0 View file
@@ -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
A RD_Client/src/framework/ui/uigridlayout.cpp +132-0 View file
@@ -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+}
A RD_Client/src/framework/ui/uigridlayout.h +68-0 View file
@@ -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
A RD_Client/src/framework/ui/uihorizontallayout.cpp +100-0 View file
@@ -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+}
A RD_Client/src/framework/ui/uihorizontallayout.h +46-0 View file
@@ -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
A RD_Client/src/framework/ui/uilayout.cpp +72-0 View file
@@ -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+}
A RD_Client/src/framework/ui/uilayout.h +66-0 View file
@@ -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
A RD_Client/src/framework/ui/uimanager.cpp +541-0 View file
@@ -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+}
Top