Exemple #1
0
static void wid_intro_create (void)
{
    if (wid_intro) {
        return;
    }

    music_play_intro();

    wid_intro = wid_new_window("intro");

    wid_set_no_shape(wid_intro);

    fpoint tl = {0.0f, 0.0f};
    fpoint br = {1.0f, 1.0f};
    wid_set_tl_br_pct(wid_intro, tl, br);

    color col = BLACK;
    col.a = 0;
    glcolor(col);

    wid_set_mode(wid_intro, WID_MODE_NORMAL);
    wid_set_color(wid_intro, WID_COLOR_TL, col);
    wid_set_color(wid_intro, WID_COLOR_BR, col);
    wid_set_color(wid_intro, WID_COLOR_BG, col);

    wid_intro_bg_create();
    wid_update(wid_intro);

    wid_move_to_pct_centered(wid_intro, 0.5f, 0.5f);
    wid_fade_in(wid_intro_title, intro_effect_delay*2);

    wid_intro_menu_create();
}
Exemple #2
0
static void wid_intro_bg_create (void)
{
    if (!wid_intro_title) {
        widp wid = wid_intro_title = wid_new_window("bg");
        fpoint tl = { 0.0, 0.0 };
        fpoint br = { 0.75, 0.15 };

        wid_set_tl_br_pct(wid, tl, br);

        wid_set_tex(wid, 0, "main_title");

        wid_lower(wid);

        color c;
        c = WHITE;
        wid_set_mode(wid, WID_MODE_NORMAL);
        wid_set_color(wid, WID_COLOR_TL, c);
        wid_set_color(wid, WID_COLOR_BR, c);
        wid_set_color(wid, WID_COLOR_BG, c);

        wid_update(wid);
        wid_move_to_pct_centered(wid_intro_title, 0.5f, -2.1f);
        wid_set_on_tick(wid, wid_intro_tick);
        wid_intro_tick_reset();
        wid_set_do_not_lower(wid, true);
    }
}
/*
 * Replace or place a tile.
 */
