Exemple #1
0
static void hat_widget(struct nk_context* nk, unsigned char state)
{
    float radius;
    struct nk_rect area;
    struct nk_vec2 center;

    if (nk_widget(&area, nk) != NK_WIDGET_VALID)
        return;

    center = nk_vec2(area.x + area.w / 2.f, area.y + area.h / 2.f);
    radius = NK_MIN(area.w, area.h) / 2.f;

    nk_stroke_circle(nk_window_get_canvas(nk),
                     nk_rect(center.x - radius,
                             center.y - radius,
                             radius * 2.f,
                             radius * 2.f),
                     1.f,
                     nk_rgb(175, 175, 175));

    if (state)
    {
        const float angles[] =
        {
            0.f,           0.f,
            NK_PI * 1.5f,  NK_PI * 1.75f,
            NK_PI,         0.f,
            NK_PI * 1.25f, 0.f,
            NK_PI * 0.5f,  NK_PI * 0.25f,
            0.f,           0.f,
            NK_PI * 0.75f, 0.f,
        };
        const float cosa = nk_cos(angles[state]);
        const float sina = nk_sin(angles[state]);
        const struct nk_vec2 p0 = nk_vec2(0.f, -radius);
        const struct nk_vec2 p1 = nk_vec2( radius / 2.f, -radius / 3.f);
        const struct nk_vec2 p2 = nk_vec2(-radius / 2.f, -radius / 3.f);

        nk_fill_triangle(nk_window_get_canvas(nk),
                            center.x + cosa * p0.x + sina * p0.y,
                            center.y + cosa * p0.y - sina * p0.x,
                            center.x + cosa * p1.x + sina * p1.y,
                            center.y + cosa * p1.y - sina * p1.x,
                            center.x + cosa * p2.x + sina * p2.y,
                            center.y + cosa * p2.y - sina * p2.x,
                            nk_rgb(175, 175, 175));
    }
}
static void
canvas_begin(struct nk_context *ctx, struct nk_canvas *canvas, nk_flags flags,
    uint32_t x, uint32_t y, uint32_t width, uint32_t height, struct nk_color background_color){
    /* save style properties which will be overwritten */
    canvas->panel_padding = ctx->style.window.padding;
    canvas->item_spacing = ctx->style.window.spacing;
    canvas->window_background = ctx->style.window.fixed_background;
    
    /* use the complete window space and set background */
    ctx->style.window.spacing = nk_vec2(0,0);
    ctx->style.window.padding = nk_vec2(0,0);
    ctx->style.window.fixed_background = nk_style_item_color(background_color);
    
    /* create/update window and set position + size */
    flags = flags & ~NK_WINDOW_DYNAMIC;
    nk_begin(ctx, "Window", nk_rect(x, y, width, height), NK_WINDOW_NO_SCROLLBAR|flags);
    //nk_window_set_bounds(ctx, nk_rect(x, y, width, height));
   
    /* allocate the complete window space for drawing */
    {struct nk_rect total_space;
    total_space = nk_window_get_content_region(ctx);
    nk_layout_row_dynamic(ctx, total_space.h, 1);
    nk_widget(&total_space, ctx);
    canvas->painter = nk_window_get_canvas(ctx);}
     
}
/* ===============================================================
 *
 *                          CUSTOM WIDGET
 *
 * ===============================================================*/
static int
ui_piemenu(struct nk_context *ctx, struct nk_vec2 pos, float radius,
            struct nk_image *icons, int item_count)
{
    int ret = -1;
    struct nk_rect total_space;
    struct nk_rect bounds;
    int active_item = 0;

    /* pie menu popup */
    struct nk_color border = ctx->style.window.border_color;
    struct nk_style_item background = ctx->style.window.fixed_background;
    ctx->style.window.fixed_background = nk_style_item_hide();
    ctx->style.window.border_color = nk_rgba(0,0,0,0);

