Elementary

Last Author
beber
Last Updated
235 Days Ago
Tokens
Subscribers
sandoval

Elementary is a widget toolkit and EFL wrapper and convenience library to make it easy to build applications and tools with UI's with less code, as well as being able to still dig down layers. Let's begin with a hello world example. This assumes you have managed to install Elementary by now and are wondering where to get started with using it.

To compile the following example, just copy and paste it into your favorite text editor and save it as 'hello.c'. Then run the following command:

gcc hello.c -o hello `pkg-config elementary --cflags --libs`

Now this may seem a little long, but it is fully commented and does more than just say "Hello world". It can interact with the user allowing them to close the window (and respond by quitting) or by pressing OK (which results in the same action). This style of programming is called event-driven programming. The application doesn't sit and loop continually itself "doing things" like the first simple C programs you may have learned. It only does some setup and then lets the toolkit (Elementary) do the "looping" for it. IT is important to get this big difference in programmming model as most toolkits do exactly this. It is more efficient to do it this way and saves the programmer lots of time in the long run when done this way. In this case Elementary will sleep and wait on input and other timing events, and based on that input, it will call the functions that it is told to - thus the need for adding the callbacks.

Callbacks are a construct where a function is registered with some system, so that it can be called "on demand" by that system. In this case when the events indicate OK has been clicked OR when the window close button is pressed.

#include <Elementary.h>

static void
on_done(void *data, Evas_Object *obj, void *event_info)
{
   /* quit the mainloop (elm_run) */
   elm_exit();
}

EAPI int
elm_main(int argc, char **argv)
{
   Evas_Object *win, *bg, *box, *lab, *btn;

   /* new window - do the usual and give it a name, title and delete handler */
   win = elm_win_add(NULL, "hello", ELM_WIN_BASIC);
   elm_win_title_set(win, "Hello");
   /* when the user clicks "close" on a window there is a request to delete */
   evas_object_smart_callback_add(win, "delete,request", on_done, NULL);

   /* add a standard bg */
   bg = elm_bg_add(win);
   /* not allow bg to expand. let's limit dialog size to contents */
   evas_object_size_hint_weight_set(bg, 0.0, 0.0);
   /* add object as a resize object for the window (controls window minimum
    * size as well as gets resized if window is resized) */
   elm_win_resize_object_add(win, bg);
   evas_object_show(bg);

   /* add a box object - default is vertical. a box holds children in a row,
    * either horizontally or vertically. nothing more. */
   box = elm_box_add(win);
   /* make the box hotizontal */
   elm_box_horizontal_set(box, EINA_TRUE);
   /* not not allow box to expand. let's limit dialog size to contents */
   evas_object_size_hint_weight_set(box, 0.0, 0.0);
   /* add object as a resize object for the window (controls window minimum
    * size as well as gets resized if window is resized) */
   elm_win_resize_object_add(win, box);
   evas_object_show(box);

   /* add a label widget, set the text and put it in the pad frame */
   lab = elm_label_add(win);
   /* set text of the label */
   elm_object_text_set(lab, "Hello out there world!");
   /* do not allow label to expand */
   evas_object_size_hint_weight_set(lab, 0.0, 0.0);
   /* pack the label at the end of the box */
   elm_box_pack_end(box, lab);
   evas_object_show(lab);

   /* add an ok button */
   btn = elm_button_add(win);
   /* se label for button to "OK" */
   elm_object_text_set(btn, "OK");
   /* do not allow button to expand */
   evas_object_size_hint_weight_set(btn, 0.0, 0.0);
   /* pack the button at the end of the box */
   elm_box_pack_end(box, btn);
   evas_object_show(btn);
   /* call on_don when button is clicked */
   evas_object_smart_callback_add(btn, "clicked", on_done, NULL);

   /* now we are done, show the window */
   evas_object_show(win);

   /* run the mainloop and process events and callbacks */
   elm_run();
   return 0;
}
ELM_MAIN()

Elementary

Elementary is a widget set. It is a new-style of widget set much more canvas object based than anything else. Why not ETK? Why not EWL? Well they both tend to veer away from the core of Evas, Ecore and Edje a lot to build their own worlds. Also I wanted something focused on embedded devices - specifically small touchscreens. Unlike GTK+ and Qt, 75% of the "widget set" is already embodied in a common core - Ecore, Edje, Evas etc. So this fine-grained library splitting means all of this is shared, just a new widget "personality" is on top. And that is... Elementary, my dear watson. Elementary.

Candy

The first thing people want to know is, what does it look like. Yes Yes. Let's get on with it. Here's the Candy.
























Those are just some of the widgets in the test application. There is more to this, with widgets animating in some cases as well as having multiple styles. Here are a few real-life application screenshots:



Elementary comes, out of the box, able to support ARGB windows - as well as shaped windows. That means for fancy windowing and interface ideas with compositors are supported out-of-the-box. note the difference with the same logo icon here. The first is shaped, giving it a "jagged" monochromatic shape. The second is smooth with an alpha channel. Same identical application code. Elementary handles detecting if a compositor exists and creating the window appropriately, or using shaped windows if not:


This is why the window background is actually a separate widget. You may not want one and thus - don't put one there. You may want multiple backgrounds and layer them, or use the layout widget and an edje design that scales nicely, or anything else. Widgets are just objects you can stack (raise/lower) etc. within the canvas that is in the window widget.

