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() { }