Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Vincent Poinot
recipes
Commits
d1cff169
Commit
d1cff169
authored
Nov 26, 2016
by
Matthias Clasen
Browse files
Refactor search into a separate class
And drop the separate ingredients search, while we are at it.
parent
03a7f4ac
Changes
16
Expand all
Hide whitespace changes
Inline
Side-by-side
src/Makefile.am
View file @
d1cff169
...
...
@@ -41,14 +41,10 @@ recipes_SOURCES = \
gr-ingredient.c
\
gr-ingredient-row.h
\
gr-ingredient-row.c
\
gr-ingredient-search-tile.h
\
gr-ingredient-search-tile.c
\
gr-ingredient-tile.h
\
gr-ingredient-tile.c
\
gr-ingredients-list.h
\
gr-ingredients-list.c
\
gr-ingredients-search-page.h
\
gr-ingredients-search-page.c
\
gr-ingredients-page.h
\
gr-ingredients-page.c
\
gr-list-page.h
\
...
...
@@ -57,6 +53,8 @@ recipes_SOURCES = \
gr-meal-row.c
\
gr-preferences.h
\
gr-preferences.c
\
gr-query-editor.h
\
gr-query-editor.c
\
gr-recipe.h
\
gr-recipe.c
\
gr-recipe-store.h
\
...
...
src/gr-diet-row.c
View file @
d1cff169
...
...
@@ -32,6 +32,7 @@ struct _GrDietRow
GtkWidget
*
label
;
GtkWidget
*
image
;
char
*
diet_name
;
GrDiets
diet
;
gboolean
include
;
...
...
@@ -234,24 +235,30 @@ gr_diet_row_set_entry (GrDietRow *row,
char
*
gr_diet_row_get_search_term
(
GrDietRow
*
row
)
{
if
(
row
->
include
)
{
switch
(
row
->
diet
)
{
case
GR_DIET_GLUTEN_FREE
:
return
g_strconcat
(
"di:"
,
"gluten-free"
,
NULL
);
case
GR_DIET_NUT_FREE
:
return
g_strconcat
(
"di:"
,
"nut-free"
,
NULL
);
case
GR_DIET_VEGAN
:
return
g_strconcat
(
"di:"
,
"vegan"
,
NULL
);
case
GR_DIET_VEGETARIAN
:
return
g_strconcat
(
"di:"
,
"vegetarian"
,
NULL
);
case
GR_DIET_MILK_FREE
:
return
g_strconcat
(
"di:"
,
"milk-free"
,
NULL
);
default:
;
}
}
if
(
row
->
include
)
return
g_strconcat
(
"di:"
,
gr_diet_row_get_diet
(
row
),
NULL
);
else
return
NULL
;
}
return
NULL
;
const
char
*
gr_diet_row_get_diet
(
GrDietRow
*
row
)
{
switch
(
row
->
diet
)
{
case
GR_DIET_GLUTEN_FREE
:
return
"gluten-free"
;
case
GR_DIET_NUT_FREE
:
return
"nut-free"
;
case
GR_DIET_VEGAN
:
return
"vegan"
;
case
GR_DIET_VEGETARIAN
:
return
"vegetarian"
;
case
GR_DIET_MILK_FREE
:
return
"milk-free"
;
default:
return
NULL
;
}
}
char
*
...
...
src/gr-diet-row.h
View file @
d1cff169
...
...
@@ -35,5 +35,6 @@ void gr_diet_row_set_entry (GrDietRow *row,
char
*
gr_diet_row_get_search_term
(
GrDietRow
*
row
);
char
*
gr_diet_row_get_label
(
GrDietRow
*
row
);
const
char
*
gr_diet_row_get_diet
(
GrDietRow
*
row
);
G_END_DECLS
src/gr-ingredient-row.c
View file @
d1cff169
...
...
@@ -265,3 +265,9 @@ gr_ingredient_row_get_label (GrIngredientRow *row)
else
return
NULL
;
}
const
char
*
gr_ingredient_row_get_ingredient
(
GrIngredientRow
*
row
)
{
return
row
->
ingredient
;
}
src/gr-ingredient-row.h
View file @
d1cff169
...
...
@@ -34,5 +34,6 @@ void gr_ingredient_row_set_entry (GrIngredientRow *row,
char
*
gr_ingredient_row_get_search_term
(
GrIngredientRow
*
row
);
char
*
gr_ingredient_row_get_label
(
GrIngredientRow
*
row
);
const
char
*
gr_ingredient_row_get_ingredient
(
GrIngredientRow
*
row
);
G_END_DECLS
src/gr-ingredients-search-page.c
deleted
100644 → 0
View file @
03a7f4ac
/* gr-ingredients-search-page.c:
*
* Copyright (C) 2016 Matthias Clasen <mclasen@redhat.com>
*
* Licensed under the GNU General Public License Version 3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more edit.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include
"config.h"
#include
<glib/gi18n.h>
#include
<gtk/gtk.h>
#include
"gr-recipe-store.h"
#include
"gr-recipe-tile.h"
#include
"gr-recipe.h"
#include
"gr-app.h"
#include
"gr-utils.h"
#include
"gr-ingredients-search-page.h"
#include
"gr-ingredient-search-tile.h"
#include
"gr-ingredient.h"
typedef
struct
{
GrIngredientsSearchPage
*
page
;
GrRecipeStore
*
store
;
char
*
cf_term
;
char
**
keys
;
int
length
;
int
pos
;
gboolean
filled
;
}
SearchParams
;
static
void
search_params_free
(
gpointer
data
)
{
SearchParams
*
params
=
data
;
g_free
(
params
->
cf_term
);
g_free
(
params
->
keys
);
g_free
(
params
);
}
struct
_GrIngredientsSearchPage
{
GtkBox
parent_instance
;
GtkWidget
*
ingredients_popover
;
GtkWidget
*
ingredients_list
;
GtkWidget
*
flow_box
;
GtkWidget
*
terms_box
;
GtkWidget
*
search_entry
;
GtkWidget
*
search_stack
;
char
*
cf_term
;
guint
search
;
};
G_DEFINE_TYPE
(
GrIngredientsSearchPage
,
gr_ingredients_search_page
,
GTK_TYPE_BOX
)
static
void
update_search
(
GrIngredientsSearchPage
*
page
);
static
void
remove_tile
(
GrIngredientSearchTile
*
tile
,
gpointer
data
)
{
GrIngredientsSearchPage
*
page
=
data
;
gtk_container_remove
(
GTK_CONTAINER
(
page
->
terms_box
),
GTK_WIDGET
(
tile
));
update_search
(
page
);
}
static
void
tile_changed
(
GrIngredientSearchTile
*
tile
,
gpointer
data
)
{
GrIngredientsSearchPage
*
page
=
data
;
update_search
(
page
);
}
static
void
add_ingredient
(
GrIngredientsSearchPage
*
page
,
const
char
*
ingredient
)
{
GtkWidget
*
tile
;
tile
=
gr_ingredient_search_tile_new
(
ingredient
);
g_signal_connect
(
tile
,
"remove-tile"
,
G_CALLBACK
(
remove_tile
),
page
);
g_signal_connect
(
tile
,
"tile-changed"
,
G_CALLBACK
(
tile_changed
),
page
);
gtk_widget_show
(
tile
);
gtk_container_add
(
GTK_CONTAINER
(
page
->
terms_box
),
tile
);
update_search
(
page
);
}
static
void
search_changed
(
GrIngredientsSearchPage
*
page
)
{
const
char
*
term
;
term
=
gtk_entry_get_text
(
GTK_ENTRY
(
page
->
search_entry
));
if
(
strlen
(
term
)
<
2
)
{
gtk_widget_hide
(
page
->
ingredients_popover
);
return
;
}
g_free
(
page
->
cf_term
);
page
->
cf_term
=
g_utf8_casefold
(
gtk_entry_get_text
(
GTK_ENTRY
(
page
->
search_entry
)),
-
1
);
gtk_list_box_invalidate_filter
(
GTK_LIST_BOX
(
page
->
ingredients_list
));
gtk_widget_show
(
page
->
ingredients_popover
);
}
static
void
search_activate
(
GrIngredientsSearchPage
*
page
)
{
const
char
*
term
;
term
=
gtk_entry_get_text
(
GTK_ENTRY
(
page
->
search_entry
));
if
(
strlen
(
term
)
<
2
)
{
gtk_widget_error_bell
(
page
->
search_entry
);
return
;
}
add_ingredient
(
page
,
term
);
gtk_entry_set_text
(
GTK_ENTRY
(
page
->
search_entry
),
""
);
gtk_widget_hide
(
page
->
ingredients_popover
);
gtk_widget_grab_focus
(
page
->
search_entry
);
}
static
void
ingredients_search_page_finalize
(
GObject
*
object
)
{
GrIngredientsSearchPage
*
self
=
GR_INGREDIENTS_SEARCH_PAGE
(
object
);
g_clear_pointer
(
&
self
->
cf_term
,
g_free
);
if
(
self
->
search
)
g_source_remove
(
self
->
search
);
G_OBJECT_CLASS
(
gr_ingredients_search_page_parent_class
)
->
finalize
(
object
);
}
static
gboolean
update_search_idle
(
gpointer
data
)
{
SearchParams
*
params
=
data
;
if
(
params
->
cf_term
==
NULL
)
{
GList
*
terms
,
*
l
;
GString
*
s
;
container_remove_all
(
GTK_CONTAINER
(
params
->
page
->
flow_box
));
terms
=
gtk_container_get_children
(
GTK_CONTAINER
(
params
->
page
->
terms_box
));
if
(
terms
==
NULL
)
{
gtk_stack_set_visible_child_name
(
GTK_STACK
(
params
->
page
->
search_stack
),
"list"
);
return
G_SOURCE_REMOVE
;
}
s
=
g_string_new
(
""
);
for
(
l
=
terms
;
l
;
l
=
l
->
next
)
{
GrIngredientSearchTile
*
tile
=
l
->
data
;
if
(
gr_ingredient_search_tile_get_excluded
(
tile
))
g_string_append
(
s
,
"i-:"
);
else
g_string_append
(
s
,
"i+:"
);
g_string_append
(
s
,
gr_ingredient_search_tile_get_ingredient
(
tile
));
g_string_append
(
s
,
" "
);
}
g_list_free
(
terms
);
params
->
cf_term
=
g_utf8_casefold
(
s
->
str
,
-
1
);
g_string_free
(
s
,
FALSE
);
return
G_SOURCE_CONTINUE
;
}
if
(
params
->
keys
==
NULL
)
{
params
->
keys
=
gr_recipe_store_get_recipe_keys
(
params
->
store
,
&
params
->
length
);
params
->
pos
=
0
;
return
G_SOURCE_CONTINUE
;
}
if
(
params
->
pos
<
params
->
length
)
{
int
i
;
for
(
i
=
0
;
params
->
pos
<
params
->
length
&&
i
<
5
;
params
->
pos
++
,
i
++
)
{
g_autoptr
(
GrRecipe
)
recipe
=
NULL
;
GtkWidget
*
tile
;
recipe
=
gr_recipe_store_get
(
params
->
store
,
params
->
keys
[
params
->
pos
]);
if
(
gr_recipe_matches
(
recipe
,
params
->
cf_term
))
{
tile
=
gr_recipe_tile_new
(
recipe
);
gtk_widget_show
(
tile
);
gtk_container_add
(
GTK_CONTAINER
(
params
->
page
->
flow_box
),
tile
);
params
->
filled
=
TRUE
;
}
}
}
if
(
params
->
pos
<
params
->
length
)
return
G_SOURCE_CONTINUE
;
gtk_stack_set_visible_child_name
(
GTK_STACK
(
params
->
page
->
search_stack
),
params
->
filled
?
"list"
:
"empty"
);
return
G_SOURCE_REMOVE
;
}
static
void
update_search
(
GrIngredientsSearchPage
*
page
)
{
SearchParams
*
params
;
if
(
page
->
search
)
{
g_source_remove
(
page
->
search
);
page
->
search
=
0
;
}
params
=
g_new0
(
SearchParams
,
1
);
params
->
page
=
page
;
params
->
store
=
gr_app_get_recipe_store
(
GR_APP
(
g_application_get_default
()));
g_idle_add_full
(
G_PRIORITY_DEFAULT_IDLE
,
update_search_idle
,
params
,
search_params_free
);
}
static
void
row_activated
(
GtkListBox
*
list
,
GtkListBoxRow
*
row
,
GrIngredientsSearchPage
*
page
)
{
GtkWidget
*
label
;
const
char
*
text
;
label
=
gtk_bin_get_child
(
GTK_BIN
(
row
));
text
=
gtk_label_get_label
(
GTK_LABEL
(
label
));
add_ingredient
(
page
,
text
);
gtk_entry_set_text
(
GTK_ENTRY
(
page
->
search_entry
),
""
);
gtk_widget_hide
(
page
->
ingredients_popover
);
gtk_widget_grab_focus
(
page
->
search_entry
);
}
static
gboolean
filter_ingredients_list
(
GtkListBoxRow
*
row
,
gpointer
data
)
{
GrIngredientsSearchPage
*
page
=
data
;
GtkWidget
*
label
;
char
*
cf
;
if
(
!
page
->
cf_term
)
return
TRUE
;
label
=
gtk_bin_get_child
(
GTK_BIN
(
row
));
cf
=
(
char
*
)
g_object_get_data
(
G_OBJECT
(
label
),
"casefolded"
);
return
g_str_has_prefix
(
cf
,
page
->
cf_term
);
}
static
void
populate_popover
(
GrIngredientsSearchPage
*
page
)
{
int
i
;
const
char
**
ingredients
;
ingredients
=
gr_ingredient_get_names
(
NULL
);
for
(
i
=
0
;
ingredients
[
i
];
i
++
)
{
GtkWidget
*
label
;
gchar
*
cf
;
label
=
gtk_label_new
(
ingredients
[
i
]);
cf
=
g_utf8_casefold
(
ingredients
[
i
],
-
1
);
g_object_set_data_full
(
G_OBJECT
(
label
),
"casefolded"
,
cf
,
g_free
);
gtk_label_set_xalign
(
GTK_LABEL
(
label
),
0
);
g_object_set
(
label
,
"margin"
,
6
,
NULL
);
gtk_widget_show
(
label
);
gtk_container_add
(
GTK_CONTAINER
(
page
->
ingredients_list
),
label
);
}
gtk_list_box_set_filter_func
(
GTK_LIST_BOX
(
page
->
ingredients_list
),
filter_ingredients_list
,
page
,
NULL
);
g_signal_connect
(
page
->
ingredients_list
,
"row-activated"
,
G_CALLBACK
(
row_activated
),
page
);
}
static
void
gr_ingredients_search_page_init
(
GrIngredientsSearchPage
*
page
)
{
gtk_widget_set_has_window
(
GTK_WIDGET
(
page
),
FALSE
);
gtk_widget_init_template
(
GTK_WIDGET
(
page
));
populate_popover
(
page
);
}
static
void
gr_ingredients_search_page_class_init
(
GrIngredientsSearchPageClass
*
klass
)
{
GObjectClass
*
object_class
=
G_OBJECT_CLASS
(
klass
);
GtkWidgetClass
*
widget_class
=
GTK_WIDGET_CLASS
(
klass
);
object_class
->
finalize
=
ingredients_search_page_finalize
;
gtk_widget_class_set_template_from_resource
(
widget_class
,
"/org/gnome/Recipes/gr-ingredients-search-page.ui"
);
gtk_widget_class_bind_template_child
(
widget_class
,
GrIngredientsSearchPage
,
flow_box
);
gtk_widget_class_bind_template_child
(
widget_class
,
GrIngredientsSearchPage
,
terms_box
);
gtk_widget_class_bind_template_child
(
widget_class
,
GrIngredientsSearchPage
,
ingredients_popover
);
gtk_widget_class_bind_template_child
(
widget_class
,
GrIngredientsSearchPage
,
search_entry
);
gtk_widget_class_bind_template_child
(
widget_class
,
GrIngredientsSearchPage
,
ingredients_list
);
gtk_widget_class_bind_template_child
(
widget_class
,
GrIngredientsSearchPage
,
search_stack
);
gtk_widget_class_bind_template_callback
(
widget_class
,
search_changed
);
gtk_widget_class_bind_template_callback
(
widget_class
,
search_activate
);
}
GtkWidget
*
gr_ingredients_search_page_new
(
void
)
{
GrIngredientsSearchPage
*
page
;
page
=
g_object_new
(
GR_TYPE_INGREDIENTS_SEARCH_PAGE
,
NULL
);
return
GTK_WIDGET
(
page
);
}
void
gr_ingredients_search_page_set_ingredient
(
GrIngredientsSearchPage
*
page
,
const
char
*
ingredient
)
{
container_remove_all
(
GTK_CONTAINER
(
page
->
terms_box
));
gtk_stack_set_visible_child_name
(
GTK_STACK
(
page
->
search_stack
),
"list"
);
if
(
ingredient
)
add_ingredient
(
page
,
ingredient
);
}
src/gr-ingredients-search-page.ui
deleted
100644 → 0
View file @
03a7f4ac
<?xml version="1.0" encoding="UTF-8"?>
<interface
domain=
"recipes"
>
<!-- interface-requires gtk+ 3.8 -->
<template
class=
"GrIngredientsSearchPage"
parent=
"GtkBox"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"orientation"
>
vertical
</property>
<child>
<object
class=
"GtkBox"
>
<property
name=
"visible"
>
1
</property>
<style>
<class
name=
"searchbar"
/>
</style>
<child>
<object
class=
"GtkBox"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"spacing"
>
6
</property>
<property
name=
"hexpand"
>
1
</property>
<child>
<object
class=
"GtkSearchEntry"
id=
"search_entry"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"valign"
>
center
</property>
<property
name=
"margin"
>
40
</property>
<property
name=
"placeholder-text"
translatable=
"yes"
>
Ingredient
</property>
<signal
name=
"search-changed"
handler=
"search_changed"
swapped=
"yes"
/>
<signal
name=
"activate"
handler=
"search_activate"
swapped=
"yes"
/>
</object>
</child>
<child
type=
"center"
>
<object
class=
"GtkBox"
id=
"terms_box"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"spacing"
>
6
</property>
<property
name=
"margin"
>
6
</property>
<property
name=
"halign"
>
center
</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object
class=
"GtkStack"
id=
"search_stack"
>
<property
name=
"visible"
>
1
</property>
<child>
<object
class=
"GtkBox"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"halign"
>
center
</property>
<property
name=
"valign"
>
center
</property>
<property
name=
"orientation"
>
vertical
</property>
<property
name=
"spacing"
>
10
</property>
<child>
<object
class=
"GtkImage"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"icon-name"
>
edit-find-symbolic
</property>
<property
name=
"pixel-size"
>
128
</property>
<style>
<class
name=
"dim-label"
/>
</style>
</object>
</child>
<child>
<object
class=
"GtkLabel"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"label"
translatable=
"yes"
>
No recipes found
</property>
<style>
<class
name=
"dim-label"
/>
</style>
<attributes>
<attribute
name=
"scale"
value=
"1.2"
/>
<attribute
name=
"weight"
value=
"bold"
/>
</attributes>
</object>
</child>
<child>
<object
class=
"GtkLabel"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"label"
translatable=
"yes"
>
Try a different search
</property>
<style>
<class
name=
"dim-label"
/>
</style>
</object>
</child>
</object>
<packing>
<property
name=
"name"
>
empty
</property>
</packing>
</child>
<child>
<object
class=
"GtkScrolledWindow"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"expand"
>
1
</property>
<property
name=
"hscrollbar-policy"
>
never
</property>
<child>
<object
class=
"GtkFlowBox"
id=
"flow_box"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"valign"
>
start
</property>
<property
name=
"column-spacing"
>
10
</property>
<property
name=
"row-spacing"
>
10
</property>
<property
name=
"margin"
>
20
</property>
<property
name=
"selection-mode"
>
none
</property>
</object>
</child>
</object>
<packing>
<property
name=
"name"
>
list
</property>
</packing>
</child>
</object>
</child>
</template>
<object
class=
"GtkPopover"
id=
"ingredients_popover"
>
<property
name=
"modal"
>
0
</property>
<property
name=
"relative-to"
>
search_entry
</property>
<property
name=
"position"
>
bottom
</property>
<child>
<object
class=
"GtkScrolledWindow"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"hscrollbar-policy"
>
never
</property>
<property
name=
"propagate-natural-height"
>
1
</property>
<child>
<object
class=
"GtkListBox"
id=
"ingredients_list"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"selection-mode"
>
none
</property>
<child
type=
"placeholder"
>
<object
class=
"GtkLabel"
>
<property
name=
"visible"
>
1
</property>
<property
name=
"label"
translatable=
"yes"
>
No match
</property>
<attributes>
<attribute
name=
"style"
value=
"italic"
/>
</attributes>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
src/gr-meal-row.c
View file @
d1cff169
...
...
@@ -260,3 +260,9 @@ gr_meal_row_get_label (GrMealRow *row)
else
return
NULL
;
}
const
char
*
gr_meal_row_get_meal
(
GrMealRow
*
row
)
{
return
row
->
meal
;
}
src/gr-meal-row.h
View file @
d1cff169
...
...
@@ -32,6 +32,7 @@ GrMealRow *gr_meal_row_new (const char *meal);
void
gr_meal_row_set_entry
(
GrMealRow
*
row
,
GdTaggedEntry
*
entry
);
const
char
*
gr_meal_row_get_meal
(
GrMealRow
*
row
);
char
*
gr_meal_row_get_search_term
(
GrMealRow
*
row
);
<