    total_space  = nk_window_get_content_region(ctx);
    ctx->style.window.spacing = nk_vec2(0,0);
    ctx->style.window.padding = nk_vec2(0,0);

    if (nk_popup_begin(ctx, NK_POPUP_STATIC, "piemenu", NK_WINDOW_NO_SCROLLBAR,
        nk_rect(pos.x - total_space.x - radius, pos.y - radius - total_space.y,
        2*radius,2*radius)))
    {
        int i = 0;
        struct nk_command_buffer* out = nk_window_get_canvas(ctx);
        const struct nk_input *in = &ctx->input;

        total_space = nk_window_get_content_region(ctx);
        ctx->style.window.spacing = nk_vec2(4,4);
        ctx->style.window.padding = nk_vec2(8,8);
        nk_layout_row_dynamic(ctx, total_space.h, 1);
        nk_widget(&bounds, ctx);

        /* outer circle */
        nk_fill_circle(out, bounds, nk_rgb(50,50,50));
        {
            /* circle buttons */
            float step = (2 * 3.141592654f) / (float)(MAX(1,item_count));
            float a_min = 0; float a_max = step;

            struct nk_vec2 center = nk_vec2(bounds.x + bounds.w / 2.0f, bounds.y + bounds.h / 2.0f);
            struct nk_vec2 drag = nk_vec2(in->mouse.pos.x - center.x, in->mouse.pos.y - center.y);
            float angle = (float)atan2(drag.y, drag.x);
            if (angle < -0.0f) angle += 2.0f * 3.141592654f;
            active_item = (int)(angle/step);

            for (i = 0; i < item_count; ++i) {
                struct nk_rect content;
                float rx, ry, dx, dy, a;
                nk_fill_arc(out, center.x, center.y, (bounds.w/2.0f),
                    a_min, a_max, (active_item == i) ? nk_rgb(45,100,255): nk_rgb(60,60,60));

                /* separator line */
                rx = bounds.w/2.0f; ry = 0;
                dx = rx * (float)cos(a_min) - ry * (float)sin(a_min);
                dy = rx * (float)sin(a_min) + ry * (float)cos(a_min);
                nk_stroke_line(out, center.x, center.y,
                    center.x + dx, center.y + dy, 1.0f, nk_rgb(50,50,50));

                /* button content */
                a = a_min + (a_max - a_min)/2.0f;
                rx = bounds.w/2.5f; ry = 0;
                content.w = 30; content.h = 30;
                content.x = center.x + ((rx * (float)cos(a) - ry * (float)sin(a)) - content.w/2.0f);
                content.y = center.y + (rx * (float)sin(a) + ry * (float)cos(a) - content.h/2.0f);
                nk_draw_image(out, content, &icons[i], nk_rgb(255,255,255));
                a_min = a_max; a_max += step;
            }
        }
        {
            /* inner circle */
            struct nk_rect inner;
            inner.x = bounds.x + bounds.w/2 - bounds.w/4;
            inner.y = bounds.y + bounds.h/2 - bounds.h/4;
            inner.w = bounds.w/2; inner.h = bounds.h/2;
            nk_fill_circle(out, inner, nk_rgb(45,45,45));

            /* active icon content */
            bounds.w = inner.w / 2.0f;
            bounds.h = inner.h / 2.0f;
            bounds.x = inner.x + inner.w/2 - bounds.w/2;
            bounds.y = inner.y + inner.h/2 - bounds.h/2;
            nk_draw_image(out, bounds, &icons[active_item], nk_rgb(255,255,255));
        }
        nk_layout_space_end(ctx);
        if (!nk_input_is_mouse_down(&ctx->input, NK_BUTTON_RIGHT)) {
            nk_popup_close(ctx);
            ret = active_item;
        }
    } else ret = -2;
    ctx->style.window.spacing = nk_vec2(4,4);
    ctx->style.window.padding = nk_vec2(8,8);
    nk_popup_end(ctx);