widp
wid_game_map_client_replace_tile (widp w, 
                                  double x, double y, 
                                  thingp t,
                                  tpp tp)
{
    tree_rootp thing_tiles;
    const char *tilename;
    tilep tile;
    widp child;

    verify(w);

    /*
     * Grow tl and br to fit the template thing. Use the first tile.
     */
    if (!tp) {
        tp = thing_tp(t);
        if (!tp) {
            ERR("no thing template to replace on client");
            return (0);
        }
    }

    if ((x < 0) || (y < 0) || (x >= MAP_WIDTH) || (y >= MAP_WIDTH)) {
        LOG("client: thing template [%s] cannot be placed at %f %f",
            tp_short_name(tp), x, y);
        return (0);
    }

    thing_tiles = tp_get_tiles(tp);
    if (!thing_tiles) {
        ERR("thing template [%s] has no tiles", tp_short_name(tp));
        return (0);
    }

    thing_tilep thing_tile;

    /*
     * Get a random tile to start with.
     */
    thing_tile = (typeof(thing_tile)) thing_tile_random(thing_tiles);

    /*
     * Find the real tile that corresponds to this name.
     */
    tilename = thing_tile_name(thing_tile);
    tile = tile_find(tilename);

    if (!tile) {
        ERR("tile name %s from thing %s not found on client",
            tilename,
            tp_short_name(tp));
        return (0);
    }

    /*
     * Make a new thing.
     */
    child = wid_new_square_button(wid_game_map_client_grid_container,
                                  "client map tile");

    wid_set_mode(child, WID_MODE_NORMAL);
    wid_set_no_shape(child);

    /*
     * "paint" the thing.
     */
    wid_game_map_client_set_thing_template(child, tp);

    if (!t) {
        t = thing_client_local_new(tp);
    }

    wid_set_thing(child, t);
    wid_set_tile(child, tile);

    double dx = 0;
    double dy = 0;

    /*
     * Does it appear as a different size on screen?
     */
    double scale = tp_get_scale(tp);

    /*
     * So we have baby and bigger slimes. But alas this is visual only and has 
     * no effect on hp on the server yet.
     */
    if (thing_is_variable_size(t)) {
        scale += gaussrand(0.0, 0.05);
    }

    if (scale != 1.0) {
        wid_scaling_blit_to_pct_in(child, scale, scale, 500, 9999999);
    }

    if (thing_is_cloud_effect(t)) {
        /*
         * The epicenter needs to be where it was on the server as we do a 
         * flood fill to see where the rest of the explosion goes.
         */
        if (!t->is_epicenter) {
            dx = gaussrand(0.0, 0.5);
            dy = gaussrand(0.0, 0.5);
        }

        wid_fade_out(child, 1000);
    }

    thing_client_wid_update(t, x + dx, y + dy, false /* smooth */,
                            true /* is new */);

    /*
     * Offset tall things
     */
    if (scale != 1.0) {
        if (thing_is_blit_y_offset(t)) {
            wid_set_blit_y_offset(child, 
                                  wid_get_height(child) * scale * -((scale - 1.0) / 2.0));
        }
    }

    /*
     * If this is a pre-existing thing perhaps being recreated ona new level
     * then it will have a direction already. Update it.
     */
    if (thing_is_animated(t)) {
        thing_animate(t);
    }

    /*
     * This adds it to the grid wid.
     */
#ifdef DEBUG_CLIENT_THING
    wid_update(child);
    char name[20];
    sprintf(name, "%d",t->thing_id);
    wid_set_text(child,name);
#endif

    /*
     * We've been told about the epicenter of an explsion, now emulate the 
     * blast.
     */
    if (t->is_epicenter && thing_is_cloud_effect(t) ) {

        if ((tp->id == THING_EXPLOSION1)        ||
            (tp->id == THING_EXPLOSION2)        ||
            (tp->id == THING_EXPLOSION3)        ||
            (tp->id == THING_EXPLOSION4)        ||
            (tp->id == THING_SMALL_EXPLOSION1)  ||
            (tp->id == THING_SMALL_EXPLOSION2)  ||
            (tp->id == THING_SMALL_EXPLOSION3)  ||
            (tp->id == THING_SMALL_EXPLOSION4)  ||
            (tp->id == THING_MED_EXPLOSION1)    ||
            (tp->id == THING_MED_EXPLOSION2)    ||
            (tp->id == THING_MED_EXPLOSION3)    ||
            (tp->id == THING_MED_EXPLOSION4)    ||
            (tp->id == THING_FIREBURST1)        ||
            (tp->id == THING_FIREBURST2)        ||
            (tp->id == THING_FIREBURST3)        ||
            (tp->id == THING_FIREBURST4)        ||
            (tp->id == THING_BOMB)              ||
            (tp->id == THING_POISON1)           ||
            (tp->id == THING_POISON2)           ||
            (tp->id == THING_CLOUDKILL1)        ||
            (tp->id == THING_CLOUDKILL2)) {

            level_place_explosion(client_level,
                                  0, /* owner */
                                  tp,
                                  t->x, t->y,
                                  t->x, t->y);
        } else {
            ERR("unknown explosion %s", thing_logname(t));
        }
    }

    const char *sound = tp_sound_on_creation(tp);
    if (sound) {
        if (thing_is_cloud_effect(t)) {
            if (t->is_epicenter) {
                sound_play_at(sound, t->x, t->y);
            }
        } else {
            sound_play_at(sound, t->x, t->y);
        }
    }

    return (child);
}
/*
 * Create the wid_game_map_client
 */
