1 modulefluid.future.context;
2 3 importstd.meta;
4 importstd.traits;
5 6 importfluid.types;
7 importfluid.tree : TreeAction;
8 importfluid.future.stack;
9 importfluid.future.static_id;
10 11 @safe:
12 13 structTreeContext {
14 15 TreeContextData* ptr;
16 17 aliasptrthis;
18 19 /// Create the context if it doesn't already exist.20 voidprepare() {
21 22 if (ptrisnull) {
23 ptr = newTreeContextData();
24 }
25 26 }
27 28 boolopCast(T : bool)() const {
29 30 returnptr !isnull;
31 32 }
33 34 }
35 36 structTreeContextData {
37 38 public {
39 40 /// Keeps track of currently active I/O systems.41 TreeIOContextio;
42 43 /// Manages and runs tree actions.44 TreeActionContextactions;
45 46 }
47 48 private {
49 int_lockTint;
50 auto_tint = Color(0xff, 0xff, 0xff, 0xff);
51 }
52 53 /// Tint is a transitive styling property that can be used to reduce color intensity of everything54 /// that a node draws. Tint applies per channel, which means it can be used to reduce opacity (by changing55 /// the alpha channel) and any of the three RGB colors.56 ///57 /// A tint of value `0` sets intensity to 0% (disable). A tint of value `255` sets intensity to 100% (no change).58 ///59 /// See_Also:60 /// `Style.tint`61 /// Returns: The current tint.62 Colortint() constnothrow {
63 return_tint;
64 }
65 66 package (fluid)
67 Colortint(ColornewValue) nothrow {
68 if (_lockTint > 0) {
69 return_tint;
70 }
71 else {
72 return_tint = newValue;
73 }
74 }
75 76 /// Lock tint in place, preventing it from changing, or cancel a lock, making changes possible again.77 ///78 /// This function is needed for compatibility with the legacy `FluidBackend` system.79 /// Locks can be stacked, so if `lockTint()` is called twice, `unlockTint()` also has to be called twice80 /// to unlock tinting.81 ///82 /// It is expected that this function will be deprecated as soon as `FluidBackend` is no longer a part83 /// of Fluid. It will then be deleted in the next minor release.84 voidlockTint() {
85 _lockTint++;
86 }
87 88 /// ditto89 voidunlockTint() {
90 if (_lockTint > 0) {
91 _lockTint--;
92 }
93 }
94 95 }
96 97 /// Active context for I/O operations. Keeps track of currently active systems for each I/O interface.98 ///99 /// I/O systems are changed by a replace operation. `replace` takes the new I/O systems, but returns the one set100 /// previously. This can be used to manage I/Os as a stack:101 ///102 /// ---103 /// auto previous = io.replace(id, this);104 /// scope (exit) io.replace(id, previous);105 /// ---106 structTreeIOContext {
107 108 importstd.range;
109 importstd.algorithm : completeSort;
110 111 structIOInstance {
112 IOIDid;
113 IOio;
114 intopCmp(constIOInstancerhs) const {
115 returnid.opCmp(rhs.id);
116 }
117 intopCmp(constIOIDrhs) const {
118 returnid.opCmp(rhs);
119 }
120 }
121 122 /// Key-value pairs of active I/O systems. Each pair contains the system and the ID of the interface123 /// it implements. Pairs are sorted by the interface ID.124 privateSortedRange!(IOInstance[]) activeIOs;
125 126 /// Returns:127 /// The active instance of the given IO interface.128 /// `null`, no instance of this interface is currently active.129 /// Params:130 /// id = ID of the IO interface to load.131 IOget(IOIDid) {
132 133 autorange = activeIOs.equalRange(id);
134 if (range.empty) {
135 returnnull;
136 }
137 else {
138 returnrange.front.io;
139 }
140 141 }
142 143 /// ditto144 Tget(T)() {
145 146 constid = ioID!T;
147 returncast(T) get(id);
148 149 }
150 151 /// Set currently active I/O instance for a set interface.152 /// Params:153 /// id = ID of the IO interface the instance implements.154 /// system = System to activate.155 /// Returns:156 /// Returns *previously set* I/O instance.157 IOreplace(IOIDid, IOsystem) {
158 159 autorange = activeIOs.equalRange(id);
160 autoinstance = IOInstance(id, system);
161 162 // Nothing set, add a new value163 if (range.empty) {
164 IOInstance[1] instanceRange = instance;
165 completeSort(activeIOs, instanceRange[]);
166 activeIOs = assumeSorted(activeIOs.release ~ instanceRange[]);
167 returnnull;
168 }
169 170 // Override the previous result171 else {
172 autoprevious = range.front;
173 range.front = instance;
174 returnprevious.io;
175 }
176 177 }
178 179 /// Iterate on all active I/O systems.180 ///181 /// Elements are passed by value and cannot be modified.182 ///183 /// Returns:184 /// A sorted input range of `(IOID id, IO io)` pairs.185 autoopIndex() {
186 187 // `map` should prevent modifications188 importstd.algorithm : map;
189 190 returnactiveIOs.save.map!(a => a).assumeSorted;
191 192 }
193 194 /// Create a copy of the context.195 ///196 /// This creates a shallow clone: I/O systems can be replaced in the copy without affecting the original197 /// and vice-versa. The individual systems will *not* be copied.198 ///199 /// This is useful if the I/O stack created at some specific point in time has to be reproduced elsewhere.200 /// For example, in a drag-and-drop scenario, while a node is being dragged, it does not have a place as a child201 /// of any node. A copy of the I/O stack it had at the start is used to continue drawing while the node202 /// is "in air".203 ///204 /// Returns:205 /// A shallow copy of the I/O context.206 TreeIOContextdup() {
207 returnTreeIOContext(activeIOs.release.dup.assumeSorted);
208 }
209 210 }
211 212 enumisIO(T) = is(T == interface)
213 && is(T : IO)
214 && !is(T == IO);
215 216 /// Get all IOs implemented by the given type217 aliasallIOs(T) = Filter!(isIO, InterfacesTuple!T);
218 219 interfaceHasContext {
220 221 /// Returns: The current tree context.222 inout(TreeContext) treeContext() inoutnothrow;
223 224 }
225 226 interfaceIO : HasContext {
227 228 boolopEquals(constObject) const;
229 230 /// Load a resource by reference. This is the same as `Node.load`.231 /// Params:232 /// resource = Resource to load. It will be updated with identifying information.233 voidloadTo(thisThis, T)(refTresource) {
234 235 autoio = cast(This) this;
236 237 // Load the resource238 constid = io.load(resource);
239 240 // Pass data into the resource241 resource.load(io, id);
242 243 }
244 245 }
246 247 IOIDioID(T)()
248 if (isIO!T) {
249 250 returnIOID(staticID!T);
251 252 }
253 254 /// ID for an I/O interface.255 structIOID {
256 257 StaticIDid;
258 259 intopCmp(constIOIDrhs) const {
260 returnid.opCmp(rhs.id);
261 }
262 263 }
264 265 /// Keeps track of currently active actions.266 structTreeActionContext {
267 268 importstd.array;
269 270 private {
271 272 structRunningAction {
273 274 TreeActionaction;
275 intgeneration;
276 277 boolisStopped() const {
278 returnaction.generation > generation;
279 }
280 281 }
282 283 /// Currently running actions.284 Appender!(RunningAction[]) _actions;
285 286 /// Number of running iterators. Removing tree actions will only happen if there is exactly one287 /// running iterator, as to not break the other ones.288 ///289 /// Multiple iterators may run in case a tree action draws nodes on its own: one iterator triggers290 /// the action, and the drawn node activates another iterator.291 int_runningIterators;
292 293 }
294 295 /// Start a number of tree actions. As the node tree is drawn, the action's hook will be called whenever296 /// a relevant place is reached in the tree.297 ///298 /// To stop a running action, call the action's `stop` method. Most tree actions will do it automatically299 /// as soon as their job is finished.300 ///301 /// If the action is already running, the previous run will be aborted. The action can only run once at a time.302 ///303 /// Params:304 /// actions = Actions to spawn.305 voidspawn(TreeAction[] actions...) {
306 307 _actions.reserve(_actions[].length + actions.length);
308 309 // Start every action and run the hook310 foreach (action; actions) {
311 312 _actions ~= RunningAction(action, ++action.generation);
313 action.started();
314 315 316 }
317 318 }
319 320 /// List all currently active actions in a loop.321 intopApply(intdelegate(TreeAction) @safeyield) {
322 323 // Update the iterator counter324 _runningIterators++;
325 scope (exit) _runningIterators--;
326 327 boolkept;
328 329 // Iterate on active actions330 // Do *not* increment if an action was removed331 for (size_ti = 0; i < _actions[].length; i += kept) {
332 333 autoaction = _actions[][i];
334 kept = true;
335 336 // If there's one running iterator, remove it from the array337 // Don't pass stopped actions to the iterator338 if (action.isStopped) {
339 340 if (_runningIterators == 1) {
341 _actions[][i] = _actions[][$-1];
342 _actions.shrinkTo(_actions[].length - 1);
343 kept = false;
344 }
345 continue;
346 347 }
348 349 // Run the hook350 if (autoresult = yield(action.action)) returnresult;
351 352 }
353 354 return0;
355 356 }
357 358 }