/src/wt/src/Wt/WJavaScriptHandle.h
Line | Count | Source |
1 | | // This may look like C code, but it's really -*- C++ -*- |
2 | | /* |
3 | | * Copyright (C) 2015 Emweb bv, Herent, Belgium. |
4 | | * |
5 | | * See the LICENSE file for terms of use. |
6 | | */ |
7 | | #ifndef WJAVASCRIPT_HANDLE_H_ |
8 | | #define WJAVASCRIPT_HANDLE_H_ |
9 | | |
10 | | #include <cassert> |
11 | | |
12 | | #include <Wt/WException.h> |
13 | | #include <Wt/WJavaScriptExposableObject.h> |
14 | | |
15 | | namespace Wt { |
16 | | |
17 | | /*! \class WJavaScriptHandle Wt/WJavaScriptHandle.h Wt/WJavaScriptHandle.h |
18 | | * \brief A handle to a JavaScript representation of an object. |
19 | | * |
20 | | * A %WJavaScriptHandle allows to access and modify an object in JavaScript. |
21 | | * This is useful to avoid server roundtrips when frequently updating something, |
22 | | * e.g. to interact with and animate a WPaintedWidget. |
23 | | * |
24 | | * You can use the value() of a %WJavaScriptHandle just as you would normally, |
25 | | * with the exception that it will be \link WJavaScriptExposableObject::isJavaScriptBound() |
26 | | * JavaScript bound\endlink, and so will any copies you make of it. You should |
27 | | * not modify a JavaScript bound object, as this will not change its client side |
28 | | * representation. Use the handle's setValue() method instead. |
29 | | * |
30 | | * You can access (and modify) the value of a handle on the client side using jsRef(). |
31 | | * |
32 | | * You can update the value from the server with setValue(). Changes on the |
33 | | * client side will be synced back to the server. |
34 | | * |
35 | | * Currently, only WPaintedWidget allows the use of \link WJavaScriptExposableObject |
36 | | * JavaScript exposable objects\endlink. |
37 | | * |
38 | | * \sa WJavaScriptExposableObject, WPaintedWidget |
39 | | */ |
40 | | template <typename T> |
41 | | class WJavaScriptHandle |
42 | | { |
43 | | public: |
44 | | /*! \brief Create an invalid WJavaScriptHandle |
45 | | * |
46 | | * The handle will be invalid until a valid WJavaScriptHandle |
47 | | * is copy-assigned to it. |
48 | | */ |
49 | | WJavaScriptHandle() noexcept |
50 | | : value_(nullptr), id_(0) |
51 | | { } |
52 | | |
53 | | /*! \brief Copy constructor |
54 | | */ |
55 | | WJavaScriptHandle(const WJavaScriptHandle &handle) noexcept |
56 | | : value_(handle.value_), id_(handle.id_) |
57 | | { |
58 | | assert(value_ == nullptr || |
59 | | value_->clientBinding_ != nullptr); |
60 | | } |
61 | | |
62 | | /*! \brief Copy assignment operator |
63 | | */ |
64 | | WJavaScriptHandle &operator=(const WJavaScriptHandle &handle) noexcept |
65 | | { |
66 | | assert(handle.value_ == nullptr || |
67 | | handle.value_->clientBinding_ != nullptr); |
68 | | |
69 | | value_ = handle.value_; |
70 | | id_ = handle.id_; |
71 | | |
72 | | return (*this); |
73 | | } |
74 | | |
75 | | /*! \brief Move constructor |
76 | | */ |
77 | | WJavaScriptHandle(WJavaScriptHandle &&handle) noexcept |
78 | | : value_(handle.value_), id_(handle.id_) |
79 | | { |
80 | | assert(handle.value_ == nullptr || |
81 | | handle.value_->clientBinding_ != nullptr); |
82 | | |
83 | | handle.value_ = nullptr; |
84 | | handle.id_ = 0; |
85 | | } |
86 | | |
87 | | /*! \brief Move assignment operator |
88 | | */ |
89 | | WJavaScriptHandle &operator=(WJavaScriptHandle &&handle) noexcept |
90 | | { |
91 | | if (this == &handle) |
92 | | return *this; |
93 | | |
94 | | assert(handle.value_ == nullptr || |
95 | | handle.value_->clientBinding_ != nullptr); |
96 | | |
97 | | value_ = handle.value_; |
98 | | id_ = handle.id_; |
99 | | handle.value_ = nullptr; |
100 | | handle.id_ = 0; |
101 | | |
102 | | return *this; |
103 | | } |
104 | | |
105 | | /*! \brief Destructor |
106 | | */ |
107 | | ~WJavaScriptHandle() |
108 | | { } |
109 | | |
110 | | /*! \brief Returns whether this is a valid handle |
111 | | * |
112 | | * A handle is not valid if it is not connected to a JavaScript |
113 | | * representation. To make a WJavaScriptHandle valid, a valid |
114 | | * WJavaScriptHandle has to be copy-assigned to it. The various |
115 | | * createJS... methods in WPaintedWidget return a valid handle. |
116 | | */ |
117 | | bool isValid() const noexcept { |
118 | | return value_; |
119 | | } |
120 | | |
121 | | /*! \brief Returns the JavaScript representation of the object. |
122 | | * |
123 | | * You can access and modify the value of this handle through its jsRef(). |
124 | | * |
125 | | * \throw WException The handle is \link isValid() invalid\endlink |
126 | | */ |
127 | | std::string jsRef() const { |
128 | | if (!value_) { |
129 | | throw WException("Can't retrieve the value of an invalid handle!"); |
130 | | } |
131 | | return value_->clientBinding_->jsRef_; |
132 | | } |
133 | | |
134 | | /*! \brief Set the value for this handle. |
135 | | * |
136 | | * The value may not be JavaScript bound, i.e. related to another WJavaScriptHandle. |
137 | | * The change to the value will be synced to the client side equivalent. |
138 | | * |
139 | | * \throw WException The handle is \link isValid() invalid\endlink |
140 | | * \throw WException Trying to assign a JavaScript bound value |
141 | | */ |
142 | | void setValue(const T& v) |
143 | | { |
144 | | if (!value_) { |
145 | | throw WException("Can't assign a value to an invalid handle!"); |
146 | | } |
147 | | if (v.isJavaScriptBound()) throw WException("Can not assign a JavaScript bound value to a WJavaScriptHandle!"); |
148 | | // Rescue the binding from being overridden by the assignment, and deleted |
149 | | WJavaScriptExposableObject::JSInfo *binding = value_->clientBinding_; |
150 | | value_->clientBinding_ = nullptr; |
151 | | |
152 | | bool changed = !value_->closeTo(v); |
153 | | (*value_) = v; |
154 | | |
155 | | value_->clientBinding_ = binding; |
156 | | if (changed) |
157 | | value_->clientBinding_->context_->dirty_[id_] = true; |
158 | | } |
159 | | |
160 | | /*! \brief Get the value for this handle. |
161 | | * |
162 | | * \warning You should not modify this value or any copy of it on the server side, |
163 | | * because this will not be synced to the client side. Use setValue() instead. |
164 | | * |
165 | | * \throw WException The handle is \link isValid() invalid\endlink |
166 | | */ |
167 | | const T& value() const { |
168 | | if (!value_) { |
169 | | throw WException("Can't retrieve the value from an invalid handle!"); |
170 | | } |
171 | | return *value_; |
172 | | } |
173 | | |
174 | | private: |
175 | | friend class WJavaScriptObjectStorage; |
176 | | |
177 | | WJavaScriptHandle(int id, T *t) noexcept |
178 | 0 | : value_(t), id_(id) |
179 | 0 | { |
180 | | assert(t != nullptr && t->clientBinding_ != nullptr); |
181 | 0 | } Unexecuted instantiation: Wt::WJavaScriptHandle<Wt::WTransform>::WJavaScriptHandle(int, Wt::WTransform*) Unexecuted instantiation: Wt::WJavaScriptHandle<Wt::WBrush>::WJavaScriptHandle(int, Wt::WBrush*) Unexecuted instantiation: Wt::WJavaScriptHandle<Wt::WPen>::WJavaScriptHandle(int, Wt::WPen*) Unexecuted instantiation: Wt::WJavaScriptHandle<Wt::WPainterPath>::WJavaScriptHandle(int, Wt::WPainterPath*) Unexecuted instantiation: Wt::WJavaScriptHandle<Wt::WRectF>::WJavaScriptHandle(int, Wt::WRectF*) Unexecuted instantiation: Wt::WJavaScriptHandle<Wt::WPointF>::WJavaScriptHandle(int, Wt::WPointF*) |
182 | | |
183 | | T *value_; |
184 | | int id_; |
185 | | }; |
186 | | |
187 | | } |
188 | | |
189 | | #endif // WJAVASCRIPT_HANDLE_H_ |