1 modulefluid.tour.buttons;
2 3 importstd.range;
4 5 importfluid;
6 importfluid.tour;
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 LabelbuttonExample() {
20 21 returnbutton("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 LabelmutableLabelExample() {
32 33 importstd.range : cycle;
34 35 ButtonmyButton;
36 autotexts = cycle(["Hello!", "Bonjour!", "¡Hola!", "Здравствуйте!"]);
37 38 // Create the button39 myButton = button(texts.front, delegate {
40 41 // Switch to the next text when clicked42 texts.popFront;
43 myButton.text = texts.front;
44 45 });
46 47 returnmyButton;
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 FramenestedLabelFirstExample() {
59 60 LabelmyLabel;
61 autotexts = cycle(["Hello!", "Bonjour!", "¡Hola!", "Здравствуйте!"]);
62 63 returnvframe(
64 myLabel = label(texts.front),
65 button("Change text", delegate {
66 67 // Change text of the button when clicked68 texts.popFront;
69 myLabel.text = texts.front;
70 71 }),
72 );
73 74 }
75 76 @system77 voidunsafeFunction() { }
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(.tags!(Tags.heading), "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 voidsafetyCheckerExample() @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(.tags!(Tags.heading), "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 FramemutableFrameExample() {
123 124 importstd.algorithm : bringToFront;
125 126 FramemyFrame;
127 128 returnvframe(
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 start137 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 voidendExample() { }