Elementary also scales - and can do so given a scaling factor. this is set on the root window or overridden with an environment variable. This can be manually set by a user or calculated from DPI, but the main point is that it scales given a value OTHER than directly looking at DPI (which is a bad way to scale - a 52" HDTV has a relatively low DPI - so you don't go scale everything down to be tiny, because you watch it from the other side of the room. Some small high-DPI screen on phones you hold close to your face - not the distance a normal computer screen would be at, so you generally don't scale up as much. So everyone runs around modifying the DPI just to have universal scaling factors - which is really wrong). That is why you want another scaling factor to use. Here are some examples of the same application - same UI, just with different scaling factors:



Note how it scales - but only the "important things" scale. Those that should. Things that should stay "pixel-perfect" like the hilights, shading, textures and decorations etc. - stay just as they should. Pixel-perfect. It'd be nice if every widget set were to respect such root properties and/or environment variables instead of looking at DPI. Then just a simple tool to set the variable or root property (based on DPI or on anything else the user likes) would do the same job - but stop people from messing about with DPI.

Now the above only vary scaling factor. Even if you scale up for reading - you want interaction to have a different scaling factor. how accurate is the mouse, stylus.. or finger? You want to force "hit areas" to be big for a fuzzy finger. The below show the UI above at scale 1.0 and 2.0 but with varying larger finger sizes - allowing for easier hitting on touchscreens with a finger:




List me up!

And now that candy time is over, it's time to begin some more in-depth information. Elementary is small. it's API currently is not incredibly extensive, but it's usable. It's written in C and it's API is C. It's clean and consistent and should be easy to pick up just by reading the Elementary.h header installed and some example code. It is primarily developed for X11, but also runs in the Framebuffer (limit of 1 window of course) and has had some work done for a WinCE port. Under X11 you can use Software, XRender or OpenGL to draw. Elementary also supports scalable interfaces (scale to a scaling factor that can be calculated from DPI for example) as well as alpha channels and compositing for windows. Of course it's look is entirely defined in a theme by Edje, so changing the look is possible, and very powerful and flexible.

So you have an idea of what things Elementary does on a broader sense, here is a quick list of the widgets and what they do:

WidgetDescription
WinThis is the basic window widget. It handles all setup and the glue of the windowing system (if any) and the canvas abstraction. It also provides sub-windows (Inwin's) inside the window intended for quick popups of information that may normally use a separate window, but you don't want to here.
BgBackground handling. As elementary does support transparent (shaped or translucent if you have a compositor) windows, the window background is actually a separate widget you can put in a window if you want a background. This gives you a standard theme defined background OR allows you to also put in a custom image for a window background too.
IconA standard icon that may be provided by the theme (delete, edit, arrows etc.) or a custom file (PNG, JPG, EDJE etc.) used for an icon. The Icon may scale or not and of course... support alpha channels.
BoxA box packing widget. Pack into a horizontal or vertical box.
ButtonA push-button. Press it and run some function. It can contain a simple label and icon object.
ScrollerThis allows the contents to be scrolled around. By default it has "finger scrolling" working with momentum, given that the focus of Elementary is gadgets.
LabelDisplay text, with simple html-like markup. The theme of course can invent new markup tags and style them any way it likes.
ToggleLooks very iPhone-like - a toggle button you have to drag left/right like a real sliding toggle-switch to toggle between 2 states.
FrameHolds some content and has a title. Looks like a "frame", but supports styles so multiple frames are available.
Tablearranges widgets in a table where items can also span multiple columns or rows - even overlap (and then be raised or lowered accordingly to adjust stacking if they do overlap).
ClockFor displaying time and setting it.
LayoutThis takes a standard Edje design file and wraps it very thinly in a widget and handles swallowing widgets into swallow regions in the Edje object, allowing Edje to be used as a design and layout tool.
HoverThis makes a widget hover over another (tracking its location and size wherever it goes). This is a core that is meant for popping up temporary things over something else. It supports multiple styles and is used in other widgets.
EntryFor entering text in a single or multiple lined way, passwords, copy and paste, etc.
NotepadA higher level widget that puts together a scroller, entry and some other code to read and write text files. This automatically will load a text file and put it in the notepad, and save it whenever it is modified back to that file. Makes writign a notepad app a breeze.
AnchorviewFor viewing text that has anchors (links) in the markup within a scrollable region, and for popping up menus (using Hover) when the anchors are clicked (really handy for doing quick displays of test like messages and having names, numbers and URL's hilighted and ready to click to access)
AnchorblockSame as Anchorview, but just not scrollable. Intended for putting inside something more complex
BubbleA lot like the Frame widget, but with a title, note and icon region. Can support multiple directions from which the "speech bubble" comes from. Intended for displaying messages (short ones) to look like some form of speech.
PhotoFor displaying the photo of a person (contact). Simple yet with a very specific purpose.
HoverselMuch like a combobox - when you press the button a list of options slides open to select from - with icons and labels for all. When one is selected it goes away again (or when somewhere else is pressed it is canceled).
ToolbarFor listing a selection of items in a list within a "bar", each item having an icon and label. This is more or less intended for use when selecting different modes - much like a tab widget, but this is just the bar piece.
ListA vertical list of items. Intended for smallish lists (maybe 100 or 200 items at most - the more you have the slower it will get). It can include arbitrary widgets packed to the left/right of a label as well as optionally compress the text of the label to fit the list width. Also includes the scroller for making it scrollable. Supports single and multiple selections.
CarouselNot finished yet. Will be a carousel-like selector.
SliderA slider bar to select a value along a range with 4 directions supported, value indicator, unit indicator, icon and label.
GenlistNot finished yet. Generic list. More work to use that List, but able to hold many more items with much more flexible layouts etc.

Back to the Future

Of course the list above isn't everything. "Where's the file selector?". "What about a range slider?", "Radio buttons?"... Well you have come to the right place. There *IS* a list of things to add/do to Elementary. Look at Elementary.h - it's kept at the bottom (so you know what you're missing... for now). For now I'll list some of these here:

  1. Bugs - high priority
    • scale change for hover doesnt seem to do new size alloc nicely
    • left/right arrow broken with password mode for entry + utf8 chars...
    • bubble doesnt handle child size changes right
    • table doesnt do homogenous properly

2. Incomplete - medium priority

  • entry selection conflicts with finger scroll (make selection start/stop work on signals?)
  • disabled not supported
  • on the fly theme changes - test (should work)
  • need a hold-scroll counter in elm_widget
  • add fullscreen mode on/off for windows
  • hoversel only vertical right now - make horizontal
  • when entries are in a scroller and change size, the scroller shows scrollbars. fix. same for selecting. for 1 line entries in a scroller should only have scroll arrow indicators.

3. More widgets/features - medium priority

  • genlist widget (complex list widget - harder to use than normal simple list, but handles huge lists of items etc.)
  • tree feature for genlist widget (like biglist - but items can expand to sub-items)
  • radio + group handling
  • checkbox (like toggle)
  • pager (for pushing/popping pages and going back and forward and flipping)
  • <edje copy & paste fixed>
  • <evas scale cache>
  • <evas shared cache>
  • carousel selector widget
  • auto-size label/text that adapts text size to its allocated region
  • [ scrollable dropdown combo box ]
  • [ notepad widget ]
  • [ toggle with 2x labelled button for 2 states ]
  • [ poker spinner with numbers + labels ]
  • [ wrapping text button bar ]
  • separator widget (h/v)
  • slide-open "panel" that can hold stuff and optionally scroll
  • calendar widget (select date)
  • range selector (select range of values from X to Y over an interval)
  • "dialogbutton" widget (bigger button for bottom of wins)
  • dialog window widget
  • phone-number widget (hilight country dial prefixes, add flags, photos of contacts that match etc.)
  • imageview widget (for large not iconic images)
  • tiled image + zoom widget (tiled map viewer)
  • dialpad widget - need one with a phone dialpad
  • file selector widget
  • progress bar widget
  • generic "tacho" widget (set min/max labels - and up to 3 intermediate labels etc.)
  • status widget (busy, stalled, running, etc.)
  • full window in window widget (so move/resize of window object does as you'd expect a child window to do within the canvas)

4. Improvements - low priority

  • need another sample theme
  • need a way to set a preferred size of a widget (but not min or max).
  • merge with gurana
  • use evas's new box instead of a box smart
  • use evas's table instead of a table smart
  • use stack for win widget
  • prepend or append theme files from code for an app to allow an app to augment the theme eithe rby forcibly overriding things with its own theme file (so it can ship some custom elements that always override user theme) and so it can also provide a final (even after default) fallback for "extended" styles of its own (but thus allowing themes to overried its extensions if they want)
  • determine prefix of app dynamically and export calls to get prefix info
  • load config from file
  • load config from x property
  • handle finger size property and on-the-fly changes like scaling does
  • somehow a pdf(ps) viewer widget that doesnt make it gpl (lgpl)
  • emotion widget
  • ewebkit widget
  • flash (gnash) widget
  • need url and path entry modes for vkbd
  • return list of toplevel window objects
  • focus should have an object that is layered above all others (definable layer) that moves + resizes (slides about - animated) from one focused widget to the next for focus. also emit focus/unfocus signals too
  • scroller could do with page up/down/left/right buttons and and idea of a page size
  • current sizing tree inefficient
  • need a way to filter entry data for entry (eg for phone numbers)
  • win should emit signals based on vkbd type - if it gets a message and is a vkbd win
  • win needs a way of setting aspect too
  • use the wrong call on the wrong widget and *BOOM* ... crashland

Getting the basics

"So how do I get my grubby hands on this natural wonder you call Elementary" you may ask. Well right now the best way is to get it from SVN. I won't go into details on how to get EFL set up - there's plenty of information elsewhere on that (and if you're reading this I will assume you already have been looking at the rest of EFL. If someone wants they can add links here), so I'll just tell you about elementary:

svn co http://svn.enlightenment.org/svn/e/trunk/elementary

And just like everything else in EFL:

cd elementary
./autogen.sh
make
sudo make install

In the src/bin directory is test.c - this is the source for the elementary_test application - run this to see it working. Read the code if you want deeper insight into how Elementary is being used - there's even some comments.

If you want some more examples try:

svn co http://svn.enlightenment.org/svn/e/trunk/TMP/st/elementary-alarm
svn co http://svn.enlightenment.org/svn/e/trunk/TMP/st/elementary-sms

And same build procedure as above, with similar binary names. Source is in src/bin.


Hello World

Now you have an idea of what can be done, we will get you started on your first application. Of course this will be the ubiquitous "Hello World" application. Don't be scared. It's in C. It's not hard. Just read the code below (without comments to show how compact it can be). This is what the application will look like when it runs:

#include <Elementary.h>

static void
win_del(void *data, Evas_Object *obj, void *event_info)
{
   elm_exit();
}

EAPI int
elm_main(int argc, char **argv)
{
   Evas_Object *win, *bg, *lb;

   win = elm_win_add(NULL, "hello", ELM_WIN_BASIC);
   elm_win_title_set(win, "Hello");
   evas_object_smart_callback_add(win, "delete,request", win_del, NULL);

   bg = elm_bg_add(win);
   evas_object_size_hint_weight_set(bg, 1.0, 1.0);
   elm_win_resize_object_add(win, bg);
   evas_object_show(bg);

   lb = elm_label_add(win);
   elm_object_text_set(lb, "Hello World!");
   evas_object_size_hint_weight_set(lb, 1.0, 1.0);
   elm_win_resize_object_add(win, lb);
   evas_object_show(lb);

   evas_object_show(win);

   elm_run();
   elm_shutdown();
   return 0;
}
ELM_MAIN()

To compile this simple program, the simple way is:

gcc hello.c -o hello `pkg-config elementary --cflags --libs`

Then just run it:

./hello

Now you may say "my god that's a lot of code for hello world!". Yes - it is. We can play games of "smallest hello world app" but Elementary is designed for longer-term usability. So it's a little extra work to get started, but not much. The benefits will pay off in the long run. I will now explain how the code above gets written, piece by piece.

#include <Elementary.h>

EAPI int
elm_main(int argc, char **argv)
{
   elm_run();
   elm_shutdown();
   return 0;
}
ELM_MAIN()

This is how you will begin your Elementary using application. The #include line includes all the relevant EFL headers you need, as well as a host of standard system headers to save you time, effort or the need to know just what to include to do C. This gets you started. Then comes the main function of your application. It MUST be called elm_main. This is a special function and is used by Elementary to start applications faster. You must NOT do ANYTHING in your application before this function is called, except initialize elementary. The ELM_MAIN() macro above is an initial template piece of code provided for you in the Elementary.h header that sets up your application's main() function, initialized Elementary and immediately calls elm_main() with your arguments. The above is pretty much boiler-plate code and how you will start all your applications.

Note that the elm_main function just starts the elm main loop with elm_run. This function will not return as long as the main loop runs (that handles incoming events from keyboards, mice, touchscreens, other applications etc.). Once the main loop stops running, elm_run will return and go onto the next function - in this case elm_shutdown to shut down elementary and anything it wants/needs to clean up. The return after this simply returns the exit/error code for the application. 0 means everything is ok. Any other value means something bad happened - an error of some sort.

#include <Elementary.h>

static void
win_del(void *data, Evas_Object *obj, void *event_info)
{
   elm_exit();
}

EAPI int
elm_main(int argc, char **argv)
{
   Evas_Object *win;

   win = elm_win_add(NULL, "hello", ELM_WIN_BASIC);
   elm_win_title_set(win, "Hello");
   evas_object_smart_callback_add(win, "delete,request", win_del, NULL);

   evas_object_show(win);

   elm_run();
   elm_shutdown();
   return 0;
}
ELM_MAIN()

Now inside elm_main we add some code to create a window, hook a callback to it (a callback is a function that is run for you when a specific event/situation occurs), define the callback and show the window. The window of course will be minuscule in size and have no content. But we need to start somewhere. So we create the window with elm_win_add. The first parameter is NULL as we have no window to associate this one with - it's our first (and only) window. The next parameter is the "name" of the window. This should be unique among all the windows in the application - every type of window should have a unique name of its own. Then the type of the window (ELM_WIN_BASIC - a basic normal window). Then we set the window title with elm_win_title_set to "Hello". Now comes the callback. We attach a callback to the window when the "delete-request" signal happens. This happens when someone presses the close button on your window. We do want the window to close and the application to exit when this happens, don't we? The reason it doesn't automatically end the application is because you may have multiple windows up, and may want to allow the closure of 1 and keep the application running. There is a mechanism in Elementary to have it automatically close (delete) the window for you when this happens. Use elm_win_autodel_set to enable this. Above, when the close button is pressed (or any other mechanism that is used for closing windows is activated), the win_del function will be called. In this function note that we do only 1 thing. We call elm_exit. This exits the main loop, returning control to the elm_main function just after elm_run.

Generally speaking, if you have anything your application wants to or needs to clean up before it exits, put it after elm_run (and before elm_shutdown). Conversely, anything your application needs to do on start - load files, configuration, create windows, parse parameter on the command-line (argv and argc as per standard C conventions) and other things, do it at the start of elm_main

At the end - the window is shown and thus you can see it when the application runs.

#include <Elementary.h>

static void
win_del(void *data, Evas_Object *obj, void *event_info)

{
   elm_exit();
}

EAPI int
elm_main(int argc, char **argv)
{
   Evas_Object *win, *bg;

   win = elm_win_add(NULL, "hello", ELM_WIN_BASIC);
   elm_win_title_set(win, "Hello");
   evas_object_smart_callback_add(win, "delete,request", win_del, NULL);

   bg = elm_bg_add(win);
   evas_object_size_hint_weight_set(bg, 1.0, 1.0);
   elm_win_resize_object_add(win, bg);
   evas_object_show(bg);

   evas_object_show(win);

   elm_run();
   elm_shutdown();
   return 0;
}
ELM_MAIN()

Now we wanted more than a blank window. We want a background. Above we add in the standard background for a window. We create a Bg widget, set the sizing weights (allowing the widget to expand and thus in turn controlling the window it lives in and the window's ability to expand). Then the window is informed that this object is one of the objects that will influence the window's geometry (minimum and maximum size, expansion etc. etc.). The window's constraints are an amalgam of all the objects that are set to be resize objects with elm_win_resize_object_add. Finally the background is shown - so you can see it.

#include <Elementary.h>

static void
win_del(void *data, Evas_Object *obj, void *event_info)
{
   elm_exit();
}

EAPI int
elm_main(int argc, char **argv)
{
   Evas_Object *win, *bg, *lb;

   win = elm_win_add(NULL, "hello", ELM_WIN_BASIC);
   elm_win_title_set(win, "Hello");
   evas_object_smart_callback_add(win, "delete,request", win_del, NULL);

   bg = elm_bg_add(win);
   evas_object_size_hint_weight_set(bg, 1.0, 1.0);
   elm_win_resize_object_add(win, bg);
   evas_object_show(bg);

   lb = elm_label_add(win);
   elm_object_text_set(lb, "Hello World!");
   evas_object_size_hint_weight_set(lb, 1.0, 1.0);
   elm_win_resize_object_add(win, lb);
   evas_object_show(lb);

   evas_object_show(win);

   elm_run();
   elm_shutdown();
   return 0;
}
ELM_MAIN()

And finally, we add a label widget. Since it is created after the background, it will be on top. Objects are by default create on the top - so newer ones will be above older. You can re-stack using evas_object_raise for example, and other related Evas stacking calls, but here we just create in order from bottom to top to keep it simple. So add the label to the window, set the label text (yes labels support minimal html-like markup so you can use newline <br> tags for example), set the label so it will expand (like the background) so it won't limit the window size, set it as another resize object, and show it. Presto. Your first Elementary application. Pun intended.

Configuration

Right now the only configuration Elementary can use is provided by environment variables, the theme(s) specified (they specify the look and feel, animations, fonts and font sizes etc.) and 1 X property on the root window. Let's go into some of these.

Environment Variables
  • ELM_DATA_DIR - This variable tells Elementary what directory system data is stored. This is primarily used for one thing currently, and that is finding out where the system themes are stored. This is $ELM_DATA_DIR/themes. Usual values of ELM_DATA_DIR would be /usr/local/share/elementary or /usr/share/elementary. If this variable is not set, Elementary uses the next variable down... ELM_PREFIX
  • ELM_PREFIX - This variable tells Elementary its install prefix. This would be the same prefix you may use to configure Elementary - example with ./configure --prefix=/my/prefix. Elementary will assume data is in $ELM_PREFIX/share/elementary and otherwise themes are a sub-dir of this as above. If this is not set, Elementary will use the location of libelementary.so, IF the target supports dlopen() and dladdr() calls. So if it finds the library is /usr/local/lib/libelementary.so, then it will assume the PREFIX is 1 directory back from that, i.e. /usr/local and otherwise the rules apply as if you set ELM_PREFIX above. If this isnt' compiled in, for for some reason Elementary doesn't find the data dir correctly, relative to the library, it will fall back to the compiled-in data dir location which is normal for almost all UNIX/Linux apps when they need to find their accompanying data. Why all these variables and funky stuff? Well Elementary is able to be ported to platforms like Windows where software must be able to be located anywhere and so it needs a way to find where it lives. But more importantly - this makes Elementary relocatable after compile-time on Linux and UNIX in general - so if you compiled with --prefix=/opt/whatever - it is possible to rename /opt/whatever to something else, or move it somewhere. As long as the relative directory structure remains intact, it will nicely adapt at runtime. This is actually the kind of support things like Autopackage would like and really should be done everywhere. It takles extra effort by developers, but is worth it.
  • ELM_ENGINE - This sets the Elementary rendering engine used for Evas. Valid values are:
    • x11 - Software X11
    • xr - XRender X11
    • gl - OpenGL X11
    • x11-16 - Software X11 16bpp engine (may have bugs and will be lower quality, but faster)
    • fb - Software Framebuffer (direct)
    • gdi - Windows XP software GDI engine.
    • wince-gdi - Windows CE GDI engine.
  • ELM_SCALE - This sets the scaling factor, overriding the scaling factor found on the root window property. It should be a float of the format 1.0, 2.0, 0.634 etc.
  • ELM_THEME - This sets the theme(s) to be used in order from most preferential, to least, just with the theme name (minus the .edj extension) with a : character delimiting the name. A simple personal theme would be mytheme. If you wish to primarily use your personal theme, and then fall back to another, you can do: mytheme:fallback. This allows as many levels as you like. It is always assumed that the final fallback theme is the default theme. A complex example would be: mytheme:fallback:systemfallback:othersystem. Remember theme names like "mytheme" mean it assumes a mytheme.edj is in $HOME/.elementary/themes OR if not found here first, it is in $ELM_DATA_DIR/themes under the same name. Themes in your users theme dir always take precedence. A Theme name can ALSO be a relative or full path to a file. In this case the fill filename including extensions needs to be given. i.e. /path/to/file.edj:mytheme:fallback:../../relative/path/file.edj:./dir/file.edj. With full or relative paths searching in order still happens. Note that there is a convenience shortcut for the users home directory of ~/. So if a theme element is ~/dir/file.edj then ~/ is expanded to the the value of $HOME (the users home directory).
  • HOME - This is used to find the users home directory - used for finding the users personal collection of theme files in $HOME/.elementary/themes. If it is not set - it will be assumed to be blank.
  • ELM_FONT_HINTING - This affects the font hinting method used when drawing outline text. Generally Bytecode hinting will give the best results as it tries to align fonts to exact pixel boundaries for best clarify. Automatic hinting tries to hint automatically without bytecode and gets it right mostly, but not always. No hinting turns it off and produces "correct" shapes but they may be blurry.
    • none - No hinting
    • auto - Automatic hinting
    • bytecode - Bytecode hinting (the default - if the font back-end supports it).
  • ELM_FONT_PATH - This sets the path (same as the UNIX PATH variable) as a colon-separated list of directories to look in for fonts, with the directories search in order from first to last. An example path may be something like: "/home/user/fonts:/usr/local/share/fonts:/usr/share/fonts"
  • ELM_IMAGE_CACHE - This sets the image cache in Kb. The default is 4096Kb. This is used to speculatively hold ARGB image data when it is not references/used anymore just in case its needed again in future.
  • ELM_FONT_CACHE - This is a number that sets the font cache in Kb. The default is 512Kb. This keeps a certain amount of font glyph rendering data around in case its needed again in future.
  • ELM_FINGER_SIZE - This is sets the size of a "finger" in pixels. This should be tuned for the intended target and use. If Elementary is being used on a desktop, a finger size of 0 would be right. The same would apply for using on a touchscreen with a stylus. If you use it with an actual finger, then the size would be recommended to be bigger. This adjusts the size and hit area of widgets so they are easy to hit with a finger. The size is in pixels and determines the minimum size of a square on the screen that is reliably hittable with a finger. This varies from device to device, but as an example at 285DPI, a finger size of about 80-100 would be right. Finger size would proportionally lower with DPI (ie ELM_SCALE). The default finger size is 40, and is multiplied by ELM_SCALE. So if ELM_SCALE is set to 2.0, the effective finger size is 80. At a scale of 1.0, the finger size will be 40. If explicitly set with the ELM_FINGER_SIZE parameter, scaling does not affect finger size, so consider these 2 separate parameters. One for display size for reading/viewing, and one for input and interaction.
X Properties

Elementary listens (and reads) 1 specific X property - used for determining the scaling factor for Elementary. If this is set, it should be a property named ENLIGHTENMENT_SCALE set on every root window, and be of type CARDINAL. The value is an integer. 1000 means a scaling factor of 1.0 (i.e. no scaling compared to the theme design font/images). 2000 means a scaling factor of 2.0. 500 means 0.5 and so on. So basically it is scaling factor * 1000 rounded to an integer. If this property changes at runtime, Elementary will re-read it and adjust the scaling of the UI automatically. An example xprop output for the property:

ENLIGHTENMENT_SCALE(CARDINAL) = 1000

Simple Dialog

So now we have some more examples to get your started. This example below is a simple command-line dialog. This will display a window with some given text (the first command-line argument to the dialog app) and with an OK and Cancel button. If the user presses OK, the application returns a success exit code (0) and otherwise, a failure code (-1).

gcc dialog.c -o dialog `pkg-config elementary --cflags --libs`

Then just run it:

./dialog 'This is a dialog with text<br>in it. YAY!'

You could use this in a script to add some GUI interactions. Build a small set of such command-line tools for popping up useful requestors from scripts. For example:

#!/bin/sh
# ask if the user really wants to delete the file
if dialog 'Do you really want to delete:<br><br>'"$1"; then
  rm -f $1
fi

And here is the code. This time with comments to give you a helping hand. See - it's not that hard.

#include <Elementary.h>

/* return value of app - keep it here so we can modify it */
static int retval = -1;

/* if someone presses the close button on our window - exit nicely */
static void
win_del(void *data, Evas_Object *obj, void *event_info)
{
   retval = -1;
   /* cleanly exit */
   elm_exit();
}

/* when ok is pressed this runs */
static void
on_ok(void *data, Evas_Object *obj, void *event_info)
{
   retval = 0;
   /* cleanly exit */
   elm_exit();
}

/* when cancel is pressed this runs */
static void
on_cancel(void *data, Evas_Object *obj, void *event_info)
{
   retval = -1;
   /* cleanly exit */
   elm_exit();
}

EAPI int
elm_main(int argc, char **argv)
{
   Evas_Object *win, *bg, *bx, *lb, *bt, *bx2, *fr, *fr0;
   const char *text = "No text provided";

   /* if an argument is there - use it as text */
   if (argc > 1) text = argv[1];

   /* new window - do the usual and give it a name, title and delete handler */
   win = elm_win_add(NULL, "dialog", ELM_WIN_BASIC);
   elm_win_title_set(win, "Dialog");
   evas_object_smart_callback_add(win, "delete,request", win_del, NULL);

   /* add a standard bg */
   bg = elm_bg_add(win);
   /* not not allow bg to expand. let's limit dialog size to contents */
   evas_object_size_hint_weight_set(bg, 0.0, 0.0);
   elm_win_resize_object_add(win, bg);
   evas_object_show(bg);

   /* add a box object - default is vertical. a box holds children in a row,
    * either horizontally or vertically. nothing more. */
   bx = elm_box_add(win);
   /* not not allow box to expand. let's limit dialog size to contents */
   evas_object_size_hint_weight_set(bx, 0.0, 0.0);
   elm_win_resize_object_add(win, bx);
   evas_object_show(bx);

   /* add a padding frame - empty space. large padding. themes can cary
    * the amount of padding, as can scaling, so don't depend on your pixel
    * cound here. add pad frame to box */
   fr = elm_frame_add(win);
   elm_object_style_set(fr, "pad_large");
   /* dont expand */
   evas_object_size_hint_weight_set(fr, 0.0, 0.0);
   /* fill allocated region */
   evas_object_size_hint_align_set(fr, -1.0, -1.0);
   elm_box_pack_end(bx, fr);
   evas_object_show(fr);

   /* add a label widget, set the text and put it in the pad frame */
   lb = elm_label_add(win);
   elm_object_text_set(lb, text);
   elm_frame_content_set(fr, lb);
   evas_object_show(lb);

   /* add another styled frame - outdent_bottom now */
   fr0 = elm_frame_add(win);
   elm_object_style_set(fr0, "outdent_bottom");
   /* dont expand */

   evas_object_size_hint_weight_set(fr0, 0.0, 0.0);
   /* fill allocated region */
   evas_object_size_hint_align_set(fr0, -1.0, -1.0);
   elm_box_pack_end(bx, fr0);
   evas_object_show(fr0);

   /* add medium pad frame inside outdent frame */
   fr = elm_frame_add(win);
   elm_object_style_set(fr, "pad_medium");
   elm_frame_content_set(fr0, fr);
   evas_object_show(fr);

   /* add horizontal box inside pad frame */
   bx2 = elm_box_add(win);
   elm_box_horizontal_set(bx2, 1);
   elm_box_homogenous_set(bx2, 1);
   elm_frame_content_set(fr, bx2);
   evas_object_show(bx2);

   /* add ok button to box - fill and expand */
   bt = elm_button_add(win);
   elm_object_text_set(bt, "OK");
   evas_object_size_hint_weight_set(bt, 1.0, 1.0);
   evas_object_size_hint_align_set(bt, -1.0, -1.0);
   elm_box_pack_end(bx2, bt);
   evas_object_show(bt);
   /* and when clicked - run "on_ok" */
   evas_object_smart_callback_add(bt, "clicked", on_ok, NULL);

   /* add cancel button to box - fill and expand */
   bt = elm_button_add(win);
   elm_object_text_set(bt, "Cancel");
   evas_object_size_hint_weight_set(bt, 1.0, 1.0);
   evas_object_size_hint_align_set(bt, -1.0, -1.0);
   elm_box_pack_end(bx2, bt);
   evas_object_show(bt);
   /* and when clicked - run "on_cancel" */
   evas_object_smart_callback_add(bt, "clicked", on_cancel, NULL);

   /* show the window */
   evas_object_show(win);

   /* get going and draw/respond to the user */
   elm_run();
   /* standard shutdown */
   elm_shutdown();
   /* return/exit code of app signals ok/cancel (0 == ok), (-1 == cancel) */
   return retval;
}
ELM_MAIN()

Simple File Selector

So now we get more complicated. This application will use the list widget too and list directories, even let you browse around (to parent directories and into child directories). Pressing OK with a file or directory selected or double-clicking on a file will "select" it and exit the app, printing the selected file path. This also uses other EFL facilities like ecore_file for making file access a bit simpler.

gcc selector.c -o selector `pkg-config elementary --cflags --libs`

Then just run it:

./selector $HOME

And you can browse files in your home directory. If a file is selected, it will be output to stdout. This can be handy in scripts like this to select a file to delete. Beware! It actually DOES delete files. Here you go.:

#!/bin/sh
FILE=`selector $HOME`
if test -n "$FILE"; then
  rm -rf "$FILE"
fi




#include <Elementary.h>
   
/* callback used for sorting files in pure ascii ordering */
static int
sort_callback(char *f1, char *f2)
{
   return strcmp(f1, f2);
}

/* clear the list of previous entries, then load a dir and fill the list */
static void
load_files(Evas_Object *li, const char *dir)
{
   Eina_List *files;
   const Eina_List *items,*cur;

   /* if no dir is given (NULL) make it "." */
   if (!dir) dir = ".";
   /* if the dir is empty ("") then we must be in "/" */
   else if (dir[0] == 0) dir = "/";
   /* while there are items in the list - delete them */
   while ((items = elm_list_items_get(li)))
     {
        /* also free the path string attached to the data value of the item */
        char *path = (char *)elm_list_item_data_get(items->data);
        if (path) free(path);
        /* delete the item */
        elm_list_item_del(items->data);
     }
   /* list files in the dir */
   files = ecore_file_ls(dir);
   if (files)
     {
        char buf[PATH_MAX];
        char *path, *file;
        Evas_Object *ic;

        /* if we are in root - set the dir name to "" to avoid double // */
        if (!strcmp(dir, "/")) dir = "";
        else
          {
             /* if not in root - we can go back a dir, so add a ".." */
             file = "..";
             snprintf(buf, sizeof(buf), "%s/%s", dir, file);
             path = strdup(buf);
             /* add an icon widget */
             ic = elm_icon_add(li);
             /* use the standard arrow_left icon */
             elm_icon_standard_set(ic, "arrow_left");
             /* allow it to scale down, but not up */
             elm_icon_scale_set(ic, 0, 1);
             /* append item with icon and path we duplicated as data */
             elm_list_item_append(li, file, ic, NULL,  NULL, path);
             /* show the icon */
             evas_object_show(ic);
          }
        /* sort the  file list we got - alphabetically (ascii-wise) */
        files = eina_list_sort(files, 0, EINA_COMPARE_CB(sort_callback));
        /* walk the file list for the dir */
        EINA_LIST_FOREACH(files, cur, file)
          {
             /* if it's a "dot file" - skip it */
             if (file[0] == '.')
               {
                  continue;
               }
             /* generate a full path */
             snprintf(buf, sizeof(buf), "%s/%s", dir, file);
             /* FIXME: This is never free'd */
             path = strdup(buf);
             /* if its a direcotry */
             if (ecore_file_is_dir(buf))
               {
                  /* put in item with an "arrow_right" icon */
                  ic = elm_icon_add(li);
                  elm_icon_standard_set(ic, "arrow_right");
                  elm_icon_scale_set(ic, 0, 1);
                  elm_list_item_append(li, file, ic, NULL,  NULL, path);
                  evas_object_show(ic);
               }
             else
               {
                  /* put in icon with "apps" icon */
                  ic = elm_icon_add(li);
                  elm_icon_standard_set(ic, "apps");
                  elm_icon_scale_set(ic, 0, 1);
                  elm_list_item_append(li, file, ic, NULL,  NULL, path);
                  evas_object_show(ic);
               }
          }
        /* free the file list */
        EINA_LIST_FOREACH(files, cur, file) free(file);
        eina_list_free(files);
     }
   /* tell the list widget we are done messing with it - and go go go! */
   elm_list_go(li);
}

/* if someone presses the close button on our window - exit nicely */
static void
win_del(void *data, Evas_Object *obj, void *event_info)
{
   elm_exit();
}
/* when ok is pressed this runs */
static void
on_ok(void *data, Evas_Object *obj, void *event_info)
{
   Evas_Object *li = data;
   /* get the selected item */
   Elm_List_Item *it = (Elm_List_Item *)elm_list_selected_item_get(li);
   if (it)
     {
        /* if there is a selected item - get the data, i'ts the string
         * we added to the item, print it */
        char *path = (char *)elm_list_item_data_get(it);
        printf("%s\n", path);
     }
   /* cleanly exit */
   elm_exit();
}

/* when cancel is pressed this runs */
static void
on_cancel(void *data, Evas_Object *obj, void *event_info)
{
   /* cleanly exit */
   elm_exit();
}

/* when the user double-clicks on the list, this runs */
static void
on_double_click(void *data, Evas_Object *obj, void *event_info)
{
   Evas_Object *li = data;
   Elm_List_Item *it = (Elm_List_Item *)elm_list_selected_item_get(li);
   if (it)
     {
        char *path = (char *)elm_list_item_data_get(it);
        if (path)
          {
             if (ecore_file_is_dir(path))
               {
                  char *p, *newpath;

                  p = strrchr(path, '/');
                  if ((p) && (!strcmp(p, "/..")))
                    {
                       *p = 0;
                       p = strrchr(path, '/');
                       if (p) *p = 0;
                    }
                  newpath = strdup(path);
                  load_files(li, newpath);
                  free(newpath);
               }
             else
               {
                  elm_exit();
               }
          }
     }
}

/* main elm app core - build the ui core here. something like the below:
 *
 * Inside a window we put:
 * +---------------------------+
 * |Box + Bg under it here     |
 * |    +-----------------+    |
 * |    |Frame + outdent  |    |
 * |    |+---------------+|    |
 * |    ||Frame + pad    ||    |
 * |    ||+-------------+||    |
 * |    |||Label        |||    |
 * |    ||+-------------+||    |
 * |    |+---------------+|    |
 * |    +-----------------+    |
 * |+-------------------------+|
 * ||List here                ||
 * ||                         ||
 * |+-------------------------+|
 * |+-------------------------+|
 * ||Frame + outdent          ||
 * ||+-----------------------+||
 * |||Frame + padding        |||
 * |||+---------------------+|||
 * ||||Box (horiz)          ||||
 * ||||+---------++--------+||||
 * |||||    OK   || Cancel |||||
 * ||||+---------++--------+||||
 * |||+---------------------+|||
 * ||+-----------------------+||
 * |+-------------------------||
 * +---------------------------+
 */
EAPI int
elm_main(int argc, char **argv)
{
   Evas_Object *win, *bg, *bx, *lb, *li, *bt, *bx2, *fr, *fr0;

   /* new window - do the usual and give it a name, title and delete handler */
   win = elm_win_add(NULL, "selector", ELM_WIN_BASIC);
   elm_win_title_set(win, "Selector");
   evas_object_smart_callback_add(win, "delete,request", win_del, NULL);

   /* add a standard bg */
   bg = elm_bg_add(win);
   evas_object_size_hint_weight_set(bg, 1.0, 1.0);
   elm_win_resize_object_add(win, bg);
   evas_object_show(bg);

   /* add a box object - default is vertical. a box holds children in a row,
    * either horizontally or vertically. nothing more. */
   bx = elm_box_add(win);
   evas_object_size_hint_weight_set(bx, 1.0, 1.0);
   elm_win_resize_object_add(win, bx);
   evas_object_show(bx);
   
   /* now let's add a styled frame for the top - outdent_top style */
   fr0 = elm_frame_add(win);
   /* weight here means "dont expand either horizontally OR vertically */
   evas_object_size_hint_weight_set(fr0, 0.0, 0.0);
   /* align here means center horizontally and fill vertically */
   evas_object_size_hint_align_set(fr0, 0.5, -1.0);
   /* add the frame to the end of the box */
   elm_box_pack_end(bx, fr0);
   evas_object_show(fr0);

   /* add a padding frame - empty space. medium padding. themes can vary
    * the amount of padding, as can scaling, so don't depend on your pixel
    * cound here. add pad frame to styled frame above */
   fr = elm_frame_add(win);
   elm_frame_content_set(fr0, fr);
   evas_object_show(fr);

   /* add a label widget, set the text and put it in the pad frame */
   lb = elm_label_add(win);
   elm_object_text_set(lb, "Please select a file.");
   elm_frame_content_set(fr, lb);
   evas_object_show(lb);

   /* add the list widget - it comes in a scroller so pretty easy */
   li = elm_list_add(win);
   /* set the weight to say "expand this entry horizontally AND vertically */
   evas_object_size_hint_weight_set(li, 1.0, 1.0);
   /* set the align to mean "fill the region this widget gets" */
   evas_object_size_hint_align_set(li, -1.0, -1.0);
   elm_box_pack_end(bx, li);
   evas_object_show(li);
   /* if you double-click on the list - on_double_click gets called and
    * pass li as data param */
   evas_object_smart_callback_add(li, "clicked,double", on_double_click, li);
                  
   /* add another styled frame - outdent_bottom now */
   fr0 = elm_frame_add(win);
   /* expand horizontallt but not vertically */
   evas_object_size_hint_weight_set(fr0, 1.0, 0.0);
   /* fill allocated region */
   evas_object_size_hint_align_set(fr0, -1.0, -1.0);
   elm_box_pack_end(bx, fr0);
   evas_object_show(fr0);

   /* add medium pad frame inside outdent frame */
   fr = elm_frame_add(win);
   elm_frame_content_set(fr0, fr);
   evas_object_show(fr);

   /* add horizontal box inside pad frame */
   bx2 = elm_box_add(win);
   elm_box_horizontal_set(bx2, 1);
   elm_box_homogenous_set(bx2, 1);
   elm_frame_content_set(fr, bx2);
   evas_object_show(bx2);
 
   /* add ok button to box - fill and expand */
   bt = elm_button_add(win);
   elm_object_text_set(bt, "OK");
   evas_object_size_hint_weight_set(bt, 1.0, 1.0);
   evas_object_size_hint_align_set(bt, -1.0, -1.0);
   elm_box_pack_end(bx2, bt);
   evas_object_show(bt);
   /* and when clicked - run "on_ok" - pass list as data param */
   evas_object_smart_callback_add(bt, "clicked", on_ok, li);
 
   /* add cancel button to box - fill and expand */
   bt = elm_button_add(win);
   elm_object_text_set(bt, "Cancel");
   evas_object_size_hint_weight_set(bt, 1.0, 1.0);
   evas_object_size_hint_align_set(bt, -1.0, -1.0);
   elm_box_pack_end(bx2, bt);
   evas_object_show(bt);
   /* and when clicked - run "on_cancel" - pass list as data param */
   evas_object_smart_callback_add(bt, "clicked", on_cancel, li);
 
   /* if we have more than 1 argument, then we have parameters as arg 0 is
    * the command itself, do this first arg is the dir */
   if (argc > 1) load_files(li, argv[1]);
   /* no args - use "." as the dir */
   else load_files(li, ".");

   /* force the window size to 300x300 otherwise list is at min size - that
    * is tiny */
   evas_object_resize(win, 300, 300);

   /* show the window */
   evas_object_show(win);

   /* get going and draw/respond to the user */
   elm_run();
   /* standard shutdown */
   elm_shutdown();
   return 0;
}
ELM_MAIN()

Imported from https://trac.enlightenment.org/e/wiki/Elementary
History:
1 raster 2009-01-08 23:49:23
2 raster 2009-01-09 01:28:21
3 raster 2009-01-09 01:57:47
4 raster 2009-01-09 02:52:13
5 raster 2009-01-09 03:03:11
6 raster 2009-01-09 03:39:45
7 raster 2009-01-09 04:04:59
8 raster 2009-01-09 04:22:40
9 raster 2009-01-09 04:48:28
10 raster 2009-01-09 05:21:22
11 raster 2009-01-09 05:59:05
12 raster 2009-01-09 06:41:13
13 raster 2009-01-09 07:02:31
14 raster 2009-01-10 02:07:05
15 raster 2009-01-10 02:26:56
16 raster 2009-01-13 02:01:07
17 raster 2009-01-15 18:21:23
18 raster 2009-01-31 15:23:08
19 raster 2009-02-03 02:37:03
20 tam1138 2009-02-13 15:54:22
21 raster 2009-02-15 23:16:31
22 raster 2009-02-17 17:53:16
23 raster 2009-02-17 23:29:53
24 raster 2009-02-17 23:35:15
25 raster 2009-02-23 00:03:21
26 vtorri 2009-02-23 13:17:56
27 raster 2009-05-22 15:48:21
28 raster 2009-05-22 16:03:53
29 vtorri 2009-06-08 00:08:13
30 vtorri 2009-06-08 00:08:42
31 raster 2009-09-13 18:49:20
32 raster 2009-09-13 18:56:51
33 raster 2009-09-13 19:03:08
34 raster 2009-10-14 01:57:57
35 raster 2009-10-14 02:00:07
36 nash 2009-11-28 16:13:40 Make the elm file example compile with current Elementary
37 T_UNIX 2010-03-16 13:49:41 elm_fram_style_set was removed.
38 T_UNIX 2010-03-16 19:04:24 fix typo and compile warnings
39 enrikem 2010-06-17 04:52:35
40 T_UN1X 2010-07-02 07:14:16
41 rubenbb 2010-07-14 23:21:56 removed spam
42 raster 2011-01-22 22:07:00
43 raster 2011-01-22 22:08:59
44 raster 2011-01-28 21:36:09
45 chilicuil 2011-03-08 17:13:11 include Elementary.h in the 1st example
46 DreamLauncher 2011-05-19 00:09:39
47 T_UNIX 2011-11-20 08:21:22 update to match "new" callback identifier for doubleclicks
48 seoz 2012-01-04 03:47:07 fixed typo.
49 chrisps 2012-04-07 11:23:27 Changed the uncommented HelloWorld demo as I think it was using an old API call (elm_label_label_set) which doesnt seem to exist anymore. Updated it to work with the latest version that I have.
50 seoz 2013-01-09 07:50:40 Fixed sample build break.