void wid_game_map_client_wid_create (void)
{
    if (sdl_is_exiting()) {
        return;
    }

    if (wid_game_map_client_window) {
        return;
    }

    wid_notify_flush();

    LOG("Client: Create map");

    {
        fpoint tl = {0.0f, 0.0f};
        fpoint br = {1.0f, 1.0f};

        wid_game_map_client_window = 
                        wid_new_square_window("wid_game_map_client");
        wid_set_movable(wid_game_map_client_window, false);
        wid_set_do_not_raise(wid_game_map_client_window, true);
        wid_set_no_shape(wid_game_map_client_window);

        wid_set_mode(wid_game_map_client_window, WID_MODE_NORMAL);

        wid_set_text_advance(wid_game_map_client_window, 0.9f);
        wid_set_text_scaling(wid_game_map_client_window, 2.0f);
        wid_set_text_pos(wid_game_map_client_window, true, 0.5f, 0.10f);

        wid_set_text_bot(wid_game_map_client_window, true);
        wid_set_text_lhs(wid_game_map_client_window, true);
        wid_set_tl_br_pct(wid_game_map_client_window, tl, br);

        fsize sz = {0.0f, 0.0f};
        wid_set_tex_tl(wid_game_map_client_window, sz);

        fsize sz2 = {1.0f, 1.0f};
        wid_set_tex_br(wid_game_map_client_window, sz2);

        wid_set_on_key_down(wid_game_map_client_window, 
                            wid_game_map_key_event);
        wid_set_on_joy_down(wid_game_map_client_window, 
                            wid_game_map_joy_event);
    }

    {
        fpoint tl = {0.00f, 0.00f};
        fpoint br = {1.00f, 1.00f};

        wid_game_map_client_grid_container =
                        wid_new_container(wid_game_map_client_window,
                                          "wid game client grid container");

        wid_set_no_shape(wid_game_map_client_grid_container);

        wid_set_color(wid_game_map_client_grid_container, WID_COLOR_TL, BLACK);
        wid_set_color(wid_game_map_client_grid_container, WID_COLOR_BG, BLACK);
        wid_set_color(wid_game_map_client_grid_container, WID_COLOR_BR, BLACK);
        wid_set_on_mouse_motion(wid_game_map_client_grid_container,
                                wid_game_map_client_receive_mouse_motion);

        wid_set_tl_br_pct(wid_game_map_client_grid_container, tl, br);
        wid_set_tex(wid_game_map_client_grid_container, 0, 0);

        wid_set_on_key_down(wid_game_map_client_grid_container, 
                            wid_game_map_key_event);

        wid_set_on_joy_down(wid_game_map_client_grid_container, 
                            wid_game_map_joy_event);

        LOG("Client: Created map container window");
    }

    {
        double base_tile_width =
                ((1.0f / ((double)TILES_SCREEN_WIDTH)) *
                    (double)global_config.video_gl_width);

        double base_tile_height =
                ((1.0f / ((double)TILES_SCREEN_HEIGHT)) *
                    (double)global_config.video_gl_height);

        fpoint tl = { 0, 0 };
        fpoint br = { 0, 0 };

        br.x += base_tile_width;
        br.y += base_tile_height;

        client_tile_width = br.x - tl.x;
        client_tile_height = br.y - tl.y;
        client_tile_width = br.x - tl.x;
        client_tile_height = br.y - tl.y;

        if (!client_tile_width) {
            client_tile_width = TILE_WIDTH;
        }

        if (!client_tile_height) {
            client_tile_height = TILE_HEIGHT;
        }

        wid_new_grid(wid_game_map_client_grid_container,
                     MAP_WIDTH,
                     MAP_HEIGHT, client_tile_width, client_tile_height);

        LOG("Client: Created map container window grid");
    }

    /*
     * Mark that we want to learn the starting stats so we can use those
     * when starting again with this player type.
     */
    global_config.starting_stats_inited = false;

    level_pos_t level_pos = global_config.stats.level_pos;

    if (!level_pos.x && !level_pos.y) {
        level_pos.x = (myrand() % LEVEL_INITIAL_RANDOM) + 1;
        level_pos.y = 1;
    }

    client_level = level_new(wid_game_map_client_grid_container, 
                             level_pos, 
                             false /* is_editor */,
                             false /* is_map_editor */,
                             false /* on_server */);

    LOG("Client: Created level %d.%d", level_pos.y, level_pos.x);

    if (!client_level) {
        WARN("failed to load level %u.%u", level_pos.y, level_pos.x);
    }

    wid_game_map_client_vert_scroll =
        wid_new_vert_scroll_bar(wid_game_map_client_window,
                                wid_game_map_client_grid_container);
    wid_game_map_client_horiz_scroll =
        wid_new_horiz_scroll_bar(wid_game_map_client_window,
                                 wid_game_map_client_grid_container);

    wid_visible(wid_get_parent(wid_game_map_client_vert_scroll), 0);
    wid_visible(wid_get_parent(wid_game_map_client_horiz_scroll), 0);
    wid_visible(wid_game_map_client_vert_scroll, 0);
    wid_visible(wid_game_map_client_horiz_scroll, 0);

    wid_update(wid_game_map_client_vert_scroll);
    wid_update(wid_game_map_client_horiz_scroll);

    wid_game_map_client_score_update(client_level, true /* redo */);

    wid_hide(wid_game_map_client_window, 0);

    if (global_config.server_current_players > 1) {
        wid_visible(wid_chat_window, 0);
    }
}
Exemple #5
0
void thing_dead (levelp level, 
                 thingp t, 
                 thingp killer, 
                 const char *reason, ...)
{
    /*
     * If in a shop, this might be the shopkeeper.
     */
    thingp owner = thing_owner(level, t);

    /*
     * If an arrow, this might be an elf.
     */
    thingp real_killer = 0;

    if (killer) {
        real_killer = thing_owner(level, killer);
        if (!real_killer) {
            real_killer = killer;
        }
    }

    va_list args;

    verify(t);

    tpp tp = thing_tp(t);

    /*
     * If the reason of death was collection, some things we do not want
     * to do.
     */
    if (!t->is_collected) {
        /*
         * When it dies, doth it polymorph and thus avoid the reaper?
         * e.g. a mob spawner dying and creating a smaller one.
         */
        const char *polymorph = tp_polymorph_on_death(tp);

        if (thing_is_sawblade(t) && killer) {
            /*
             * Skip polymorph if there is a killer. We want the blades to
             * just vanish and not get more bloody. That only happens if
             * there is no killer and we force a polymorph.
             */
        } else if (polymorph) {
            tpp what = tp_find(polymorph);
            if (!what) {
                ERR("could now find %s to polymorph into on %s death",
                    polymorph, thing_logname(t));
            }

            /*
             * It doth polymorph.
             */
            t->tp_id = tp_to_id(what);
            t->hp = what->max_hp;
            return;
        }

        /*
         * Or perhaps it does die, but spawns something else, like the
         * player dying and creating a mob spawner.
         */
        const char *spawn = tp_spawn_on_death(tp);
        if (spawn) {
            thingp newt = thing_mob_spawn_on_death(level, t);

            /*
             * If this is the player death then give the gravestone a lot of 
             * health or it can be immediately killed by a lingering explosion 
             * that killed the player too.
             */
            if (newt && thing_is_player(t)) {
                newt->hp = 200;
            }
        }
    }

    /*
     * If a wall is gone, remove the decorations.
     */
    if (thing_is_wall(t)) {
        if (!level->is_being_destroyed) {
            /*
             * Keep walls on edge of level to stop things falling off.
             */
            if ((int)t->x <= 0) {
                return;
            }
            if ((int)t->x >= MAP_WIDTH-1) {
                return;
            }
            if ((int)t->y <= 0) {
                return;
            }
            if ((int)t->y >= MAP_HEIGHT-1) {
                return;
            }

            int dx, dy;
            for (dx = -1; dx <= 1; dx++) {
                for (dy = -1; dy <= 1; dy++) {
                    widp w;
                    while (map_find_wall_deco_at(level, t->x + dx, t->y + dy, &w)) {
                        thing_destroy(level, wid_get_thing(w), __FUNCTION__);
                    }
                }
            }
        }
    }
    /*
     * You only die once.
     */
    if (thing_is_dead(t)) {
        return;
    }

    thing_set_is_dead(t, true);

    /*
     * Bounty for the killer?
     */
    uint32_t xp = tp_get_bonus_score_on_death(tp);
    if (xp && real_killer) {
        /*
         * Did someone throw this weapon and gets the xp?
         */
        int32_t val = tp_get_bonus_score_on_death(tp);

        if (val) {
            real_killer->score += val;

            if (thing_is_player(real_killer)) {
#if 0
                if (thing_is_cloud_effect(killer)) {
                    /*
                        * Too many packets if we kill a lot of things in one
                        * go.
                        *
                        * But it looks nice... 8)
                        */
                } else {
#endif
                    MSG_SHOUT_AT(OVER_THING,
                                 t,
                                 0, 0,
                                 "%%%%font=%s$%%%%fg=%s$+%d XP",
                                 "large", "gold",
                                 val);
#if 0
                }
#endif
            }
        }
    }

    /*
     * Flash briefly red on death.
     */
    if (thing_is_monst(t)        ||
        thing_is_mob_spawner(t)  ||
        thing_is_rock(t)         ||
        thing_is_wall(t)         ||
        thing_is_door(t)) {

        widp w = t->wid;
        if (w) {
            wid_set_mode(w, WID_MODE_ACTIVE);
            if (!wid_is_hidden(w)) {
                wid_set_color(w, WID_COLOR_BLIT, RED);
            }
        }
    }

    /*
     * Boom! If this bomb is not being collected then make it blow up.
     */
    {
#if 0
if (thing_is_treasure(t)) {
CON("%s destroyed",thing_logname(t));
if (owner) {
CON("  %s owner is keeper",thing_logname(owner));
}
if (killer) {
CON("  %s killer ",thing_logname(killer));
}
if (real_killer) {
CON("  %s real_killer ",thing_logname(real_killer));
}
}
#endif
        if (!t->is_collected) {
            if (thing_is_bomb(t)        || 
                thing_is_fireball(t)    ||
                thing_is_bullet(t)) {
                level_place_explosion(level,
                                      0, /* owner */
                                      thing_tp(t),
                                      t->x, t->y,
                                      t->x, t->y);
            }

            /*
             * Breaking stuff in a shop? bad idea.
             */
            if (thing_is_treasure(t)) {
                if (owner && thing_is_shopkeeper(owner)) {
                    if (thing_is_player(real_killer)) {
                        shop_break_message(level, real_killer, owner);
                    } else {
                        shop_whodunnit_break_message(level, real_killer, owner);
                    }
                }
            }
        } else {
            /*
             * Collecting a thing?
             */
            if (thing_is_treasure(t)) {
                if (owner && thing_is_shopkeeper(owner)) {
                    if (thing_is_player(real_killer)) {
                        shop_collect_message(level, real_killer, t);
                    }
                }
            }
        }
    }

    /*
     * Stop bouncing or swaying.
     */
    if (t->wid) {
        if (tp_is_effect_pulse(tp)) {
            wid_scaling_to_pct_in(t->wid, 1.0, 1.0, 0, 0);
        }

        if (tp_is_effect_sway(tp)) {
            wid_rotate_to_pct_in(t->wid, 0, 0, 0, 0);
        }
    }

    /*
     * Log the means of death!
     */
    if (reason) {
        va_start(args, reason);
        thing_dead_(level, t, dynvprintf(reason, args));
        va_end(args);
    } else {
        thing_dead_(level, t, 0);
    }
}
/*
 * Key down etc...
 */
