What ui would work for choosing from a long, long list?
To let the user explicitly select an item from a large set, I discourage a template_list - although there's a search filter.

1. Instead, I recommend the search popup:

import bpy


def item_cb(self, context):
    return [(str(i), "Item %i" % i, "") for i in range(100)]


class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    bl_property = "my_enum"

    my_enum = bpy.props.EnumProperty(items=item_cb)

    def execute(self, context):
        self.report({'INFO'}, "Selected: %s" % self.my_enum)
        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        wm.invoke_search_popup(self)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.simple_operator('INVOKE_DEFAULT')




2. Or nested menus if there's some kind of hierarical structure in your data. The example uses dynamically-created menus:

import bpy

class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.report"
    bl_label = "Report"

    text = bpy.props.StringProperty()

    def execute(self, context):
        self.report({'INFO'}, self.text)
        return {'FINISHED'}

class SimpleCustomMenu(bpy.types.Menu):
    bl_label = "Simple Custom Menu"
    bl_idname = "OBJECT_MT_simple_custom_menu"

    def draw(self, context):
        layout = self.layout
        for i in range(6):
            layout.menu("OBJECT_MT_dyn_%d" % i)


def register():
    bpy.utils.register_class(SimpleCustomMenu)
    bpy.utils.register_class(SimpleOperator)


def unregister():
    bpy.utils.unregister_class(SimpleCustomMenu)
    bpy.utils.unregister_class(SimpleOperator)

if __name__ == "__main__":
    register()


    for i in range(6):
        idname = "OBJECT_MT_dyn_%d" % i

        def func(self, context):
            self.layout.label("Parent: " + self.bl_label)
            for j in range(10):
                option_text = "Option %i" % j
                props = self.layout.operator(SimpleOperator.bl_idname, text=option_text)
                props.text = "%s &gt; %s" % (self.bl_label, option_text)


        opclass = type("OBJECT_MT_dyn_%d" % i,
                       (bpy.types.Menu, ),
                       {"bl_idname": idname, "bl_label": "Test %i" % i, "draw": func},
                       )
        bpy.utils.register_class(opclass)

    # The menu can also be called from scripts
    bpy.ops.wm.call_menu(name=SimpleCustomMenu.bl_idname)




3. A template_list() can be used to show a single line:

layout.template_list(..., rows=1)


You can force the built-in filter options to always show in the UIList subclass:

def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
    self.use_filter_show = True




4. Something hand-crafted: a StringProperty for a search phrase and as many buttons as there are matches. The number of matches should be limited (hard-limited in below code), you could implement pagination if necessary. You make make the layout adapt to the panel width if you want:

import bpy
import random

vowels = ["a", "e", "i", "o", "u"]

def fake_words():
    words = []
    for w in range(random.randint(1, 3)):
        for i, l in enumerate(range(random.randint(2, 7))):
            if i % 2 == 0:
                words.append(vowels[random.randrange(len(vowels))])
            else:
                words.append(chr(random.randint(97, 122)))
        words.append(" ")
    return "".join(words).title()

wordlist = [fake_words() for i in range(1000)]

class ReportSelected(bpy.types.Operator):
    bl_label = "Report"
    bl_idname = "wm.report_selected"

    text = bpy.props.StringProperty()

    def execute(self, context):
        self.report({'INFO'}, self.text)
        return {'FINISHED'}


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        wm = context.window_manager
        search_phrase = wm.custom_search
        cols = context.region.width // 100
        matches_per_col = 10
        matches_max = matches_per_col * cols

        layout.prop(context.window_manager, "custom_search", text="", icon="VIEWZOOM")

        matches = [w for w in wordlist if search_phrase.lower() in w.lower()]

        col = layout.column(True)

        for i, w in enumerate(matches):
            if i &gt;= matches_max:
                break

            if i % cols == 0:
                row = col.row(True)

            row.operator("wm.report_selected", text=w).text = w

        matches_more = len(matches) - matches_max
        if matches_more &gt; 0:
            layout.label("+ %i matches" % matches_more)

def register():
    bpy.types.WindowManager.custom_search = bpy.props.StringProperty()
    bpy.utils.register_class(HelloWorldPanel)
    bpy.utils.register_class(ReportSelected)


def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)
    bpy.utils.unregister_class(ReportSelected)
    del bpy.types.WindowManager.custom_search


if __name__ == "__main__":
    register()


I can think of many more solutions, like a grid of buttons with a letter of the alphabet each as a first step, and in the second, show only strings that start with the chosen letter (probably as buttons).

Or multiple dropdown menus, select a letter or category first, and let the second update based on the first, to show only the relevant entries.