    ctx->style.window.fixed_background = background;
    ctx->style.window.border_color = border;
    return ret;
}
    CharacterDialoguePopup::UpdateResult CharacterDialoguePopup::update(struct nk_context* ctx)
    {
        auto renderer = FARender::Renderer::get();

        auto boxTex = renderer->loadImage(this->mWide ? "data/textbox.cel" : "data/textbox2.cel");
        int32_t screenW, screenH;
        renderer->getWindowDimensions(screenW, screenH);

        nk_flags flags = NK_WINDOW_NO_SCROLLBAR;

        auto dialogRectangle =
            nk_rect(screenW / 2.0f - (boxTex->getWidth() / 2.0f), screenH / 2.0f - (boxTex->getHeight() / 2.0f), boxTex->getWidth(), boxTex->getHeight());

        UpdateResult result = UpdateResult::DoNothing;

        mGuiManager.nk_fa_begin_image_window(ctx,
                                             "dialog",
                                             dialogRectangle,
                                             flags,
                                             boxTex->getNkImage(),
                                             [&]() {
                                                 // apply black checkerboard in background
                                                 {
                                                     auto blackTex = renderer->loadImage("resources/black.png");
                                                     auto cbRect =
                                                         nk_rect(dialogRectangle.x + 3, dialogRectangle.y + 3, dialogRectangle.w - 6, dialogRectangle.h - 6);

                                                     ScopedApplyEffect effect(ctx, EffectType::checkerboarded);
                                                     auto nkImage =
                                                         nk_subimage_handle(blackTex->getNkImage().handle, blackTex->getWidth(), blackTex->getHeight(), cbRect);
                                                     nk_draw_image(nk_window_get_canvas(ctx), cbRect, &nkImage, nk_rgb(0, 0, 0));
                                                 }

                                                 nk_layout_row_dynamic(ctx, 30, 1);

                                                 DialogData data = getDialogData();

                                                 for (const auto& line : data.introduction)
                                                     GuiManager::smallText(ctx, line.c_str());

                                                 // fill the rest of the window
                                                 struct nk_rect bounds = nk_widget_bounds(ctx);
                                                 struct nk_rect panelSize = nk_window_get_bounds(ctx);
                                                 float contentHeight = panelSize.h + panelSize.y - bounds.y;
                                                 nk_layout_row_dynamic(ctx, contentHeight, 1);

                                                 Misc::ScopedSetter<nk_style> styleSetter(ctx->style);

                                                 // setup style for scrollbar
                                                 {
                                                     constexpr int32_t scrollUpButtonIndex = 9;
                                                     constexpr int32_t scrollDownButtonIndex = 8;
                                                     constexpr int32_t scrollbarBackgroundIndex = 13;

                                                     // TODO: the "cursor" of the scrollbar is still the default nuklear look, which should change

                                                     auto sliderImg = renderer->loadImage("data/textslid.cel");

                                                     // TODO: this image should repeat, not stretch
                                                     ctx->style.scrollv.normal = nk_style_item_image(sliderImg->getNkImage(scrollbarBackgroundIndex));

                                                     ctx->style.scrollv.active = ctx->style.scrollv.normal;
                                                     ctx->style.scrollv.hover = ctx->style.scrollv.normal;
                                                     ctx->style.scrollv.cursor_active = ctx->style.scrollv.cursor_normal;
                                                     ctx->style.scrollv.cursor_hover = ctx->style.scrollv.cursor_normal;

                                                     ctx->style.scrollv.show_buttons = true;

                                                     ctx->style.scrollv.dec_button.normal = nk_style_item_image(sliderImg->getNkImage(scrollUpButtonIndex));
                                                     ctx->style.scrollv.dec_symbol = NK_SYMBOL_NONE;
                                                     ctx->style.scrollv.inc_button.normal = nk_style_item_image(sliderImg->getNkImage(scrollDownButtonIndex));
                                                     ctx->style.scrollv.inc_symbol = NK_SYMBOL_NONE;

                                                     ctx->style.scrollv.dec_button.active = ctx->style.scrollv.dec_button.normal;
                                                     ctx->style.scrollv.dec_button.hover = ctx->style.scrollv.dec_button.normal;

                                                     ctx->style.scrollv.inc_button.active = ctx->style.scrollv.inc_button.normal;
                                                     ctx->style.scrollv.inc_button.hover = ctx->style.scrollv.inc_button.normal;

                                                     ctx->style.window.group_padding = nk_vec2(10, 10);

                                                     ctx->style.window.scrollbar_size = nk_vec2(sliderImg->getWidth(), 0);
                                                 }

                                                 nk_group_scrolled_begin(ctx, &mScroll, "dialog_main", 0);
                                                 {
                                                     if (mDialogMenu.update(ctx, data.dialogOptions, mScroll) == MouseAndClickMenu::Result::Activated)
                                                         result = data.dialogActions[mDialogMenu.getSelectedIndex()]();
                                                 }
                                                 nk_group_scrolled_end(ctx);

                                             },
                                             true);

        return result;
    }