static uint8_t wid_text_input_receive_input (widp w, const SDL_KEYSYM *key)
{
    widp button;

    switch (key->sym) {
        case SDLK_RETURN:
            button = wid_find(wid_get_top_parent(w), "ok");
            if (button) {
                wid_text_input_button_selected(button);
                return (true);
            }

            button = wid_find(wid_get_top_parent(w), "yes");
            if (button) {
                wid_text_input_button_selected(button);
                return (true);
            }

            if (wid_text_input_button_selected(w)) {
                return (true);
            }

            break;

        case SDLK_ESCAPE: {
            button = wid_find(wid_get_top_parent(w), "cancel");
            if (button) {
                wid_text_input_button_selected(button);
                return (true);
            }

            button = wid_find(wid_get_top_parent(w), "no");
            if (button) {
                wid_text_input_button_selected(button);
                return (true);
            }

            button = wid_find(wid_get_top_parent(w), "close");
            if (button) {
                wid_text_input_button_selected(button);
                return (true);
            }

            if (wid_text_input_button_selected(w)) {
                return (true);
            }

            break;
        }

        default:
            break;
    }

    /*
     * Feed to the general input handler
     */
    int32_t ret = wid_receive_input(w, key);

    /*
     * Update the text_input to show only matching files.
     */
    widp container = wid_find(wid_get_top_parent(w),
                              wid_text_input_filelist_container_str);
    if (!container) {
        return (ret);
    }

    tree_root *r = wid_get_children(container);
    if (!r) {
        return (ret);
    }

    tree_node *n = tree_root_first(r);
    while (n) {
        fpoint container_tl;
        fpoint container_br;
        fpoint trough_tl;
        fpoint trough_br;
        double child_height;
        widp child;
        fpoint tl;
        fpoint br;

        child = (typeof(child)) n;

        /*
         * If a partial match, highlight the widget.
         */
        if (strncmp(wid_get_text(child), wid_get_text(w),
                    strlen(wid_get_text(w)))) {

            n = tree_get_next(r, r->node, n);
            continue;
        }

        wid_set_mode(child, WID_MODE_ACTIVE);

        /*
         * Find where the child is with respect to the parent.
         */
        wid_get_tl_br(child, &tl, &br);
        wid_get_tl_br(container, &container_tl, &container_br);

        /*
         * Compare within the overall height of all children.
         */
        wid_get_children_size(container, 0, &child_height);

        /*
         * What percentage of the way down.
         */
        double pct = (double)(tl.y - container_tl.y) / (double)child_height;

        /*
         * Now adjust the scrollbar.
         */
        widp scrollbar = wid_get_scrollbar_vert(container);

        /*
         * But we need the trough height.
         */
        wid_get_tl_br(wid_get_parent(scrollbar), &trough_tl, &trough_br);

        double trough_height =
                wid_get_height(wid_get_parent(scrollbar));

        /*
         * Now apply the same percentage as a wid move.
         */
        wid_move_to_abs_in(scrollbar, trough_tl.x,
                           trough_tl.y + pct * trough_height, 100);

        return (ret);
    }

    return (ret);
}
/*
 * Create the wid_text_input
 */
