1 module fluid.showcase.buttons;
2 
3 import std.range;
4 
5 import fluid;
6 import fluid.showcase;
7 
8 
9 @safe:
10 
11 
12 @(
13     () => label("Buttons are the most basic node when it comes to user input. Pressing a button triggers an event, "
14         ~ "and that's about the functionality of the button. Under the hood, buttons are labels — but they're modified "
15         ~ "to react to user clicks."),
16     () => label("To initialize a button, you need to pass a delegate to dictate the button's effect. Here's one that "
17         ~ "does nothing:"),
18 )
19 Label buttonExample() {
20 
21     return button("Hello, World!", delegate { });
22 
23 }
24 
25 @(
26     () => label("Naturally, user interfaces are made to react to user input and other events that happen in the "
27         ~ "system. The interface needs to change to keep displayed information up to date. Glui nodes expose "
28         ~ "properties that make them possible to change. So, we can make the button change its text when clicked."),
29     () => label("Just note that to refer to the button within itself we need to declare it beforehand."),
30 )
31 Label mutableLabelExample() {
32 
33     import std.range : cycle;
34 
35     Button!() myButton;
36     auto texts = cycle(["Hello!", "Bonjour!", "¡Hola!", "Здравствуйте!"]);
37 
38     // Create the button
39     myButton = button(texts.front, delegate {
40 
41         // Switch to the next text when clicked
42         texts.popFront;
43         myButton.text = texts.front;
44 
45     });
46 
47     return myButton;
48 
49 }
50 
51 @(
52     () => label("Let's try making this a bit more complex. Once we connect a few nodes, we need to retain access to "
53         ~ "all the nodes we're interested. If we place a label in a frame, we still need to keep a reference to the "
54         ~ "label if we intend to change it. Fortunately, we can assign variables while building the tree. This is "
55         ~ "somewhat unconventional, since a typical user interface system would instead have you query nodes by ID, "
56         ~ "but this is simpler and more flexible."),
57 )
58 Frame nestedLabelFirstExample() {
59 
60     Label myLabel;
61     auto texts = cycle(["Hello!", "Bonjour!", "¡Hola!", "Здравствуйте!"]);
62 
63     return vframe(
64         myLabel = label(texts.front),
65         button("Change text", delegate {
66 
67             // Change text of the button when clicked
68             texts.popFront;
69             myLabel.text = texts.front;
70 
71         }),
72     );
73 
74 }
75 
76 @system
77 void unsafeFunction() { }
78 
79 @(
80     () => label("Of course, we also have the choice to assign the label on the same line it was declared "
81         ~ "on. The downside of that usage is that it is usually helpful to keep each component of the tree united "
82         ~ "for easier analysis. However, it might be preferrable if the tree is becoming complex, so you need "
83         ~ "to find the right balance."),
84 
85     () => label(.headingTheme, "A note on @safe-ty"),
86     () => label("D has a memory safety checker, which will help prevent memory errors at compile-time. Fluid is fully "
87         ~ "opted into it, which means your delegates will be rejected if the compiler deems them unsafe!"),
88     () => label("Most importantly, C functions like the ones provided by Raylib aren't tested for safety, so they're "
89         ~ "marked as unsafe and cannot be used in Fluid delegates. If you need to, you'll have to tell the compiler "
90         ~ "by making them @trusted."),
91     () => label("Tip: Memory safety is controlled with three attributes, @system, @trusted and @safe. The first two "
92         ~ "disable memory safety checks, while @safe enables them. Use @trusted to mark code which you 'trust' to be "
93         ~ "safe, so you can call it from @safe code."),
94     () => button("More information on D memory safety", delegate {
95         openURL("https://dlang.org/spec/memory-safe-d.html");
96     }),
97 )
98 void safetyCheckerExample() @system {
99 
100     // This button calls an unsafe function, so it'll be rejected.
101     button("Oops.", delegate {
102 
103         /* unsafeFunction(); Won't work! */
104 
105     });
106 
107     // To make it work, you have to make it @trusted.
108     button("Trusted button.", delegate () @trusted {
109 
110         unsafeFunction();
111 
112     });
113 
114 }
115 
116 @(
117     () => label(.headingTheme, "Editing layouts"),
118     () => label("Frame contents can be changed at runtime by changing their 'children' property. The operation is a "
119         ~ "bit more complex than updating labels, and you have to pay attention to this one if you intend to rearrange "
120         ~ "frames this way."),
121 )
122 Frame mutableFrameExample() {
123 
124     import std.algorithm : bringToFront;
125 
126     Frame myFrame;
127 
128     return vframe(
129         myFrame = hframe(
130             label("Foo, "),
131             label("Bar, "),
132             label("Baz, "),
133         ),
134         button("Reorder nodes", delegate {
135 
136             // Move the last node to start
137             bringToFront(
138                 myFrame.children[0..$-1],
139                 myFrame.children[$-1..$]
140             );
141             myFrame.updateSize();
142 
143         }),
144     );
145 
146 }
147 
148 // TODO cover .layout?
149 
150 @(
151     () => label("Fluid frames provide full control over their contents, making it possible to use the power of D "
152         ~ "libraries for the job. See, to move the nodes around in the example above, we use 'bringToFront' from "
153         ~ "std.algorithm. As a downside to this, Fluid is not able to detect changes and resize ahead of time, like it "
154         ~ "does with labels, so you must call 'updateSize()' on the frame for the changes to apply. Fluid will issue "
155         ~ "an error at runtime if you don't do this, so be careful!"),
156     () => label("Do not worry though, a lot of layout management can be made easier with helpers like nodeSlot, which "
157         ~ "we'll cover later."),
158 )
159 void endExample() { }