static int
node_editor(struct nk_context *ctx)
{
    int n = 0;
    struct nk_rect total_space;
    const struct nk_input *in = &ctx->input;
    struct nk_command_buffer *canvas;
    struct node *updated = 0;
    struct node_editor *nodedit = &nodeEditor;

    if (!nodeEditor.initialized) {
        node_editor_init(&nodeEditor);
        nodeEditor.initialized = 1;
    }

    if (nk_begin(ctx, "NodeEdit", nk_rect(0, 0, 800, 600),
        NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE))
    {
        /* allocate complete window space */
        canvas = nk_window_get_canvas(ctx);
        total_space = nk_window_get_content_region(ctx);
        nk_layout_space_begin(ctx, NK_STATIC, total_space.h, nodedit->node_count);
        {
            struct node *it = nodedit->begin;
            struct nk_rect size = nk_layout_space_bounds(ctx);
            struct nk_panel *node = 0;

            if (nodedit->show_grid) {
                /* display grid */
                float x, y;
                const float grid_size = 32.0f;
                const struct nk_color grid_color = nk_rgb(50, 50, 50);
                for (x = (float)fmod(size.x - nodedit->scrolling.x, grid_size); x < size.w; x += grid_size)
                    nk_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, 1.0f, grid_color);
                for (y = (float)fmod(size.y - nodedit->scrolling.y, grid_size); y < size.h; y += grid_size)
                    nk_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, 1.0f, grid_color);
            }

            /* execute each node as a movable group */
            while (it) {
                /* calculate scrolled node window position and size */
                nk_layout_space_push(ctx, nk_rect(it->bounds.x - nodedit->scrolling.x,
                    it->bounds.y - nodedit->scrolling.y, it->bounds.w, it->bounds.h));

                /* execute node window */
                if (nk_group_begin(ctx, it->name, NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE))
                {
                    /* always have last selected node on top */

                    node = nk_window_get_panel(ctx);
                    if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, node->bounds) &&
                        (!(it->prev && nk_input_mouse_clicked(in, NK_BUTTON_LEFT,
                        nk_layout_space_rect_to_screen(ctx, node->bounds)))) &&
                        nodedit->end != it)
                    {
                        updated = it;
                    }

                    /* ================= NODE CONTENT =====================*/
                    nk_layout_row_dynamic(ctx, 25, 1);
                    nk_button_color(ctx, it->color);
                    it->color.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, it->color.r, 255, 1,1);
                    it->color.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, it->color.g, 255, 1,1);
                    it->color.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, it->color.b, 255, 1,1);
                    it->color.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, it->color.a, 255, 1,1);
                    /* ====================================================*/
                    nk_group_end(ctx);
                }
                {
                    /* node connector and linking */
                    float space;
                    struct nk_rect bounds;
                    bounds = nk_layout_space_rect_to_local(ctx, node->bounds);
                    bounds.x += nodedit->scrolling.x;
                    bounds.y += nodedit->scrolling.y;
                    it->bounds = bounds;

                    /* output connector */
                    space = node->bounds.h / (float)((it->output_count) + 1);
                    for (n = 0; n < it->output_count; ++n) {
                        struct nk_rect circle;
                        circle.x = node->bounds.x + node->bounds.w-4;
                        circle.y = node->bounds.y + space * (float)(n+1);
                        circle.w = 8; circle.h = 8;
                        nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100));

                        /* start linking process */
                        if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true)) {
                            nodedit->linking.active = nk_true;
                            nodedit->linking.node = it;
                            nodedit->linking.input_id = it->ID;
                            nodedit->linking.input_slot = n;
                        }

                        /* draw curve from linked node slot to mouse position */
                        if (nodedit->linking.active && nodedit->linking.node == it &&
                            nodedit->linking.input_slot == n) {
                            struct nk_vec2 l0 = nk_vec2(circle.x + 3, circle.y + 3);
                            struct nk_vec2 l1 = in->mouse.pos;
                            nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y,
                                l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100));
                        }
                    }

                    /* input connector */
                    space = node->bounds.h / (float)((it->input_count) + 1);
                    for (n = 0; n < it->input_count; ++n) {
                        struct nk_rect circle;
                        circle.x = node->bounds.x-4;
                        circle.y = node->bounds.y + space * (float)(n+1);
                        circle.w = 8; circle.h = 8;
                        nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100));
                        if (nk_input_is_mouse_released(in, NK_BUTTON_LEFT) &&
                            nk_input_is_mouse_hovering_rect(in, circle) &&
                            nodedit->linking.active && nodedit->linking.node != it) {
                            nodedit->linking.active = nk_false;
                            node_editor_link(nodedit, nodedit->linking.input_id,
                                nodedit->linking.input_slot, it->ID, n);
                        }
                    }
                }
                it = it->next;
            }

            /* reset linking connection */
            if (nodedit->linking.active && nk_input_is_mouse_released(in, NK_BUTTON_LEFT)) {
                nodedit->linking.active = nk_false;
                nodedit->linking.node = NULL;
                fprintf(stdout, "linking failed\n");
            }

            /* draw each link */
            for (n = 0; n < nodedit->link_count; ++n) {
                struct node_link *link = &nodedit->links[n];
                struct node *ni = node_editor_find(nodedit, link->input_id);
                struct node *no = node_editor_find(nodedit, link->output_id);
                float spacei = node->bounds.h / (float)((ni->output_count) + 1);
                float spaceo = node->bounds.h / (float)((no->input_count) + 1);
                struct nk_vec2 l0 = nk_layout_space_to_screen(ctx,
                    nk_vec2(ni->bounds.x + ni->bounds.w, 3.0f + ni->bounds.y + spacei * (float)(link->input_slot+1)));
                struct nk_vec2 l1 = nk_layout_space_to_screen(ctx,
                    nk_vec2(no->bounds.x, 3.0f + no->bounds.y + spaceo * (float)(link->output_slot+1)));

                l0.x -= nodedit->scrolling.x;
                l0.y -= nodedit->scrolling.y;
                l1.x -= nodedit->scrolling.x;
                l1.y -= nodedit->scrolling.y;
                nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y,
                    l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100));
            }

            if (updated) {
                /* reshuffle nodes to have least recently selected node on top */
                node_editor_pop(nodedit, updated);
                node_editor_push(nodedit, updated);
            }

            /* node selection */
            if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nk_layout_space_bounds(ctx))) {
                it = nodedit->begin;
                nodedit->selected = NULL;
                nodedit->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200);
                while (it) {
                    struct nk_rect b = nk_layout_space_rect_to_screen(ctx, it->bounds);
                    b.x -= nodedit->scrolling.x;
                    b.y -= nodedit->scrolling.y;
                    if (nk_input_is_mouse_hovering_rect(in, b))
                        nodedit->selected = it;
                    it = it->next;
                }
            }

            /* contextual menu */
            if (nk_contextual_begin(ctx, 0, nk_vec2(100, 220), nk_window_get_bounds(ctx))) {
                const char *grid_option[] = {"Show Grid", "Hide Grid"};
                nk_layout_row_dynamic(ctx, 25, 1);
                if (nk_contextual_item_label(ctx, "New", NK_TEXT_CENTERED))
                    node_editor_add(nodedit, "New", nk_rect(400, 260, 180, 220),
                            nk_rgb(255, 255, 255), 1, 2);
                if (nk_contextual_item_label(ctx, grid_option[nodedit->show_grid],NK_TEXT_CENTERED))
                    nodedit->show_grid = !nodedit->show_grid;
                nk_contextual_end(ctx);
            }
        }
        nk_layout_space_end(ctx);

        /* window content scrolling */
        if (nk_input_is_mouse_hovering_rect(in, nk_window_get_bounds(ctx)) &&
            nk_input_is_mouse_down(in, NK_BUTTON_MIDDLE)) {
            nodedit->scrolling.x += in->mouse.delta.x;
            nodedit->scrolling.y += in->mouse.delta.y;
        }
    }
    nk_end(ctx);
    return !nk_window_is_closed(ctx, "NodeEdit");
}
Exemple #6
0
int main(int argc, char** argv)
{
    if (argc > 2)
        message_and_abort_fmt("Usage: %s [filename]", argv[0]);

    Render::RenderSettings renderSettings;
    renderSettings.windowWidth = 800;
    renderSettings.windowHeight = 600;
    renderSettings.fullscreen = false;

    NuklearMisc::StandaloneGuiHandler guiHandler("Cel Viewer", renderSettings);

    nk_context* ctx = guiHandler.getNuklearContext();

    Settings::Settings settings;
    settings.loadFromFile("resources/celview.ini");

    bool faioInitDone = false;
    std::string listFile = settings.get<std::string>("celview", "listFile", "Diablo I.txt");
    std::string mpqFile = settings.get<std::string>("celview", "mpqFile", "DIABDAT.MPQ");

    std::vector<std::string> celFiles;

    std::string selectedImage = "";
    std::unique_ptr<NuklearMisc::GuiSprite> image;

    std::unique_ptr<NuklearMisc::GuiSprite> nextImage;

    int animate = false;
    int32_t frame = 0;

    float rowHeight = 30;
    auto lastFrame = std::chrono::high_resolution_clock::now();

    bool quit = false;
    while (!quit)
    {
        auto now = std::chrono::high_resolution_clock::now();

        if (nextImage)
            image = std::unique_ptr<NuklearMisc::GuiSprite>(nextImage.release());

        renderSettings = Render::getWindowSize();

        if (nk_begin(ctx, "main_window", nk_rect(0, 0, renderSettings.windowWidth, renderSettings.windowHeight), NK_WINDOW_NO_SCROLLBAR))
        {
            struct nk_rect bounds = nk_window_get_content_region(ctx);

            nk_layout_row_dynamic(ctx, bounds.h, 2);

            if (nk_group_begin(ctx, "image", 0))
            {
                nk_layout_row_dynamic(ctx, rowHeight, 1);

                std::string label = selectedImage;

                if (selectedImage == "")
                    label = "No image selected";

                nk_label(ctx, label.c_str(), NK_TEXT_CENTERED);

                nk_checkbox_label(ctx, "Animate", &animate);

                if (image)
                {
                    nk_label(ctx, (boost::format("Number of Frames: %1%") % image.get()->getSprite()->size()).str().c_str(), NK_TEXT_LEFT);
                    nk_label(ctx, (boost::format("Width: %1%") % image->getSprite()->getWidth()).str().c_str(), NK_TEXT_LEFT);
                    nk_label(ctx, (boost::format("Height: %1%") % image->getSprite()->getHeight()).str().c_str(), NK_TEXT_LEFT);
                    frame = nk_propertyi(ctx, "Frame", 0, frame, image->getSprite()->size(), 1, 0.2f);

                    if (nk_button_label(ctx, "save as png"))
                    {
                        nfdchar_t* outPath = NULL;
                        nfdresult_t result = NFD_SaveDialog("png", NULL, &outPath);
                        if (result == NFD_OKAY)
                        {
                            Render::SpriteGroup::toPng(selectedImage, outPath);
                            free(outPath);
                        }
                    }

                    if (nk_button_label(ctx, "save as gif"))
                    {
                        nfdchar_t* outPath = NULL;
                        nfdresult_t result = NFD_SaveDialog("gif", NULL, &outPath);
                        if (result == NFD_OKAY)
                        {
                            Render::SpriteGroup::toGif(selectedImage, outPath);
                            free(outPath);
                        }
                    }

                    auto msSinceLastFrame = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastFrame).count();
                    if (animate && msSinceLastFrame > 100)
                    {
                        lastFrame = now;
                        frame++;
                    }

                    if (frame >= (int32_t)image.get()->getSprite()->size())
                        frame = 0;

                    Render::Sprite sprite = image.get()->getSprite()->operator[](frame);

                    int32_t w, h;
                    Render::spriteSize(sprite, w, h);

                    nk_layout_space_begin(ctx, NK_STATIC, h, 1);
                    {
                        nk_layout_space_push(ctx, nk_rect(0, 0, w, h));

                        auto canvas = nk_window_get_canvas(ctx);

                        struct nk_rect imageRect;
                        nk_widget(&imageRect, ctx);
                        nk_fill_rect(canvas, imageRect, 0.0, nk_rgb(0, 255, 0));

                        auto img = image.get()->getNkImage(frame);
                        nk_draw_image(canvas, imageRect, &img, nk_rgb(255, 255, 255));
                    }
                    nk_layout_space_end(ctx);
                }

                nk_group_end(ctx);
            }

            if (nk_group_begin(ctx, "file list", 0))
            {
                if (!faioInitDone)
                {
                    nk_layout_row_dynamic(ctx, rowHeight * 2, 1);

                    NuklearMisc::nk_file_pick(ctx, "DIABDAT.MPQ", mpqFile, "mpq,MPQ", rowHeight);
                    NuklearMisc::nk_file_pick(ctx, "Diablo listfile", listFile, "txt", rowHeight);

                    if (nk_button_label(ctx, "load"))
                    {
                        FAIO::init(mpqFile, listFile);
                        celFiles = FAIO::listMpqFiles("*.cel");
                        auto tmp = FAIO::listMpqFiles("*.cl2");
                        celFiles.insert(celFiles.end(), tmp.begin(), tmp.end());

                        std::sort(celFiles.begin(), celFiles.end());

                        settings.set<std::string>("celview", "listFile", listFile);
                        settings.set<std::string>("celview", "mpqFile", mpqFile);
                        settings.save();

                        faioInitDone = true;

                        if (argc > 1)
                        {
                            selectedImage = argv[1];
                            frame = 0;
                            nextImage = std::unique_ptr<NuklearMisc::GuiSprite>(guiHandler.getSprite(new Render::SpriteGroup(selectedImage)));
                        }
                    }
                }

                nk_layout_row_dynamic(ctx, rowHeight, 1);

                for (size_t i = 0; i < celFiles.size(); i++)
                {
                    auto buttonStyle = ctx->style.button;

                    if (selectedImage == celFiles[i])
                        buttonStyle.normal = buttonStyle.hover;

                    if (nk_button_label_styled(ctx, &buttonStyle, celFiles[i].c_str()))
                    {
                        selectedImage = celFiles[i];
                        frame = 0;
                        nextImage = std::unique_ptr<NuklearMisc::GuiSprite>(guiHandler.getSprite(new Render::SpriteGroup(selectedImage)));
                    }
                }

                nk_group_end(ctx);
            }
        }
        nk_end(ctx);

        quit = guiHandler.update();
    }

    FAIO::quit();

    return 0;
}