widp wid_large_text_input (const char *title, double x, double y, int32_t args, ...)
{
    widp wid_text_input_container;
    widp wid_text_input_title;
    widp wid_text_input_window;
    widp wid_text_input_textbox;
    double title_h;
    uint32_t toth = 0;
    uint32_t maxbuttonw = 0;
    uint32_t maxbuttonh = 0;
    uint32_t button_y;
    va_list ap;
    const char *button_names[args];
    double maxw;
    double maxh;

    wid_text_input_callback button_callback[args];
    memset(button_callback, 0, sizeof(button_callback));

    {
        double w;
        fontp font = large_font;

        ttf_text_size(&font,
                      "TITLE", &w, &title_h, 0, 1.0f, 1.0f,
                      false /* fixed width */);

        toth += title_h;
    }

    {
        fontp font = large_font;

        ttf_text_size(&font,
                      "xxxxxxxxxxxxxxxx",
                      &maxw, &maxh, 0, 1.0f, 1.0f,
                      false /* fixed width */);
    }

    maxw = min(maxw, (uint32_t) ((game.video_gl_width * 3) / 4));

    {
        int32_t n = args;

        while (n--) {
            button_names[n] = 0;
        }
    }

    {
        va_start(ap, args);

        int32_t n = args;

        while (n--) {
            button_names[args - n - 1] = va_arg(ap, const char*);
            button_callback[args - n - 1] = va_arg(ap, wid_text_input_callback);
        }

        va_end(ap);
    }

    {
        int32_t n = args;

        while (n--) {
            double w;
            double h;

            const char *button_name = button_names[n];

            if (!button_name){
                button_name = "<bug>";
            }

            fontp font = med_font;

            ttf_text_size(&font, button_name, &w, &h, 0, 1.0f, 1.0f,
                          false /* fixed width */);

            w += BUTTON_PAD_X;

            maxbuttonw = max(w, maxbuttonw);
            maxbuttonh = max(h, maxbuttonh);
        }
    }

    {
        wid_text_input_window = wid_new_rounded_window("wid_text_input");

        wid_set_color(wid_text_input_window, WID_COLOR_TEXT, WHITE);

        color c = STEELBLUE2;
        c.a = 200;
        wid_set_color(wid_text_input_window, WID_COLOR_BG, c);

        c = STEELBLUE;
        c.a = 150;
        wid_set_color(wid_text_input_window, WID_COLOR_TL, c);
        wid_set_color(wid_text_input_window, WID_COLOR_BR, c);
        wid_set_bevel(wid_text_input_window, 4);

        fpoint tl = {0, 0};
        fpoint br = {0, 0};

        br.x += maxw;
        br.y += toth;
        br.y += toth;
        br.y += toth;

        br.x += PAD_X;

        /*
         * Space for input box.
         */
        br.y += maxbuttonh;

        button_y = br.y;
        br.y += maxbuttonh;

        wid_set_tl_br(wid_text_input_window, tl, br);
    }

    if (title) {
        fpoint tl = {0, 0};
        fpoint br = {0, 0};

        br.x += maxw + PAD_X;
        br.y += title_h;
        tl.y += title_h/2;
        br.y += title_h/2;

        wid_text_input_title = wid_new_container(wid_text_input_window,
                                              "wid text_input container1");
        wid_set_tl_br(wid_text_input_title, tl, br);

        wid_set_font(wid_text_input_title, large_font);
        wid_set_text(wid_text_input_title, title);
    }

    {
        fpoint tl = {0, 0};
        fpoint br = {0, 0};

        br.x = maxw;
        br.y += toth;

        tl.x += PAD_X/2;
        br.x += PAD_X/2;

        if (title) {
            /*
             * Add space for title.
             */
            tl.y += title_h;
            br.y += title_h;
        }

        wid_text_input_container =
                        wid_new_container(wid_text_input_window,
                                          wid_text_input_filelist_container_str);
        wid_set_tl_br(wid_text_input_container, tl, br);
    }

    {
        widp child;

        fpoint tl = {0.0f, 0.0f};
        fpoint br = {0.0f, 0.0f};

        tl.x = PAD_X / 2;
        tl.y = button_y - maxbuttonh - PAD_Y * 4;
        br.x = maxw + PAD_X / 2;
        br.y = tl.y + maxbuttonh * 4 - PAD_Y * 2;

        child = wid_new_square_button(wid_text_input_window,
                                      wid_text_input_filename_input_str);

        wid_set_color(child, WID_COLOR_BG, BLACK);
        wid_set_color(child, WID_COLOR_TL, STEELBLUE);
        wid_set_color(child, WID_COLOR_BR, STEELBLUE);

        color c;
        c = STEELBLUE2;

        wid_set_mode(child, WID_MODE_NORMAL);
        c.a = 100;
        wid_set_color(child, WID_COLOR_BG, c);

        wid_set_mode(child, WID_MODE_OVER);
        c.a = 50;
        wid_set_color(child, WID_COLOR_BG, c);

        wid_set_mode(child, WID_MODE_ACTIVE);
        c.a = 60;
        wid_set_color(child, WID_COLOR_BG, c);

        wid_set_mode(child, WID_MODE_NORMAL);

        wid_set_color(child, WID_COLOR_TEXT, GREEN);

        wid_set_tl_br(child, tl, br);
        wid_set_font(child, large_font);
        wid_set_text_outline(child, true);

        wid_set_on_key_down(child, wid_text_input_receive_input);
        wid_set_show_cursor(child, true);
        wid_set_focusable(child, args + 2);

        wid_text_input_textbox = child;
    }

    {
        int32_t x = 0;
        int32_t n = args;
        int32_t focus_order = args + 1;

        x += maxw;
        x += PAD_X/2;

        while (n--) {
            double w;
            double h;

            const char *button_name = button_names[n];
            fontp font = med_font;

            ttf_text_size(&font, button_name, &w, &h, 0, 1.0f, 1.0f,
                          false /* fixed width */);

            widp child;
            child = wid_new_rounded_small_button(wid_text_input_window,
                                                 button_name);

            fpoint tl;
            fpoint br;

            tl.y = button_y;
            br.y = tl.y + maxbuttonh;
            br.x = x;
            tl.x = br.x - maxbuttonw;

            x = tl.x;
            x -= BUTTON_PAD_X;

            wid_set_tl_br(child, tl, br);
            wid_set_text(child, button_name);
            wid_set_font(child, med_font);

            color c;
            if (focus_order == 1) {
                c = GREEN;
            } else if (focus_order == 2) {
                c = STEELBLUE2;
            } else if (focus_order == 3) {
                c = CYAN;
            } else {
                c = GRAY;
            }

            wid_set_mode(child, WID_MODE_NORMAL);
            c.a = 100;
            wid_set_color(child, WID_COLOR_BG, c);

            wid_set_mode(child, WID_MODE_OVER);
            c.a = 250;
            wid_set_color(child, WID_COLOR_BG, c);

            wid_set_mode(child, WID_MODE_NORMAL);

            wid_set_focusable(child, focus_order--);

            wid_set_on_mouse_down(child, wid_text_input_button_event);

            wid_set_context(child, (void*)button_callback[n]);
        }
    }

    wid_move_to_pct_centered(wid_text_input_window, -2.5, y);
    wid_fade_in(wid_text_input_window, wid_fade_delay);
    wid_move_to_pct_centered_in(wid_text_input_window, x, y, wid_swipe_delay);
    wid_raise(wid_text_input_window);
    wid_focus_lock(wid_text_input_window);

    wid_update(wid_text_input_window);

    return (wid_text_input_textbox);
}