summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavi Artigas <xavierartigas@yahoo.es>2020-02-12 19:22:43 +0100
committerXavi Artigas <xavierartigas@yahoo.es>2020-02-20 16:55:35 +0100
commitf3076c6d1951a8e66f3be74e1a0c53d9b34d6cb0 (patch)
tree48d4e2cd2c5a9c6eb9767e0a267d30adc974da0f
parentac5879e3e5c2ce86dc6166f0d7f669ea5ab063fa (diff)
homescreen: Simplify code and UI structure
Remove global variables. Add comments. General beautification.
-rw-r--r--apps/c/homescreen/src/homescreen.c417
1 files changed, 241 insertions, 176 deletions
diff --git a/apps/c/homescreen/src/homescreen.c b/apps/c/homescreen/src/homescreen.c
index ff328175..1b2eb087 100644
--- a/apps/c/homescreen/src/homescreen.c
+++ b/apps/c/homescreen/src/homescreen.c
@@ -1,240 +1,305 @@
1#define EFL_BETA_API_SUPPORT 1#define EFL_BETA_API_SUPPORT
2#include <Efl_Ui.h> 2#include <Efl_Ui.h>
3#include <Efreet.h> 3#include <Eio.h>
4 4
5static Efl_Ui_Spotlight_Container *over_container; 5#define TABLE_COLUMNS 4
6#define TABLE_ROWS 5
6 7
7#define SCALE 0.5 8typedef struct _Build_Data
9{
10 Eo *over_container;
11 Efl_Io_Manager *io_manager;
12 Eo *table;
13 int x, y;
14} Build_Data;
15
16static const char *launcher_apps[][3] =
17 { { "Call", "", "call-start" },
18 { "Contacts", "", "contact-new" },
19 { "Home", "xdg-open $HOME", "user-home" },
20 { "Mail", "xdg-email 'info@enlightenment.org'", "mail-send-receive" },
21 { "Documents", "xdg-open $(xdg-user-dir DOCUMENTS)", "folder-documents" } };
8 22
9typedef struct 23static void
24_icon_clicked_cb(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
10{ 25{
11 Eina_Position2D position; 26 const char *command = data;
12 const char *name; 27 printf("%s\n", command);
13 const char *icon; 28 efl_add(EFL_EXE_CLASS, efl_main_loop_get(),
14 Efl_Event_Cb cb; 29 efl_core_command_line_command_string_set(efl_added, command),
15} Icon; 30 efl_task_run(efl_added));
16 31}
17static void _fake_app_cb(void *data, const Efl_Event *ev);
18
19#define P "/usr/share/icons/hicolor/128x128/apps/"
20
21static Icon workspace1[] = {
22 { EINA_POSITION2D(0, 0), "Firefox", P"firefox.png", NULL},
23 { EINA_POSITION2D(0, 1), "Monodevelop", P"monodevelop.png", NULL},
24 { EINA_POSITION2D(0, 2), "Terminal", "utilities-terminal", NULL},
25 { EINA_POSITION2D(2, 0), NULL, NULL, NULL},
26};
27
28static Icon workspace2[] = {
29 { EINA_POSITION2D(0, 3), "web-browser", "web-browser", _fake_app_cb},
30 { EINA_POSITION2D(1, 3), "Emacs", P"emacs.png", _fake_app_cb},
31 { EINA_POSITION2D(2, 3), "etui", P"etui.png", _fake_app_cb},
32 { EINA_POSITION2D(3, 3), "CAD", P"librecad.png", _fake_app_cb},
33 { EINA_POSITION2D(4, 3), "Libreoffice", P"libreoffice-base.png", _fake_app_cb},
34 { EINA_POSITION2D(0, 4), "Riot", P"riot.png", _fake_app_cb},
35 { EINA_POSITION2D(1, 4), "Tex", P"texstudio.png", _fake_app_cb},
36 { EINA_POSITION2D(2, 4), "Telegram", P"telegram.png", _fake_app_cb},
37 { EINA_POSITION2D(3, 4), "Vlc", P"vlc.png", _fake_app_cb},
38 { EINA_POSITION2D(4, 4), "Mono", P"monodevelop.png", _fake_app_cb},
39 { EINA_POSITION2D(2, 0), NULL, NULL, NULL},
40};
41
42static Icon workspace3[] = {
43 { EINA_POSITION2D(0, 0), "bla", P"remmina.png", NULL},
44 { EINA_POSITION2D(1, 1), "bla", "ic2", NULL},
45 { EINA_POSITION2D(0, 2), "bla", "ic", NULL},
46 { EINA_POSITION2D(1, 3), "bla", "ic2", NULL},
47 { EINA_POSITION2D(0, 4), "bla", "ic", NULL},
48 { EINA_POSITION2D(2, 0), "bla", "ic", NULL},
49 { EINA_POSITION2D(3, 1), "bla", "ic2", NULL},
50 { EINA_POSITION2D(2, 2), "bla", "ic", NULL},
51 { EINA_POSITION2D(3, 3), "bla", "ic2", NULL},
52 { EINA_POSITION2D(2, 4), "bla", "ic", NULL},
53 { EINA_POSITION2D(4, 0), "bla", "ic", NULL},
54 { EINA_POSITION2D(4, 2), "bla", "ic", NULL},
55 { EINA_POSITION2D(4, 4), "bla", "ic", NULL},
56 { EINA_POSITION2D(0, 2), NULL, NULL, NULL},
57};
58
59static Icon* workspaces[] = {workspace1, workspace2, workspace3};
60
61static void _home_screen_cb(void *data, const Efl_Event *cb);
62
63static Icon start_line_config[] = {
64 { EINA_POSITION2D(0, 0), "Call", "call-start", _fake_app_cb},
65 { EINA_POSITION2D(0, 0), "Contact", "contact-new", _fake_app_cb},
66 { EINA_POSITION2D(0, 0), "Home", "applications-internet", _home_screen_cb},
67 { EINA_POSITION2D(0, 0), "Mail", "emblem-mail", _fake_app_cb},
68 { EINA_POSITION2D(0, 0), "Documents", "emblem-documents", _fake_app_cb},
69 { EINA_POSITION2D(0, 0), NULL, NULL, NULL},
70};
71
72static Eo *compositor;
73 32
33static void
34_icon_deleted_cb(void *data, const Efl_Event *ev EINA_UNUSED)
35{
36 free(data);
37}
74 38
75static Efl_Ui_Widget* 39// If "string" begins with the "key" prefix, sets "value" to whatever comes after "key"
76_create_icon(Icon *icon, Eo *parent) 40// up until the next \n, replacing it with a \0, in a newly allocated string
41// (which must be freed).
42// Returns 1 if key was found.
43static int
44_parse_token(const char *string, const char *key, char **value)
45{
46 if (eina_str_has_prefix(string, key))
47 {
48 int key_len = strlen(key);
49 const char *end = strchr(string + key_len, '\n');
50 if (!end)
51 end = string + strlen(string); // Point to EOF '\0'
52 int len = end - string - key_len;
53 if (*value) free(*value);
54 *value = eina_strndup(string + key_len, len + 1);
55 *((*value) + len) = '\0';
56 return 1;
57 }
58 return 0;
59}
60
61// Reads a .desktop file and returns the app name, the command to launch and the icon name.
62// Returns 0 if it didn't work.
63// Free the strings after usage.
64static int
65_parse_desktop_file(const char *desktop_file_path, char **app_name, char **app_command, char **app_icon_name)
66{
67 EINA_RW_SLICE_DECLARE(slice, 1024);
68 Efl_Io_File *desktop_file;
69 int ret = 0;
70
71 desktop_file = efl_new(EFL_IO_FILE_CLASS,
72 efl_file_set(efl_added, desktop_file_path),
73 efl_io_closer_close_on_invalidate_set(efl_added, EINA_TRUE));
74
75 if (!desktop_file)
76 return 0;
77
78 char *name = NULL, *command = NULL, *icon = NULL;
79 while (!efl_io_reader_eos_get(desktop_file) &&
80 efl_io_reader_read(desktop_file, &slice) == EINA_ERROR_NO_ERROR)
81 {
82 char *content = eina_rw_slice_strdup(slice);
83 char *ptr = content;
84 while ((ptr = strchr(ptr, '\n')))
85 {
86 ptr++;
87 _parse_token(ptr, "Name=", &name);
88 _parse_token(ptr, "Exec=", &command);
89 _parse_token(ptr, "Icon=", &icon);
90 }
91 free(content);
92 }
93 if (name && command && icon)
94 {
95 *app_name = name;
96 *app_command = command;
97 *app_icon_name = icon;
98 ret = 1;
99 }
100 else
101 {
102 if (name)
103 free(name);
104 if (command)
105 free(command);
106 if (icon)
107 free(icon);
108 }
109
110 efl_unref(desktop_file);
111
112 return ret;
113}
114
115// Creates a widget "button" with the specified name, icon and command
116// to execute on click.
117// These buttons are actually just an image with a label below.
118static Efl_Ui_Widget *
119_create_icon(Eo *parent, const char *name, const char *command, const char *icon)
77{ 120{
78 Eo *box = efl_add(EFL_UI_BOX_CLASS, parent); 121 Eo *box = efl_add(EFL_UI_BOX_CLASS, parent);
79 // Icon 122 // Icon
80 Eo *ic = efl_add(EFL_UI_IMAGE_CLASS, box, 123 efl_add(EFL_UI_IMAGE_CLASS, box,
81 efl_ui_image_icon_set(efl_added, icon->icon), 124 efl_ui_image_icon_set(efl_added, icon),
82 efl_gfx_hint_weight_set(efl_added, 1.0, 1.0), 125 efl_gfx_hint_weight_set(efl_added, 1.0, 1.0),
83 efl_gfx_entity_size_set(efl_added, EINA_SIZE2D(300, 300)), 126 efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(64, 64)),
84 efl_pack(box, efl_added)); 127 efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _icon_clicked_cb, command),
85 if (icon->cb) 128 efl_event_callback_add(efl_added, EFL_EVENT_DEL, _icon_deleted_cb, command),
86 efl_event_callback_add(ic, EFL_INPUT_EVENT_CLICKED, icon->cb, icon); 129 efl_pack(box, efl_added));
87 130
88 // Label 131 // Label
89 efl_add(EFL_UI_TEXTBOX_CLASS, box, 132 efl_add(EFL_UI_TEXTBOX_CLASS, box,
90 efl_text_set(efl_added, icon->name), 133 efl_text_set(efl_added, name),
91 efl_canvas_textblock_style_apply(efl_added, "effect_type=soft_shadow shadow_color=black"), 134 efl_text_multiline_set(efl_added, EINA_TRUE),
92 efl_text_horizontal_align_set(efl_added, 0.5), 135 efl_canvas_textblock_style_apply(efl_added,
93 efl_text_interactive_editable_set(efl_added, EINA_FALSE), 136 "effect_type=soft_shadow shadow_color=#444 wrap=word font_size=10 align=center ellipsis=1"),
94 efl_text_interactive_selection_allowed_set(efl_added, EINA_FALSE), 137 efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(0, 40)),
95 efl_pack(box, efl_added)); 138 efl_text_interactive_editable_set(efl_added, EINA_FALSE),
139 efl_text_interactive_selection_allowed_set(efl_added, EINA_FALSE),
140 efl_pack(box, efl_added));
96 141
97 return box; 142 return box;
98} 143}
99 144
100static Efl_Ui_Table* 145// Creates a widget "button" for the specified .desktop file.
101_hs_screen_new(Icon *icons) 146// These buttons are actually just an image with a label below.
147static Efl_Ui_Widget *
148_create_app_icon(Eo *parent, const char *desktop_file_path)
149{
150 char *name = NULL, *command = NULL, *icon = NULL;
151 Eo *widget = NULL;
152
153 if (!_parse_desktop_file(desktop_file_path, &name, &command, &icon))
154 return NULL;
155
156 widget = _create_icon(parent, name, command, icon);
157
158 free(name);
159 free(icon);
160 return widget;
161}
162
163// Adds a new empty page to the homescreen
164static void
165_add_page(Build_Data *bdata)
102{ 166{
103 Efl_Ui_Table *table; 167 bdata->table = efl_add(EFL_UI_TABLE_CLASS, bdata->over_container,
168 efl_pack_table_size_set(efl_added, TABLE_COLUMNS, TABLE_ROWS));
169 efl_pack_end(bdata->over_container, bdata->table);
170 bdata->x = bdata->y = 0;
171}
104 172
105 table = efl_add(EFL_UI_TABLE_CLASS, over_container); 173// Adds all files in the array to the homescreen, adding pages as they become full.
174static void
175_app_found(void *data, Eina_Array *files)
176{
177 unsigned int i;
178 const char *item;
179 Eina_Array_Iterator iterator;
180 Build_Data *bdata = data;
106 181
107 for (int y = 0; y < 5; ++y) 182 EINA_ARRAY_ITER_NEXT(files, i, item, iterator)
108 { 183 {
109 for (int x = 0; x < 5; ++x) 184 Eo *app = _create_app_icon(bdata->over_container, item);
185 if (app)
110 { 186 {
111 Eo *obj = efl_add(EFL_CANVAS_RECTANGLE_CLASS, table, efl_gfx_color_set(efl_added, 0, 0, 0, 0)); 187 if (!bdata->table)
112 efl_pack_table(table, obj, x, y, 1, 1); 188 _add_page(bdata);
189 efl_pack_table(bdata->table, app, bdata->x, bdata->y, 1, 1);
190 bdata->x++;
191 if (bdata->x == TABLE_COLUMNS)
192 {
193 bdata->x = 0;
194 bdata->y++;
195 if (bdata->y == TABLE_ROWS)
196 bdata->table = NULL;
197 }
113 } 198 }
114 } 199 }
200}
115 201
116 for (int i = 0; icons[i].name; ++i) 202// Called when directory listing has finished
203static Eina_Value
204_file_listing_done_cb (void *data, const Eina_Value file, const Eina_Future *dead EINA_UNUSED)
205{
206 Build_Data *bdata = data;
207 // Fill any remaining empty cells with invisible rectangles so the rest of the cells
208 // keep the same size as other pages
209 while (bdata->y < TABLE_ROWS)
117 { 210 {
118 Eo *icon = _create_icon(&icons[i], table); 211 while (bdata->x < TABLE_COLUMNS)
119 efl_pack_table(table, icon, icons[i].position.x, icons[i].position.y, 1, 1); 212 {
213 efl_add(EFL_CANVAS_RECTANGLE_CLASS, bdata->table,
214 efl_gfx_color_set(efl_added, 0, 0, 0, 0),
215 efl_pack_table(bdata->table, efl_added, bdata->x, bdata->y, 1, 1));
216 bdata->x++;
217 }
218 bdata->x = 0;
219 bdata->y++;
120 } 220 }
121 221 return file;
122 return table;
123} 222}
124 223
125static Efl_Ui_Widget* 224// Create Spotlight widget and start populating it with user apps.
126_build_homescreen(Efl_Ui_Win *win) 225static void
226_build_homescreen(Efl_Ui_Win *win, Build_Data *bdata)
127{ 227{
128 Efl_Ui_Spotlight_Indicator *indicator = efl_new(EFL_UI_SPOTLIGHT_ICON_INDICATOR_CLASS); 228 Efl_Ui_Spotlight_Indicator *indicator = efl_new(EFL_UI_SPOTLIGHT_ICON_INDICATOR_CLASS);
129 Efl_Ui_Spotlight_Manager *scroll = efl_new(EFL_UI_SPOTLIGHT_SCROLL_MANAGER_CLASS); 229 Efl_Ui_Spotlight_Manager *scroll = efl_new(EFL_UI_SPOTLIGHT_SCROLL_MANAGER_CLASS);
130 230 bdata->over_container = efl_add(EFL_UI_SPOTLIGHT_CONTAINER_CLASS, win,
131 over_container = efl_add(EFL_UI_SPOTLIGHT_CONTAINER_CLASS, win, 231 efl_ui_spotlight_manager_set(efl_added, scroll),
132 efl_ui_spotlight_manager_set(efl_added, scroll), 232 efl_ui_spotlight_indicator_set(efl_added, indicator)
133 efl_ui_spotlight_indicator_set(efl_added, indicator)
134 ); 233 );
234 bdata->table = NULL;
135 235
136 for (int i = 0; i < 3; ++i) 236 Eina_Future *future = efl_io_manager_ls(bdata->io_manager, "/usr/share/applications", bdata, _app_found, NULL);
137 { 237 eina_future_then(future, _file_listing_done_cb, bdata, NULL);
138 Eo *screen = _hs_screen_new(workspaces[i]);
139
140 efl_pack_end(over_container, screen);
141 }
142 return over_container;
143} 238}
144 239
240// The main box, with an upper space for the apps list and a lower space
241// for the quick-action buttons.
145static Efl_Ui_Widget* 242static Efl_Ui_Widget*
146_build_overall_structure(Efl_Ui_Win *win, Efl_Ui_Widget *homescreen) 243_build_overall_structure(Efl_Ui_Win *win, Efl_Ui_Widget *homescreen)
147{ 244{
148 Efl_Ui_Widget *o, *start_line; 245 Efl_Ui_Widget *box, *start_line;
246
247 box = efl_add(EFL_UI_BOX_CLASS, win);
149 248
150 o = efl_add(EFL_UI_BOX_CLASS, win); 249 // Set box background
151 efl_pack_end(o, homescreen); 250 // Objects retrieved with efl_part() only survive one function call, so we ref it
251 Eo *background = efl_ref(efl_part(box, "background"));
252 efl_file_key_set(background, "e/desktop/background");
253 efl_file_set(background, "../src/Hills.edj");
254 efl_file_load(background);
255 efl_unref(background);
152 256
153 //start line 257 efl_pack_end(box, homescreen);
154 start_line = efl_add(EFL_UI_BOX_CLASS, win); 258
259 // Start line
260 start_line = efl_add(EFL_UI_BOX_CLASS, win,
261 efl_gfx_color_set(efl_part(efl_added, "background"), 128, 128, 128, 128));
155 efl_gfx_hint_weight_set(start_line, 1.0, 0.0); 262 efl_gfx_hint_weight_set(start_line, 1.0, 0.0);
156 efl_ui_layout_orientation_set(start_line, EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL); 263 efl_ui_layout_orientation_set(start_line, EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL);
157 efl_gfx_hint_size_min_set(start_line, EINA_SIZE2D(5*150*SCALE, 150*SCALE)); 264 efl_pack_end(box, start_line);
158 efl_gfx_hint_size_max_set(start_line, EINA_SIZE2D(-1, 150*SCALE));
159 efl_pack_end(o, start_line);
160 265
161 for (int i = 0; i < 5; ++i) 266 for (unsigned int i = 0; i < sizeof(launcher_apps)/sizeof(launcher_apps[0]); ++i)
162 { 267 {
163 efl_pack_end(start_line, _create_icon(&start_line_config[i], start_line)); 268 efl_pack_end(start_line, _create_icon(start_line,
269 launcher_apps[i][0],
270 strdup(launcher_apps[i][1]),
271 launcher_apps[i][2]));
164 } 272 }
165 273
166 return o; 274 return box;
167}
168
169static Efl_Ui_Widget*
170_build_compositor(Efl_Ui_Win *win)
171{
172 Efl_Ui_Widget *comp;
173 Efl_Ui_Spotlight_Manager *stack;
174
175 stack = efl_new(EFL_UI_SPOTLIGHT_FADE_MANAGER_CLASS);
176 comp = efl_add(EFL_UI_SPOTLIGHT_CONTAINER_CLASS, win,
177 efl_ui_spotlight_manager_set(efl_added, stack));
178
179 return comp;
180}
181
182static void
183_home_screen_cb(void *data EINA_UNUSED, const Efl_Event *cb EINA_UNUSED)
184{
185 Efl_Canvas_Rectangle *rect;
186
187 rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS, compositor);
188 efl_ui_spotlight_push(compositor, rect);
189}
190
191static void
192_fake_app_cb(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
193{
194 Efl_Ui_Table *table = efl_add(EFL_UI_TABLE_CLASS, compositor);
195 Efl_Canvas_Rectangle *rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS, compositor);
196 efl_gfx_color_set(rect, 50, 50, 50, 255);
197 efl_pack_table(table, rect, 0, 0, 1, 1);
198 Efl_Ui_Textbox *text = efl_add(EFL_UI_TEXTBOX_CLASS , table);
199 efl_text_interactive_editable_set(text, EINA_FALSE);
200 efl_text_multiline_set(text, EINA_FALSE);
201 efl_text_set(text, "This is a testing application");
202 efl_pack_table(table, text, 0, 0, 1, 1);
203 efl_ui_spotlight_push(compositor, table);
204} 275}
205 276
277// Called when the app is closed
206static void 278static void
207_gui_quit_cb(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) 279_gui_quit_cb(void *data, const Efl_Event *event EINA_UNUSED)
208{ 280{
281 Build_Data *bdata = data;
282 if (bdata->io_manager)
283 efl_del(bdata->io_manager);
284 free(bdata);
209 efl_exit(0); 285 efl_exit(0);
210} 286}
211 287
212EAPI_MAIN void 288EAPI_MAIN void
213efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED) 289efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
214{ 290{
215 Eo *win, *over_container, *desktop, *background; 291 Eo *win, *desktop;
292 Build_Data *bdata = calloc (1, sizeof(Build_Data));
216 293
217 efreet_init(); 294 bdata->io_manager = efl_add(EFL_IO_MANAGER_CLASS, efl_main_loop_get());
218 295
219 win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(), 296 win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
220 efl_ui_win_autodel_set(efl_added, EINA_TRUE)); 297 efl_ui_win_autodel_set(efl_added, EINA_TRUE));
221 // when the user clicks "close" on a window there is a request to delete 298 // when the user clicks "close" on a window there is a request to delete
222 efl_event_callback_add(win, EFL_UI_WIN_EVENT_DELETE_REQUEST, _gui_quit_cb, NULL); 299 efl_event_callback_add(win, EFL_UI_WIN_EVENT_DELETE_REQUEST, _gui_quit_cb, bdata);
223 efl_gfx_entity_size_set(win, EINA_SIZE2D(720*SCALE+15, 1280*SCALE)); 300
224 301 _build_homescreen(win, bdata);
225 over_container = _build_homescreen(win); 302 desktop = _build_overall_structure(win, bdata->over_container);
226 desktop = _build_overall_structure(win, over_container); 303 efl_content_set(win, desktop);
227
228 compositor = _build_compositor(win);
229 //efl_pack_end(compositor, desktop);
230 efl_ui_spotlight_push(compositor, desktop);
231 efl_gfx_entity_size_set(compositor, EINA_SIZE2D(720*SCALE, 1280*SCALE));
232
233 background = efl_add(EFL_UI_IMAGE_CLASS, win,
234 //efl_file_set(efl_added, "../src/wood_01.jpg"),
235 efl_file_key_set(efl_added, "e/desktop/background"),
236 efl_file_set(efl_added, "../src/Hills.edj"),
237 efl_gfx_image_scale_method_set(efl_added, EFL_GFX_IMAGE_SCALE_METHOD_EXPAND));
238 efl_content_set(win, background);
239} 304}
240EFL_MAIN() 305EFL_MAIN()