summaryrefslogtreecommitdiff
path: root/pages/develop/tutorials
diff options
context:
space:
mode:
authorXavi Artigas <xavierartigas@yahoo.es>2018-10-18 02:50:35 -0700
committerapache <apache@e5-web1.enlightenment.org>2018-10-18 02:50:35 -0700
commitb9ac8862a61eda0ac543c218a5d6c2d19f0a3619 (patch)
treed4f60ee8d3450aefb8de8e57e75c866e4a520fc3 /pages/develop/tutorials
parentcc355d73c09b18a92edb94ca9c0e735f733059d2 (diff)
Wiki page texteditor-cs.md changed with summary [] by Xavi Artigas
Diffstat (limited to 'pages/develop/tutorials')
-rw-r--r--pages/develop/tutorials/csharp/texteditor-cs.md.txt619
1 files changed, 619 insertions, 0 deletions
diff --git a/pages/develop/tutorials/csharp/texteditor-cs.md.txt b/pages/develop/tutorials/csharp/texteditor-cs.md.txt
new file mode 100644
index 000000000..2093a48db
--- /dev/null
+++ b/pages/develop/tutorials/csharp/texteditor-cs.md.txt
@@ -0,0 +1,619 @@
1---
2~~Title: Graphical "Hello World" in C#~~
3~~NOCACHE~~
4---
5
6# A Simple Text Editor in C# #
7
8In this tutorial you will learn how to build a simple text editor application, including:
9
10* A toolbar with buttons
11* A text entry area
12* File saving and loading capabilities
13* Message popups
14
15| | WARNING | |
16| --- | ------- | --- |
17| ![NOTE](/_media/note-important.png) | **The C# bindings are currently in BETA state**<br>They should only be used for experimenting and **NOT** for any product development.<br>The source code for the tutorials is subject to change in the future. | ![NOTE](/_media/note-important.png) |
18
19## Prerequisites ##
20
21* Read the [Setting Up a C# Development Environment](/develop/setup/csharp/) guide so you are able to build and run EFL C# applications.
22* This tutorial builds on top of the previous one, [Graphical "Hello World" in C#](hello-world-gui-cs.md), so you should have read that one first.
23
24## The Code ##
25
26Copy the code below into a file and build it as instructed in the [Setting Up a C# Development Environment](/develop/setup/csharp/) guide. You can also find this file in the [EFL examples repository](https://git.enlightenment.org/tools/examples.git/) in [`apps/csharp/texteditor`](https://git.enlightenment.org/tools/examples.git/tree/apps/csharp/texteditor).
27
28```csharp
29/* Simple text editor with a main text box and a toolbar on top:
30 +vbox----------------------------------------+
31 | +hbox------------------------------------+ |
32 | | +btn-+ +btn-+ +btn-+ +box-----+ +btn-+ | |
33 | | |NEW | |SAVE| |LOAD| | spacer | |QUIT| | |
34 | | +----+ +----+ +----+ +--------+ +----+ | |
35 | +----------------------------------------+ |
36 | +text------------------------------------+ |
37 | | | |
38 | | | |
39 | | Main text box | |
40 | | | |
41 | | | |
42 | +----------------------------------------+ |
43 +--------------------------------------------+
44*/
45
46using System;
47
48public class TextEditor
49{
50 private efl.ui.IWin win; // The main window
51 private efl.ui.IText editorTextBox; // The main text entry
52 private efl.ui.IButton toolbarButtonNew; // The "New" button in the toolbar
53 private efl.ui.IButton toolbarButtonSave; // The "Save" button in the toolbar
54 private efl.ui.IButton toolbarButtonLoad; // The "Load" button in the toolbar
55
56 private bool edited = false; // Document was edited since last save
57
58 // File to load and save is fixed since we do not use a file selection dialog
59 private readonly string filename = System.IO.Path.Combine(System.IO.Path.GetTempPath(),
60 "texteditor_example.txt");
61
62 // Quits the application
63 private void GUIQuitCb(object sender, EventArgs ea)
64 {
65 efl.ui.Config.Exit();
66 }
67
68 // Enables or disables buttons on the toolbar as required
69 private void GUIToolbarRefresh()
70 {
71 // "New" is enabled if there is text in the text box
72 toolbarButtonNew.SetDisabled(string.IsNullOrEmpty(editorTextBox.GetText()));
73 // "Save" is enabled if the text has been modified since last save or load
74 toolbarButtonSave.SetDisabled(!edited);
75 // "Load" is enabled if there is a file to load
76 toolbarButtonLoad.SetDisabled(!System.IO.File.Exists(filename));
77 }
78
79 // Called when the text in the editor has changed
80 private void EditorChangedCb(object sender, EventArgs ea)
81 {
82 edited = true;
83 GUIToolbarRefresh();
84 }
85
86 // Shows a modal message popup with an "OK" button
87 private void ShowMessage(string message)
88 {
89 new efl.ui.Popup_Alert_Text (win, (efl.ui.IPopup_Alert_Text epopup) => {
90 epopup.SetText(message);
91 epopup.SetExpandable(new eina.Size2D(200,200));
92 epopup.SetButton(efl.ui.Popup_Alert_Button.Positive, "OK", null);
93 epopup.ButtonClickedEvt +=
94 (object sender, efl.ui.Popup_Alert.ButtonClickedEvt_Args ea) => {
95 // Dismiss popup when the button is clicked
96 ((efl.ui.IPopup_Alert_Text)sender).SetParent(null);
97 };
98 });
99 }
100
101 // Adds a button to the toolbar, with the given text, icon and click event handler
102 private efl.ui.IButton GUIToolbarButtonAdd(efl.ui.IBox toolbar, string name,
103 string iconName, EventHandler func)
104 {
105 return new efl.ui.Button(toolbar, (efl.ui.IButton ebutton) => {
106 ebutton.SetText(name);
107 ebutton.ClickedEvt += func;
108 ebutton.SetHintWeight(0, 1);
109 toolbar.DoPack(ebutton);
110
111 // Set the content of the button
112 efl.Content.static_cast(ebutton.GetPart("efl.content")).SetContent(
113 // Which is an image
114 new efl.ui.Image(toolbar, (efl.ui.IImage eimage) => {
115 eimage.SetIcon(iconName);
116 })
117 );
118 });
119 }
120
121 // Creates a new toolbar, with all its buttons
122 private void GUIToolbarSetup(efl.ui.IBox parent)
123 {
124 // Create an horizontal box container for the buttons
125 efl.ui.IBox bar = new efl.ui.Box(parent, (efl.ui.IBox ebox) => {
126 // 0 vertical weight means that the toolbar will have the minimum height
127 // to accommodate all its buttons and not a pixel more. The rest of the
128 // space will be given to the other object in the parent container.
129 ebox.SetHintWeight(1, 0);
130 ebox.SetDirection(efl.ui.Dir.Horizontal);
131 parent.DoPack(ebox);
132 });
133
134 // "New" button
135 toolbarButtonNew = GUIToolbarButtonAdd(bar, "New", "document-new",
136 (object sender, EventArgs ea) => {
137 // When this button is clicked, remove content and refresh toolbar
138 editorTextBox.SetText("");
139 GUIToolbarRefresh();
140 });
141
142 // "Save" button
143 toolbarButtonSave = GUIToolbarButtonAdd(bar, "Save", "document-save",
144 (object sender, EventArgs ea) => {
145 // When this button is clicked, try to save content and refresh toolbar
146 try {
147 System.IO.File.WriteAllText(filename, editorTextBox.GetText());
148 edited = false;
149 GUIToolbarRefresh();
150 ShowMessage("Saved!");
151 } catch (Exception e) {
152 // If something fails, show the error message
153 ShowMessage(e.Message);
154 }
155 });
156
157 // "Load" button
158 toolbarButtonLoad = GUIToolbarButtonAdd(bar, "Load", "document-open",
159 (object sender, EventArgs ea) => {
160 // When this button is clicked, try to load content and refresh toolbar
161 try {
162 editorTextBox.SetText(System.IO.File.ReadAllText(filename));
163 edited = false;
164 GUIToolbarRefresh();
165 ShowMessage("Loaded!");
166 } catch (Exception e) {
167 // If something fails, show the error message
168 ShowMessage(e.Message);
169 }
170 });
171
172 // Spacer box to use all available space not required by buttons
173 // (It has a default horizontal weight of 1, whereas all buttons have
174 // an horizontal weight of 0).
175 // As a result, it pushes the "Quit" button to the right margin and
176 // the rest to the left.
177 efl.ui.IBox box = new efl.ui.Box(parent);
178 bar.DoPack(box);
179
180 // "Quit" button
181 GUIToolbarButtonAdd(bar, "Quit", "application-exit", GUIQuitCb);
182 }
183
184 // Builds the user interface for the text editor
185 public TextEditor()
186 {
187 // Create a window and initialize it
188 win = new efl.ui.Win(efl.App.GetLoopMain(), (efl.ui.IWin ewin) => {
189 ewin.SetText("Text Editor");
190 ewin.SetAutohide(true);
191 ewin.HideEvt += GUIQuitCb;
192 });
193
194 // Create a vertical box container
195 efl.ui.IBox box = new efl.ui.Box(win);
196 win.SetContent(box);
197
198 // Create the toolbar and add it to the box
199 GUIToolbarSetup(box);
200
201 // Create the main text entry
202 editorTextBox = new efl.ui.Text(box, (efl.ui.IText etext) => {
203 etext.SetFont("Mono", 14);
204 etext.SetMultiline(true);
205 etext.SetEditable(true);
206 etext.SetScrollable(true);
207 etext.SetHintMin(new eina.Size2D(360, 240));
208 etext.ChangedEvt += EditorChangedCb;
209 etext.ChangedUserEvt += EditorChangedCb;
210 box.DoPack(etext);
211 });
212
213 // Initial refresh of the toolbar buttons
214 GUIToolbarRefresh();
215 }
216
217 // This method won't return until the application quits
218 public void Run()
219 {
220 // Start the EFL main loop
221 efl.ui.Config.Run();
222 }
223}
224
225public class Example
226{
227#if WIN32
228 [STAThreadAttribute()]
229#endif
230 public static void Main()
231 {
232 // Initialize EFL and all UI components
233 efl.All.Init(efl.Components.Ui);
234
235 var textEditor = new TextEditor();
236 textEditor.Run();
237
238 // Shutdown EFL
239 efl.All.Shutdown();
240 }
241}
242```
243
244When you run the application, it should look something like this:
245
246![Text Editor application](/_media/playground/texteditor-app/image01.png)
247
248## Walkthrough ##
249
250This tutorial builds on top of the previous one, [Graphical "Hello World" in C#](hello-world-gui-cs.md), so you should be familiar with the basics of creating an EFL application and adding widgets to it. The following sections explain how to build this particular user interface and how to react to the different events.
251
252You can use the navigation menu on the side to jump to the section you are most interested in.
253
254### The User Interface Structure ###
255
256A rough sketch of the application's user interface structure is depicted in the code's opening comment:
257
258```
259 +vbox----------------------------------------+
260 | +hbox------------------------------------+ |
261 | | +btn-+ +btn-+ +btn-+ +box-----+ +btn-+ | |
262 | | |NEW | |SAVE| |LOAD| | spacer | |QUIT| | |
263 | | +----+ +----+ +----+ +--------+ +----+ | |
264 | +----------------------------------------+ |
265 | +text------------------------------------+ |
266 | | | |
267 | | | |
268 | | Main text box | |
269 | | | |
270 | | | |
271 | +----------------------------------------+ |
272 +--------------------------------------------+
273```
274
275The main widget is a vertical container box (`vbox`) with two children: the toolbar and the main text entry.
276
277The toolbar is an horizontal container box (`hbox`) full of buttons. If you are looking at the final image above, bear in mind that the toolbar itself is invisible (as are all box containers); only the buttons are visible.
278
279In between the toolbar buttons there's an empty container box (also invisible) to act as a separator: it uses up all available space, effectively pushing buttons on either side to the far ends of the container. See [The Space Box](#The_Spacer_Box) section for details.
280
281Each time the content of the text box changes, the toolbar is refreshed to enable or disable some of the buttons. For example, the Save button is only enabled (clickable) if there is text to save (i.e. the text box is not empty). See the [Refreshing the Toolbar](#Refreshing_the_Toolbar) section for details.
282
283To keep this example simple a file selection dialog has not been used. Therefore, file saving and loading actions always act on the same file, located in the system's temporary folder. For example: `/tmp/texteditor_example.txt`.
284
285### The Main Method ###
286
287```csharp
288 public static void Main()
289 {
290 // Initialize EFL and all UI components
291 efl.All.Init(efl.Components.Ui);
292
293 var textEditor = new TextEditor();
294 textEditor.Run();
295
296 // Shutdown EFL
297 efl.All.Shutdown();
298 }
299```
300
301The Main method simply initializes EFL, instantiates a `TextEditor` object and relinquishes control to it. The bulk of the work is done by the `TextEditor` class defined in the example. When `texteditor.Run()` returns EFL is shut down and the application finishes.
302
303`texteditor.Run()` contains the call to `efl.ui.Config.Run()` which will block until EFL is instructed to quit using `efl.ui.Config.Exit()`, as seen in the [Graphical "Hello World" in C#](hello-world-gui-cs.md) tutorial.
304
305This structure is not strictly necessary for such a brief example, but it mimics what a more complex application would normally do.
306
307### Building the Text Editor ###
308
309The constructor for the `TextEditor` class builds the User Interface sketched above. The process is split in this tutorial in different sections for clarity.
310
311First, a window is created as explained in previous tutorials:
312
313```csharp
314 public TextEditor()
315 {
316 // Create a window and initialize it
317 win = new efl.ui.Win(efl.App.GetLoopMain(), (efl.ui.IWin ewin) => {
318 ewin.SetText("Text Editor");
319 ewin.SetAutohide(true);
320 ewin.HideEvt += GUIQuitCb;
321 });
322```
323
324Then the vertical container box is created and set as the content for the window:
325
326
327```csharp
328 // Create a vertical box container
329 efl.ui.IBox box = new efl.ui.Box(win);
330 win.SetContent(box);
331```
332
333The toolbar containing all the buttons is created and added to the box (explained in the next section):
334
335```csharp
336 // Create the toolbar and add it to the box
337 GUIToolbarSetup(box);
338```
339
340Finally, the text entry area is added. This is the main feature of the text editor, so it is worth describing in more detail:
341
342```csharp
343 // Create the main text entry
344 editorTextBox = new efl.ui.Text(box, (efl.ui.IText etext) => {
345 etext.SetFont("Mono", 14);
346 etext.SetMultiline(true);
347 etext.SetEditable(true);
348 etext.SetScrollable(true);
349 etext.SetHintMin(new eina.Size2D(360, 240));
350 etext.ChangedEvt += EditorChangedCb;
351 etext.ChangedUserEvt += EditorChangedCb;
352 box.DoPack(etext);
353 });
354```
355
356* `efl.ui.Text.SetFont()` sets the font family and size for the text area. The font string has to follow `fontconfig`'s convention for naming fonts, as it is the underlying library used to query system fonts (see the output of the `fc-list` command). Alternatively, you can use the full path to a font file.
357
358* `efl.ui.Text.SetMultiline()` selects whether the text area will support more than one line of text (`true` is customary for a text editor).
359
360* `efl.ui.Text.SetEditable()` sets whether the text can be modified by the user or it is only meant for output. If set to `true` all editing facilities (like keyboard input handling and caret positioning) will be enabled.
361
362* `efl.ui.SetScrollable()` selects whether the text area should automatically use scroll bars to accommodate text bigger than the window. If enabled, the caret will always be kept visible when typing. If disabled, when typing off the borders you won't see the new characters.
363
364* `efl.ui.SetHintMin()` sets a minimum size for the text area, as seen in previous tutorials.
365
366Furthermore, handlers are installed for two events: `ChangedUserEvt` is triggered when the text content changes in response to user actions (for example, a key has been pressed). `ChangedEvt` is triggered when the text content changes for any other reason.
367
368This example uses the same handler for both events (`EditorChangedCb()`), which only records the fact that the text has been edited in an internal variable and refreshes the toolbar.
369
370Note how only two objects have been added to the vertical box: the toolbar and the text area. If no further configuration is done these two objects will share the vertical space in the box at 50% each, creating a very tall toolbar. The configuration of the toolbar prevents this, as shown in the next section.
371
372The last step when building the User Interface is to refresh the toolbar, so the buttons reflect their initial states (explained in the [Refreshing the Toolbar](#Refreshing_the_Toolbar) section):
373
374```csharp
375 // Initial refresh of the toolbar buttons
376 GUIToolbarRefresh();
377 }
378```
379
380The user interface has now been built and is ready to be shown once the EFL main loop is executed (by calling `efl.ui.Config.Run()` in `TextEditor.Run()`).
381
382The following sections describe in more detail each part of the user interface.
383
384### Building the Toolbar ###
385
386This is done in the `GUIToolbarSetup()` method:
387
388```csharp
389 private void GUIToolbarSetup(efl.ui.IBox parent)
390 {
391 // Create an horizontal box container for the buttons
392 efl.ui.IBox bar = new efl.ui.Box(parent, (efl.ui.IBox ebox) => {
393 ebox.SetHintWeight(1, 0);
394 ebox.SetDirection(efl.ui.Dir.Horizontal);
395 parent.DoPack(ebox);
396 });
397
398 [... Buttons are added to the toolbar ...]
399 }
400```
401
402As it can be seen, the toolbar is just a regular horizontal box container. Please note the `SetHintWeight(1, 0)` line: Giving a widget a 0 weight means that its parent container will allocate only the minimum room to fit it. The rest of the space will be used by the other children of the container, in this case, the text area.
403
404The rest of this method adds the different buttons by using the helper function `GUIToolbarButtonAdd()` described below.
405
406### Adding a Button to the Toolbar ###
407
408Let's examine this very compact method to create and customize a button, while adding it to a parent container:
409
410```csharp
411 private efl.ui.IButton GUIToolbarButtonAdd(efl.ui.IBox toolbar, string name,
412 string iconName, EventHandler func)
413 {
414 return new efl.ui.Button(toolbar, (efl.ui.IButton ebutton) => {
415 ebutton.SetText(name);
416 ebutton.ClickedEvt += func;
417 ebutton.SetHintWeight(0, 1);
418 toolbar.DoPack(ebutton);
419
420 // Set the content of the button
421 efl.Content.static_cast(ebutton.GetPart("efl.content")).SetContent(
422 // Which is an image
423 new efl.ui.Image(toolbar, (efl.ui.IImage eimage) => {
424 eimage.SetIcon(iconName);
425 })
426 );
427 });
428 }
429```
430
431The function contains a single statement, which instantiates an `efl.ui.Button` (as a child of the toolbar) and returns it.
432
433The rest of the work is done in the initialization method:
434
435* The button's label is set with `SetText()`
436
437* The method to call when the button is clicked is set by installing an event handler for the `ClickedEvt` event.
438
439* The button is given an horizontal weight of 0, so its parent (the toolbar) will allocate for it the minimum space.
440
441* The button is added to the toolbar with `DoPack()`.
442
443* Finally, the requested icon is added to the button. Like many other EFL widgets, Buttons are very flexible when it comes to customizing some of its parts. In fact, this example uses an image but any other widget (or combination of widgets) could be put inside the Button.
444
445 The price for this flexibility is a bit more involved setup of the widgets. Please read the [Parts tutorial](parts.md) to learn more about this topic (*coming soon*).
446
447### The "New" Button ###
448
449This is how the "New" button is created in the `GUIToolbarSetup()` method:
450
451```csharp
452 // "New" button
453 toolbarButtonNew = GUIToolbarButtonAdd(bar, "New", "document-new",
454 (object sender, EventArgs ea) => {
455 // When this button is clicked, remove content and refresh toolbar
456 editorTextBox.SetText("");
457 GUIToolbarRefresh();
458 });
459```
460
461Its label is `"New"` and it uses the icon named `"document-new"`. When clicked, it will simply empty the `editorTextBox` and refresh the toolbar.
462
463### The "Save" Button ###
464
465The "Save" button instantiation is similar to that of the "New" button:
466
467```csharp
468 // "Save" button
469 toolbarButtonSave = GUIToolbarButtonAdd(bar, "Save", "document-save",
470 (object sender, EventArgs ea) => {
471 // When this button is clicked, try to save content and refresh toolbar
472 try {
473 System.IO.File.WriteAllText(filename, editorTextBox.GetText());
474 edited = false;
475 GUIToolbarRefresh();
476 ShowMessage("Saved!");
477 } catch (Exception e) {
478 // If something fails, show the error message
479 ShowMessage(e.Message);
480 }
481 });
482```
483
484The clicked event handler does a bit more work, though:
485
486* It performs file operations, so the body of the handler is enclosed in a `try {} catch` clause. In case an exception is thrown its error message is shown using a popup (see the [Showing a Popup Message](#Showing_a_Popup_Message) section).
487
488* The whole content of the `editorTextBox` is retrieved with `GetText()` and written to a file:
489 ```csharp
490 System.IO.File.WriteAllText(filename, editorTextBox.GetText());
491 ```
492 `filename` is a read-only variable in this example, for simplicity.
493
494* Once the file has been written, the `edited` flag is set to `false` to indicate that there are no pending changes to commit to disk. This has the effect to disable the "Save" button as shown in the [Refreshing the Toolbar](#Refreshing_the_Toolbar) section. The toolbar is manually refreshed by calling `GUIToolbarRefresh()` so this change is visible immediately.
495
496* A popup is shown to indicate that the operation succeeded (see the [Showing a Popup Message](#Showing_a_Popup_Message) section).
497
498### The "Load" Button ###
499
500The "Load" button is very similar to the "Save" one:
501
502```csharp
503 // "Load" button
504 toolbarButtonLoad = GUIToolbarButtonAdd(bar, "Load", "document-open",
505 (object sender, EventArgs ea) => {
506 // When this button is clicked, try to load content and refresh toolbar
507 try {
508 editorTextBox.SetText(System.IO.File.ReadAllText(filename));
509 edited = false;
510 GUIToolbarRefresh();
511 ShowMessage("Loaded!");
512 } catch (Exception e) {
513 // If something fails, show the error message
514 ShowMessage(e.Message);
515 }
516 });
517```
518
519The operation is the opposite to that of the "Save" button: the text is read from the file and set as the content of the `editorTextBox` with `SetText()`.
520
521The `edited` flag is also updated, the toolbar refreshed and a popup message is shown.
522
523### The Spacer Box ###
524
525It is more or less traditional that the "Quit" button is located on the right-hand side of the window (think of the `X` button on the top-right corner of every window on most desktops) whereas the rest of the buttons (the "Command" buttons) are stacked on the left side.
526
527A simple mechanism to achieve this in EFL is to insert a **spacer** widget between the command and the quit buttons which uses up all available space, pushing all buttons to either side of the spacer as far as they can go.
528
529Any widget can act as an spacer, the only thing required of it is that it uses up space. To reduce complexity and unwanted side effects, an empty container widget is ideal for the job. For example, a `Box`:
530
531```csharp
532 efl.ui.IBox box = new efl.ui.Box(parent);
533 bar.DoPack(box);
534```
535
536To ensure that all available space is given to the spacer and not distributed evenly among all sibling buttons, the spacer is given an horizontal weight of 1 (it's the default value, so there is no need to use `SetHintWeight()`) whereas the rest of the buttons have an horizontal weight of 0 (as seen in the [Adding a Button to the Toolbar](#Adding_a_Button_to_the_Toolbar) section).
537
538How a container widget distributes available space among its children is a complex topic explained in more detail in a different tutorial (*coming soon*).
539
540### Showing a Popup Message ###
541
542Applications typically convey important informational or error messages through "modal" popup dialogs, which block the application until acknowledged by the user. This would be an example:
543
544![Popup message](/_media/playground/texteditor-app/image02.png)
545
546To achieve this EFL uses the `efl.ui.Popup` class and its derivatives, for instance, the `efl.ui.Popup_Alert_Text` used in this tutorial. This class implements a popup which contains text and optional configurable buttons.
547
548This is how it's used in this tutorial, in the `ShowMessage()` method:
549
550```csharp
551 // Shows a modal message popup with an "OK" button
552 private void ShowMessage(string message)
553 {
554 new efl.ui.Popup_Alert_Text (win, (efl.ui.IPopup_Alert_Text epopup) => {
555 epopup.SetText(message);
556 epopup.SetExpandable(new eina.Size2D(200,200));
557 epopup.SetButton(efl.ui.Popup_Alert_Button.Positive, "OK", null);
558 epopup.ButtonClickedEvt +=
559 (object sender, efl.ui.Popup_Alert.ButtonClickedEvt_Args ea) => {
560 // Dismiss popup when the button is clicked
561 ((efl.ui.IPopup_Alert_Text)sender).SetParent(null);
562 };
563 });
564 }
565```
566
567* The main text is set with `SetText()`.
568
569* By default the popup has the width required to display all buttons and a fixed height. If the text does not fit in that area a vertical scroll bar is shown, which can be inconvenient when there's room enough in the window to make a bigger popup dialog. `SetExpandable()` allows extending the popup size beyond its default size if required, so that no scroll bars are needed when the text is large.
570
571* `SetButton()` customizes the optional buttons of the popup. Alert popups can have 3 buttons: one for Positive answers, one for Negative answers and a user-defined one. `SetButton()` changes the text and icon of each of these buttons, which are invisible by default until `SetButton()` is called.
572
573* When any of the buttons is pressed, the `ButtonClickedEvt` event is emitted. The event argument specifies which button has been pressed, in case there's more than one button enabled (in this example there is no need to check the event argument because there is only one button).
574
575* In this tutorial pressing the "OK" button dismisses the popup, which is done by removing the widget in the `ButtonClickedEvt` event handler. To delete a widget, the reference hold by its parent must be removed using `SetParent(null)`.
576
577### Refreshing the Toolbar ###
578
579Command buttons do not need to be always available. When a certain action cannot be performed its button can be disabled with `SetDisabled()`, graying it out and making it un-clickable. The `GUIToolbarRefresh()` enables or disables each command button based on the internal state of the editor:
580
581```csharp
582 private void GUIToolbarRefresh()
583 {
584 // "New" is enabled if there is text in the text box
585 toolbarButtonNew.SetDisabled(string.IsNullOrEmpty(editorTextBox.GetText()));
586 // "Save" is enabled if the text has been modified since last save or load
587 toolbarButtonSave.SetDisabled(!edited);
588 // "Load" is enabled if there is a file to load
589 toolbarButtonLoad.SetDisabled(!System.IO.File.Exists(filename));
590 }
591```
592
593* The "New" button clears the content of the editor, therefore, it only makes sense when the editor is not already empty.
594
595* The "Save" button is only enabled when the content of the editor has been changed. The internal variable `edited` is set to `true` in the `EditorChangedCb` event handler and to `false` whenever the file is saved or loaded.
596
597* The "Load" button is only available when the file to load, which is always the same in this example, exists.
598
599`GUIToolbarRefresh()` is called whenever the internal state of the editor changes to ensure that the user interface reflects it.
600
601## Summary ##
602
603At the end of this tutorial you have learned:
604
605* How to create **complex user interfaces** by nesting widget containers.
606* How to create a **toolbar** with buttons located in specific positions.
607* How to **set and retrieve** the content of a text entry, for example, to save and load it from a file.
608* How to use **message popups**.
609
610## Further Reading ##
611
612[Setting up a C# Development Environment](/develop/setup/csharp/)
613: Read this before trying to develop with the EFL and C#
614
615[Graphical "Hello World" in C#](hello-world-gui-cs.md)
616: Explains the basics of creating an EFL application and its user interface.
617
618[Tutorial Code Examples](https://git.enlightenment.org/tools/examples.git/tree/apps/csharp/texteditor)
619: C# Source code for this tutorial.