Puzzle / src /
view.c
/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd
*
* Licensed under the Flora License, Version 1.1 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://floralicense.org/license/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <Elementary.h>
#include <efl_extension.h>
#include "view.h"
#include "view_defines.h"
#include "$(appName).h"
#define IMG_SMILE "smile.jpg"
#define IMG_CORRECT "correct.jpg"
#define PUZZLE_RATIO 0.6
#define MENU_RATIO 0.2
#define PUZZLE_SIZE_LEVEL_4 4
#define PUZZLE_SIZE_LEVEL_5 5
#define IMAGE_PREVIEW_SIZE 0.5
#define MAX_SHUFFLE_COUNT 70
#define PIECE_COUNT PUZZLE_SIZE_LEVEL_5 * PUZZLE_SIZE_LEVEL_5 + 1
typedef enum {NB_UP, NB_DOWN, NB_LEFT, NB_RIGHT} navi_button_e;
static struct view_info {
Evas_Object *win;
Evas_Object *conform;
Evas_Object *radio_group;
int radio_selector;
Evas_Object *piece[PIECE_COUNT];
int piece_pos[PIECE_COUNT];
Evas* e;
int full_image_width;
int full_image_height;
int origin_image_width;
int origin_image_height;
int puzzle_start_x;
int puzzle_start_y;
int white_piece;
int size;
int screen_width;
int screen_height;
int r;
int g;
int b;
int a;
int start;
bool shuffling;
int shuffle_count;
int level;
} s_info = {
.win = NULL,
.conform = NULL,
.radio_group = NULL,
.radio_selector = 0,
.piece = {NULL,},
.piece_pos = {0,},
.e = NULL,
.full_image_width = 0,
.full_image_height = 0,
.origin_image_width = 0,
.origin_image_height = 0,
.puzzle_start_x = 0,
.puzzle_start_y = 0,
.white_piece = 0,
.size = 0,
.screen_width = 0,
.screen_height = 0,
.r = 0,
.g = 0,
.b = 0,
.a = 0,
.start = 0,
.shuffling = 0,
.shuffle_count = 0,
.level = 0,
};
static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info);
static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info);
static void _popup_close_cb(void *data, Evas_Object *obj, void *event_info);
static void _popup_level_accept_cb(void *data, Evas_Object *obj, void *event_info);
static void _answer_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _level_icon_click_cb(void* data, Evas *e, Evas_Object *obj, void *event_info);
static void _shuffle_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _navigation_button_cb(void *data, Evas_Object *obj, void *event_info);
static Eina_Bool _shuffle_cb(void *data);
static void _get_app_resource(const char *edj_file_in, char *edj_path_out);
static bool _create_main_layout(void);
static bool _create_menu(Evas_Object *parent);
static bool _create_navigation(Evas_Object *parent);
static void _create_puzzle(int level);
static void _move_puzzle(int offset);
static bool _check_puzzle_move(navi_button_e nb_type);
static void _change_size(int level);
static void _finished(void);
/**
* @brief Creates essential objects: window, conformant and layout.
*/
Eina_Bool view_create(void *user_data)
{
/* Create the window */
s_info.win = view_create_win(PACKAGE);
if (s_info.win == NULL) {
dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a window.");
return EINA_FALSE;
}
elm_win_screen_size_get(s_info.win, NULL, NULL, &s_info.screen_width, &s_info.screen_height);
/* Create the conformant */
s_info.conform = view_create_conformant_without_indicator(s_info.win);
if (s_info.conform == NULL) {
dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a conformant");
return EINA_FALSE;
}
if (!_create_main_layout())
return EINA_FALSE;
/* Show the window after main view is set up */
evas_object_show(s_info.win);
return EINA_TRUE;
}
/**
* @brief Creates a basic window named package.
* @param[in] pkg_name Name of the window
*/
Evas_Object *view_create_win(const char *pkg_name)
{
Evas_Object *win = NULL;
/*
* Window
* Create and initialize elm_win.
* elm_win is mandatory to manipulate the window.
*/
win = elm_win_util_standard_add(pkg_name, pkg_name);
elm_win_conformant_set(win, EINA_TRUE);
elm_win_autodel_set(win, EINA_TRUE);
elm_win_indicator_mode_set(win, ELM_WIN_INDICATOR_HIDE);
elm_win_indicator_opacity_set(win, ELM_WIN_INDICATOR_OPAQUE);
evas_object_smart_callback_add(win, "delete,request", _delete_win_request_cb, NULL);
return win;
}
/**
* @brief Creates a layout object for parent object based on provided EDJE script.
* @param[in] parent The parent object for layout object.
* @param[in] edj_file_name The relative path to the layout EDJE script.
* @param[in] edj_group The name of the group to be loaded from the EDJE script.
* @param[in] part_name The EDJE part's name where the layout is to be set.
* @return The function returns layout object if it was created successfully,
* otherwise 'NULL' is returned.
*/
Evas_Object *view_create_layout(Evas_Object *parent, const char *edj_file_name, const char *group_name, const char *part_name)
{
char edj_path[PATH_MAX] = {0, };
Evas_Object *layout = NULL;
_get_app_resource(edj_file_name, edj_path);
layout = elm_layout_add(parent);
if (!layout) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_layout_add() failed.");
return NULL;
}
if (!elm_layout_file_set(layout, edj_path, group_name)) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_layout_file_set() failed.");
return NULL;
}
evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
eext_object_event_callback_add(layout, EEXT_CALLBACK_BACK, _layout_back_cb, NULL);
if (!part_name)
elm_object_content_set(parent, layout);
else
elm_object_part_content_set(parent, part_name, layout);
evas_object_show(layout);
return layout;
}
/**
* @brief Creates a conformant without indicator for wearable app.
* @param[in] win The object to which you want to set this conformant
* Conformant is mandatory for base GUI to have proper size
*/
Evas_Object *view_create_conformant_without_indicator(Evas_Object *win)
{
/*
* Conformant
* Create and initialize elm_conformant.
* elm_conformant is mandatory for base GUI to have proper size
* when indicator or virtual keypad is visible.
*/
Evas_Object *conform = NULL;
if (win == NULL) {
dlog_print(DLOG_ERROR, LOG_TAG, "window is NULL.");
return NULL;
}
conform = elm_conformant_add(win);
evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(win, conform);
evas_object_show(conform);
return conform;
}
/**
* @brief Creates an icon object and sets it to the parent object.
* The icon displays an standard image specified by icon_type.
* @param[in] parent The parent object for icon object.
* @param[in] icon_type The icon type name.
* @param[in] part_name The EDJE part's name where the icon is to be set.
* @return The function returns icon object if it was created successfully,
* otherwise 'NULL' is returned.
*/
Evas_Object *view_create_icon(Evas_Object *parent, const char *icon_type, const char *part_name)
{
Evas_Object *icon = elm_icon_add(parent);
if (!icon) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_icon_add() failed.");
return NULL;
};
if (!elm_icon_standard_set(icon, icon_type)) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_icon_standard_set() failed. No '%s' icon.", icon_type);
return NULL;
}
elm_object_part_content_set(parent, part_name, icon);
evas_object_show(icon);
return icon;
}
/**
* @brief Creates an image object and sets it to the parent object.
* @param[in] parent The parent object for image object.
* @param[in] file_name The name of the image file to be displayed.
* @param[in] part_name The EDJE part's name where the image is to be set.
* @param[in] width The width of the image in pixels.
* @param[in] height The height of the image in pixels.
* @return The function returns image object if it was created successfully,
* otherwise 'NULL' is returned.
*/
Evas_Object *view_create_image(Evas_Object *parent, const char *file_name, const char *part_name, int width, int height)
{
char img_path[PATH_MAX] = {0, };
Evas_Object *image = elm_image_add(parent);
if (!image) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_image_add() failed.");
return NULL;
};
_get_app_resource(file_name, img_path);
if (!elm_image_file_set(image, img_path, NULL)) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_image_file_set() failed.");
return NULL;
}
elm_image_aspect_fixed_set(image, EINA_TRUE);
evas_object_size_hint_min_set(image, width, height);
evas_object_size_hint_max_set(image, width, height);
elm_object_part_content_set(parent, part_name, image);
evas_object_show(image);
return image;
}
/**
* @brief Creates a button object for the parent object.
* @param[in] parent The parent object for button object.
* @param[in] caption The caption to be displayed on the button.
* @param[in] part_name The part name where the button is to be set.
* @param[in] on_click_cb The callback function's handler to be invoked on button click.
* @param[in] data The data to be passed to the callback function.
* @return The function returns button object if it was created successfully,
* otherwise 'NULL' is returned.
*/
Evas_Object *view_create_button(Evas_Object *parent, const char *caption, const char *part_name, Evas_Smart_Cb on_click_cb, void *data)
{
Evas_Object *button = elm_button_add(parent);
if (!button) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_button_add() failed.");
return NULL;
}
evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_text_set(button, caption);
elm_object_part_content_set(parent, part_name, button);
evas_object_smart_callback_add(button, "clicked", on_click_cb, data);
evas_object_show(button);
return button;
}
/**
* @brief Creates a popup window with 'OK' button set.
* @param[in] title The popup window title.
* @param[in] button1 The first button caption.
* @param[in] on_click1_cb The first button's callback function invoked on click event.
* @param[in] button2 The second button caption.
* @param[in] on_click2_cb The second button's callback function invoked on click event.
* @return This function returns popup window object if it was successfully created,
* otherwise 'false' is returned.
*/
Evas_Object *view_create_popup(const char *title, const char *button1, Evas_Smart_Cb on_click1_cb, const char *button2, Evas_Smart_Cb on_click2_cb)
{
Evas_Object *button;
Evas_Object *popup = elm_popup_add(s_info.win);
if (!popup) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_popup_add() failed.");
return NULL;
}
elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0);
elm_object_part_text_set(popup, "title,text", title);
if (button1) {
button = view_create_button(popup, button1, "button1", on_click1_cb, (void *)popup);
if (!button) {
evas_object_del(popup);
return NULL;
}
}
if (button2) {
button = view_create_button(popup, button2, "button2", on_click2_cb, (void *)popup);
if (!button) {
evas_object_del(popup);
return NULL;
}
}
evas_object_show(popup);
return popup;
}
/**
* @brief Creates a radio button within a group of radio buttons and sets it to the parent object.
* @param[in] parent The parent object for radio button.
* @param[in] part_name The part name where the radio is to be set.
* @param[in] v_pointer The pointer to the selected item's id.
* @return This function returns radio button object if it was successfully created,
* otherwise 'false' is returned.
*/
Evas_Object *view_create_radio(Evas_Object *parent, const char *part_name, int *v_pointer)
{
static int radio_id = 0;
Evas_Object *radio = elm_radio_add(parent);
if (!radio) {
dlog_print(DLOG_ERROR, LOG_TAG, "elm_radio_add() failed.");
return NULL;
}
if (!s_info.radio_group) {
radio_id = 0;
s_info.radio_group = radio;
elm_radio_value_pointer_set(radio, v_pointer);
} else {
elm_radio_group_add(radio, s_info.radio_group);
}
elm_radio_state_value_set(radio, radio_id++);
evas_object_size_hint_align_set(radio, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(radio, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_object_part_content_set(parent, part_name, radio);
evas_object_show(radio);
return radio;
}
/**
* @brief Destroys window and frees its resources.
*/
void view_destroy(void)
{
if (s_info.win == NULL)
return;
evas_object_del(s_info.win);
}
/**
* @brief Internal callback function invoked when the main window needs to be destroyed.
* @param[in] data The user data passed to the evas_object_smart_callback_add() function.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info)
{
ui_app_exit();
}
/**
* @brief Internal callback function invoked when the HW Back button is pressed.
* @param[in] data The user data passed to the eext_object_event_callback_add() function.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info)
{
/* Let window go to hide state. */
elm_win_lower(s_info.win);
}
/**
* @brief Internal callback function invoked when the 'Close' button is pressed
* while popup window is visible.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _popup_close_cb(void *data, Evas_Object *obj, void *event_info)
{
s_info.radio_group = NULL;
evas_object_del((Evas_Object *)data);
}
/**
* @brief Internal callback function invoked when the 'OK' button is pressed
* while popup window is visible.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _popup_level_accept_cb(void *data, Evas_Object *obj, void *event_info)
{
if (s_info.level != s_info.radio_selector) {
if (s_info.radio_selector == 0)
_change_size(PUZZLE_SIZE_LEVEL_4);
else if (s_info.radio_selector == 1)
_change_size(PUZZLE_SIZE_LEVEL_5);
s_info.level = s_info.radio_selector;
}
_popup_close_cb(data, obj, event_info);
}
/**
* @brief Internal callback function invoked when the 'answer' icon is pressed.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] e The evas surface of the object.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _answer_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
Evas_Object *popup;
int width = 0;
popup = view_create_popup("Answer", "Close", _popup_close_cb, NULL, NULL);
if (!popup)
return;
elm_win_screen_size_get(s_info.win, NULL, NULL, &width, NULL);
if (!view_create_image(popup, IMG_SMILE, "default", width * IMAGE_PREVIEW_SIZE, width * IMAGE_PREVIEW_SIZE)) {
evas_object_del(popup);
return;
}
evas_object_show(popup);
}
/**
* @brief Internal callback function invoked when the 'level' icon is pressed.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] e The evas surface of the object.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _level_icon_click_cb(void* data, Evas *e, Evas_Object *obj, void *event_info)
{
Evas_Object *popup;
Evas_Object *layout;
popup = view_create_popup("Level", "OK", _popup_level_accept_cb, "Close", _popup_close_cb);
if (!popup)
return;
layout = view_create_layout(popup, EDJ_MAIN, GRP_POPUP, "default");
if (!layout) {
evas_object_del(popup);
return;
}
view_create_radio(layout, PART_POPUP_ITEM_1, &s_info.radio_selector);
view_create_radio(layout, PART_POPUP_ITEM_2, &s_info.radio_selector);
evas_object_show(popup);
}
/**
* @brief Internal callback function invoked when the 'shuffle' icon is pressed.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] e The evas surface of the object.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _shuffle_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
Ecore_Animator *animator;
if (s_info.shuffling)
return;
s_info.shuffle_count = 0;
s_info.shuffling = true;
animator = ecore_animator_add(_shuffle_cb, NULL);
}
/**
* @brief Internal callback function invoked when one of the navigation buttons is pressed.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
static void _navigation_button_cb(void *data, Evas_Object *obj, void *event_info)
{
navi_button_e nb_type = (navi_button_e)data;
if (!_check_puzzle_move(nb_type))
return;
switch (nb_type) {
case NB_UP:
_move_puzzle(s_info.size);
break;
case NB_DOWN:
_move_puzzle(-s_info.size);
break;
case NB_LEFT:
_move_puzzle(1);
break;
case NB_RIGHT:
_move_puzzle(-1);
break;
default:
dlog_print(DLOG_WARN, LOG_TAG, "Unknown navigation button type.");
}
}
/**
* @brief Internal callback function invoked when the mouse button is pressed
* over the puzzle piece.
* This function performs selected piece highlighting.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] e The evas surface of the object.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
void _mouse_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
Evas_Object *piece = obj;
int pos = (int)evas_object_data_get(piece, "position");
int r, g, b, a;
if (pos == (int)evas_object_data_get(s_info.piece[s_info.white_piece], "position"))
return;
evas_object_color_get(piece, &r, &g, &b, &a);
evas_object_color_set(piece, r, g, b, 200);
evas_object_show(piece);
}
/**
* @brief Internal callback function invoked when the mouse button is released
* from the puzzle piece.
* This function performs selected piece movement to the white space.
* @param[in] data The user data passed to the callback attachment function.
* @param[in] e The evas surface of the object.
* @param[in] obj The object invoking this callback function.
* @param[in] event_info The structure containing the information on this event.
*/
void _mouse_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
Evas_Object *piece = obj;
int pos = (int)evas_object_data_get(piece, "position");
if (pos == (int)evas_object_data_get(s_info.piece[s_info.white_piece], "position"))
return;
evas_object_color_set(piece, 255, 255, 255, 255);
if (pos - s_info.size == s_info.white_piece)
_navigation_button_cb((void *)NB_UP, NULL, NULL);
else if (pos - 1 == s_info.white_piece && ((s_info.white_piece + 1) % s_info.size))
_navigation_button_cb((void *)NB_LEFT, NULL, NULL);
else if (pos + 1 == s_info.white_piece && (s_info.white_piece % s_info.size))
_navigation_button_cb((void *)NB_RIGHT, NULL, NULL);
else if (pos + s_info.size == s_info.white_piece)
_navigation_button_cb((void *)NB_DOWN, NULL, NULL);
evas_object_show(piece);
}
/**
* @brief Internal callback function invoked on pieces shuffling animation.
* @param[in] data The user data passed to the callback attachment function.
*/
static Eina_Bool _shuffle_cb(void *data)
{
navi_button_e nb_type = (navi_button_e)(rand() % 4);
s_info.shuffle_count++;
if (!_check_puzzle_move(nb_type))
s_info.shuffle_count--;
else
_navigation_button_cb((void *)nb_type, NULL, NULL);
if (s_info.shuffle_count < MAX_SHUFFLE_COUNT) {
return ECORE_CALLBACK_RENEW;
} else {
s_info.shuffling = false;
return ECORE_CALLBACK_CANCEL;
}
}
/**
* @brief Internal function which creates fully qualified path to the provided resource file.
* @param[in] edj_file_in The file name.
* @param[out] edj_path_out The fully qualified path to the edj_file_in file.
*/
static void _get_app_resource(const char *edj_file_in, char *edj_path_out)
{
char *res_path = app_get_resource_path();
if (res_path) {
snprintf(edj_path_out, PATH_MAX, "%s%s", res_path, edj_file_in);
free(res_path);
}
}
/**
* @brief Internal function which creates the main layout.
* @return This function returns 'true' if the main layout was created successfully,
* otherwise 'false' is returned.
*/
static bool _create_main_layout(void)
{
Evas_Object *layout = NULL;
layout = view_create_layout(s_info.conform, EDJ_MAIN, GRP_MAIN, "elm.swallow.content");
if (!layout)
return false;
s_info.e = evas_object_evas_get(layout);
if (!s_info.e)
return false;
if (!_create_menu(layout))
return false;
if (!_create_navigation(layout))
return false;
_create_puzzle(PUZZLE_SIZE_LEVEL_4);
return true;
}
/**
* @brief Internal function which creates the top menu layout.
* The menu consists of three icons (buttons): answer preview, level selection, puzzle shuffle.
* @return This function returns 'true' if the menu layout was created successfully,
* otherwise 'false' is returned.
*/
static bool _create_menu(Evas_Object *parent)
{
Evas_Object *layout;
Evas_Object *answer;
Evas_Object *level;
Evas_Object *shuffle;
layout = view_create_layout(parent, EDJ_MAIN, GRP_MENU, PART_MAIN_MENU);
if (!layout)
return false;
answer = view_create_icon(layout, "no_photo", PART_MENU_ANSWER);
if (!answer)
return false;
level = view_create_icon(layout, "file", PART_MENU_LEVEL);
if (!level)
return false;
shuffle = view_create_icon(layout, "refresh", PART_MENU_SHUFFLE);
if (!shuffle)
return false;
evas_object_event_callback_add(answer, EVAS_CALLBACK_MOUSE_UP, _answer_icon_click_cb, NULL);
evas_object_event_callback_add(level, EVAS_CALLBACK_MOUSE_UP, _level_icon_click_cb, NULL);
evas_object_event_callback_add(shuffle, EVAS_CALLBACK_MOUSE_UP, _shuffle_icon_click_cb, NULL);
return true;
}
/**
* @brief Internal function which creates the navigation layout.
* The navigation consists of four buttons for puzzle pieces moving in four directions.
* @return This function returns 'true' if the navigation layout was created successfully,
* otherwise 'false' is returned.
*/
static bool _create_navigation(Evas_Object *parent)
{
Evas_Object *layout = view_create_layout(parent, EDJ_MAIN, GRP_NAVI, PART_MAIN_NAVI);
if (!layout)
return false;
return (view_create_button(layout, "UP", PART_NAVI_UP, _navigation_button_cb, (void *)NB_UP) &&
view_create_button(layout, "DOWN", PART_NAVI_DOWN, _navigation_button_cb, (void *)NB_DOWN) &&
view_create_button(layout, "LEFT", PART_NAVI_LEFT, _navigation_button_cb, (void *)NB_LEFT) &&
view_create_button(layout, "RIGHT", PART_NAVI_RIGHT, _navigation_button_cb, (void *)NB_RIGHT));
}
/**
* @brief Internal function which creates the puzzle image area.
*/
static void _create_puzzle(int level)
{
char file_path[PATH_MAX] = {0,};
int x, y;
int extract_region_width, extract_region_height;
int puzzle_width, puzzle_height;
int i;
int ret;
_get_app_resource(IMG_SMILE, file_path);
s_info.full_image_width = s_info.screen_width - (6 + 6);
s_info.full_image_height = (s_info.screen_height * PUZZLE_RATIO) - (6 + 6);
s_info.puzzle_start_x = 6;
s_info.puzzle_start_y = (s_info.screen_height * MENU_RATIO) + 6;
for (i = 0; i < PIECE_COUNT; i++) {
s_info.piece[i] = evas_object_image_filled_add(s_info.e);
s_info.piece_pos[i] = i;
evas_object_image_file_set(s_info.piece[i], file_path, NULL);
ret = evas_object_image_load_error_get(s_info.piece[i]);
if (ret != EVAS_LOAD_ERROR_NONE)
dlog_print(DLOG_ERROR, LOG_TAG, "Failed to load image");
evas_object_data_set(s_info.piece[i], "position", (void *)i);
}
evas_object_image_size_get(s_info.piece[PIECE_COUNT - 1], &s_info.origin_image_width, &s_info.origin_image_height);
extract_region_width = s_info.origin_image_width / level;
extract_region_height = s_info.origin_image_height / level;
puzzle_width = s_info.full_image_width / level;
puzzle_height = s_info.full_image_height / level;
for (y = 0; y < level; y++)
for (x = 0; x < level; x++) {
evas_object_move(s_info.piece[y * level + x], s_info.puzzle_start_x + x * (2 + puzzle_width), s_info.puzzle_start_y + y * (2 + puzzle_height));
evas_object_image_load_region_set(s_info.piece[y * level + x], x * extract_region_width, y * extract_region_height, extract_region_width, extract_region_height);
evas_object_resize(s_info.piece[y * level + x], puzzle_width, puzzle_height);
evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, NULL);
evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, NULL);
if (y == level - 1 && x == level - 1) {
evas_object_color_get(s_info.piece[y * level + x], &s_info.r, &s_info.g, &s_info.b, &s_info.a);
evas_object_color_set(s_info.piece[y * level + x], s_info.r, s_info.g, s_info.b, 0);
s_info.white_piece = y * level + x;
}
evas_object_show(s_info.piece[y * level + x]);
}
s_info.size = level;
s_info.start = 0;
}
/**
* @brief Internal function which moves the puzzle piece.
* @param[in] offset The number of cells to move the current piece. In order to
* specify a valid value, the puzzle image must be considered as a continuous
* list (array) of cells. To move the current cell:
* - down/up - the offset equals to +/-row_length;
* - right/left - the offset equals to +/-1.
*/
static void _move_puzzle(int offset)
{
int x1, y1, w1, h1;
int x2, y2, w2, h2;
int temp;
int white_piece = s_info.white_piece;
evas_object_image_load_region_get(s_info.piece[white_piece + offset], &x1, &y1, &w1, &h1);
evas_object_image_load_region_get(s_info.piece[white_piece], &x2, &y2, &w2, &h2);
evas_object_image_load_region_set(s_info.piece[white_piece + offset], x2, y2, w2, h2);
evas_object_image_load_region_set(s_info.piece[white_piece], x1, y1, w1, h1);
evas_object_color_set(s_info.piece[white_piece], s_info.r, s_info.g, s_info.b, s_info.a);
evas_object_color_set(s_info.piece[white_piece + offset], s_info.r, s_info.g, s_info.b, 0);
evas_object_show(s_info.piece[white_piece]);
evas_object_show(s_info.piece[white_piece + offset]);
temp = s_info.piece_pos[white_piece];
s_info.piece_pos[white_piece] = s_info.piece_pos[white_piece + offset];
s_info.piece_pos[white_piece + offset] = temp;
s_info.white_piece = white_piece + offset;
if (s_info.piece_pos[s_info.white_piece] == s_info.size * s_info.size - 1 && s_info.start == 1)
_finished();
}
/**
* @brief Internal function which checks whether the current puzzle can be moved
* with the provided button type.
* @param[in] nb_type The type of the navigation button to check against.
* @return This function returns 'true' if the current piece can be moved using
* the provided button type, otherwise 'false' is returned.
*/
static bool _check_puzzle_move(navi_button_e nb_type)
{
switch (nb_type) {
case NB_UP:
return (s_info.white_piece + s_info.size < s_info.size * s_info.size);
case NB_DOWN:
return (s_info.white_piece - s_info.size >= 0);
case NB_LEFT:
return ((s_info.white_piece + 1) % s_info.size > 0);
case NB_RIGHT:
return (s_info.white_piece % s_info.size > 0);
default:
return false;
}
}
/**
* @brief Internal function which changes the puzzle size and recalculates all the
* parameters and coordinates of each puzzle piece.
* @param[in] level The number of pieces along one edge.
*/
static void _change_size(int level)
{
char file_path[PATH_MAX] = {0,};
int x, y;
int extract_region_width, extract_region_height;
int puzzle_width, puzzle_height;
int i;
_get_app_resource(IMG_SMILE, file_path);
for (i = 0; i < PIECE_COUNT; i++) {
evas_object_color_set(s_info.piece[i], s_info.r, s_info.g, s_info.b, 0);
evas_object_show(s_info.piece[i]);
evas_object_move(s_info.piece[i], 0, 0);
}
extract_region_width = s_info.origin_image_width / level;
extract_region_height = s_info.origin_image_height / level;
puzzle_width = s_info.full_image_width / level;
puzzle_height = s_info.full_image_height / level;
for (y = 0; y < level; y++)
for (x = 0; x < level; x++) {
evas_object_color_set(s_info.piece[y * level + x], s_info.r, s_info.g, s_info.b, 255);
evas_object_image_file_set(s_info.piece[y * level + x], file_path, NULL);
evas_object_move(s_info.piece[y * level + x], s_info.puzzle_start_x + x * (2 + puzzle_width), s_info.puzzle_start_y + y * (2 + puzzle_height));
evas_object_image_load_region_set(s_info.piece[y * level + x], x * extract_region_width, y * extract_region_height, extract_region_width, extract_region_height);
evas_object_resize(s_info.piece[y * level + x], puzzle_width, puzzle_height);
evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, NULL);
evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, NULL);
if (y == (level - 1) && x == (level - 1)) {
evas_object_color_get(s_info.piece[y * level + x], &s_info.r, &s_info.g, &s_info.b, &s_info.a);
evas_object_color_set(s_info.piece[y * level + x], s_info.r, s_info.g, s_info.b, 0);
s_info.white_piece = y * level + x;
}
evas_object_show(s_info.piece[y * level + x]);
}
s_info.size = level;
s_info.start = 0;
}
/**
* @brief Internal function which validates the pieces arrangement correctness
* and displays the final popup window if the validation is passed.
*/
static void _finished(void)
{
Evas_Object *popup = NULL;
int width = 0;
int i;
for (i = 0; i < s_info.size * s_info.size; i++)
if (s_info.piece_pos[i] != i)
return;
popup = view_create_popup("Correct !!!", "OK", _popup_close_cb, NULL, NULL);
if (!popup)
return;
elm_win_screen_size_get(s_info.win, NULL, NULL, &width, NULL);
if (!view_create_image(popup, IMG_CORRECT, "default", width * IMAGE_PREVIEW_SIZE, width * IMAGE_PREVIEW_SIZE)) {
evas_object_del(popup);
return;
}
evas_object_show(popup);
s_info.start = 0;
}