コード例 #1
0
    def __init__(self, parent, with_more_button=True):
        unused(with_more_button)
        fsui.Group.__init__(self, parent)
        self.layout = fsui.VerticalLayout()

        self.model_ids = [
            x["id"] for x in Amiga.models if "/" not in x["id"]]
        self.model_titles = [
            x["title"] for x in Amiga.models if "/" not in x["id"]]

        self.sub_model_ids = []
        self.sub_model_titles = []
        self.sub_model_updating = False

        self.model_choice = fsui.Choice(self, self.model_titles)
        AmigaEnableBehavior(self.model_choice)
        self.sub_model_choice = fsui.Choice(self, self.sub_model_titles)
        AmigaEnableBehavior(self.sub_model_choice)
        self.accuracy_label = fsui.Label(self, gettext("Accuracy:"))
        self.accuracy_choice = fsui.Choice(self, [
            gettext("High"),
            gettext("Medium"),
            gettext("Low")])
        AmigaEnableBehavior(self.accuracy_choice)
        self.ntsc_checkbox = ConfigCheckBox(self, "NTSC", "ntsc_mode")
        AmigaEnableBehavior(self.ntsc_checkbox)

        # if fs_uae_launcher.ui.get_screen_size()[1] > 768:
        # self.layout.add(heading_label, margin=10)
        # self.layout.add_spacer(0)

        hori_layout = fsui.HorizontalLayout()
        self.layout.add(hori_layout, fill=True)

        heading_label = fsui.HeadingLabel(self, gettext("Amiga Model"))
        hori_layout.add(heading_label, margin=10)
        hori_layout.add_spacer(10)
        hori_layout.add(self.ntsc_checkbox, expand=False,
                        margin_left=10, margin_right=10)
        hori_layout.add_spacer(0, expand=True)

        hori_layout.add(self.accuracy_label, margin_right=10)
        hori_layout.add(self.accuracy_choice, margin_right=10)

        hori_layout = fsui.HorizontalLayout()
        self.layout.add(hori_layout, fill=True)
        hori_layout.add(self.model_choice, expand=False, margin=10)
        hori_layout.add(self.sub_model_choice, expand=True, margin=10)

        ConfigBehavior(self, ["accuracy", "amiga_model"])

        self.model_choice.on_changed = self.on_model_changed
        self.sub_model_choice.on_changed = self.on_sub_model_changed
        self.accuracy_choice.on_changed = self.on_accuracy_changed
コード例 #2
0
    def __init__(self, parent):
        fsui.Group.__init__(self, parent)
        self.layout = fsui.VerticalLayout()

        heading_label = fsui.HeadingLabel(self, gettext("ROM & RAM"))
        self.layout.add(heading_label, margin=10)
        self.layout.add_spacer(0)

        hori_layout = fsui.HorizontalLayout()
        self.layout.add(hori_layout, fill=True)

        label = fsui.Label(self, gettext("Kickstart ROM") + ":")
        hori_layout.add(label, margin_left=10, margin_right=10)

        kickstart_types = [
            gettext("Default"),
            gettext("Custom"),
            gettext("Internal")
        ]
        self.kickstart_type_choice = fsui.Choice(self, kickstart_types)
        hori_layout.add(self.kickstart_type_choice, margin=10)

        self.text_field = fsui.TextField(self, "", read_only=True)
        hori_layout.add(self.text_field, expand=True, margin=10)

        self.browse_button = IconButton(self, "browse_file_16.png")
        self.browse_button.set_tooltip(gettext("Browse for File"))
        self.browse_button.activated.connect(self.on_browse_button)
        hori_layout.add(self.browse_button, margin=10)

        hori_layout = fsui.HorizontalLayout()
        self.layout.add(hori_layout, fill=True)

        label = fsui.Label(self, gettext("Extended ROM") + ":")
        hori_layout.add(label, margin_left=10, margin_right=10)
        # self.layout.add_spacer(0)

        kickstart_types = [gettext("Default"), gettext("Custom")]
        self.ext_rom_type_choice = fsui.Choice(self, kickstart_types)
        hori_layout.add(self.ext_rom_type_choice, margin_right=10)

        self.ext_text_field = fsui.TextField(self, "", read_only=True)
        hori_layout.add(self.ext_text_field, expand=True, margin_right=10)

        self.ext_browse_button = IconButton(self, "browse_file_16.png")
        self.ext_browse_button.set_tooltip(gettext("Browse for File"))
        self.ext_browse_button.activated.connect(self.on_ext_browse_button)
        hori_layout.add(self.ext_browse_button, margin_right=10)

        self.initialize_from_config()
        self.set_config_handlers()
コード例 #3
0
ファイル: InputSelector.py プロジェクト: jelmer/fs-uae-debian
    def __init__(self, parent, port, autofire_button=True):
        self.port = port
        self.device_option_key = "joystick_port_{0}".format(port)
        self.mode_option_key = "joystick_port_{0}_mode".format(port)
        self.autofire_mode_option_key = "joystick_port_{0}_autofire".format(
            port)

        fsui.Group.__init__(self, parent)
        self.layout = fsui.HorizontalLayout()

        self.joystick_mode_values = [
            "nothing", "mouse", "joystick", "cd32 gamepad"
        ]
        self.joystick_mode_titles = [
            gettext("No Amiga Device"),
            gettext("Amiga Mouse"),
            gettext("Amiga Joystick"),
            gettext("CD32 Pad")
        ]

        self.mode_choice = fsui.Choice(self, self.joystick_mode_titles)
        AmigaEnableBehavior(self.mode_choice)
        self.layout.add(self.mode_choice)
        self.layout.add_spacer(10)
        # else:
        #     self.mode_choice = None
        if port >= 4:
            self.mode_choice.disable()

        # devices = ["", _("No Host Device"), _("Mouse"),
        #         _("Cursor Keys and Right Ctrl/Alt")]
        # for i, name in enumerate(DeviceManager.get_joystick_names()):
        #     devices.append(name)
        #     if not self.joystick_values_initialized:
        #         self.joystick_values.append(DeviceManager.device_ids[i])
        # self.joystick_values_initialized = True

        self.device_choice = fsui.ComboBox(self, [""], read_only=True)
        AmigaEnableBehavior(self.device_choice)
        self.joystick_values = []
        self.rebuild_device_list()
        self.device_choice.set_index(0)
        # print(self.device_choice.get_index())
        self.layout.add(self.device_choice, expand=True)

        if port < 4 and autofire_button:
            self.autofire_button = IconButton(self, "16/lightning_off.png")
            self.autofire_button.activated.connect(self.on_autofire_button)
            self.layout.add(self.autofire_button, margin_left=10)
        else:
            self.autofire_button = None

        if port == 4:
            self.help_button = HelpButton(
                self, "https://fs-uae.net/custom-joystick-port")
            self.layout.add(self.help_button, margin_left=10)

        self.initialize_from_config()
        self.set_config_handlers()
コード例 #4
0
    def __init__(self, parent, device_name):
        title = gettext("Configure {device_name}").format(
            device_name=device_name)
        super().__init__(parent,
                         title=title,
                         minimizable=False,
                         maximizable=False,
                         separator=False)

        self.layout = fsui.VerticalLayout()

        self.image = fsui.Image("workspace:res/gamepad-config.png")
        self.joystick_panel = fsui.ImageView(self, self.image)
        self.layout.add(self.joystick_panel)

        if Skin.fws():
            from workspace.ui import TitleSeparator
            separator = TitleSeparator(self)
            self.layout.add(separator, fill=True)

        panel = fsui.Panel(self)
        self.layout.add(panel, fill=True)

        panel.layout = fsui.HorizontalLayout()
        panel.layout.padding = 20

        self.device_type_ids = [
            "",
            "gamepad",
            "joystick",
            # "flightstick",
            "other",
        ]
        self.device_type_labels = [
            gettext("Choose Type"),
            gettext("Gamepad"),
            gettext("Digital Joystick"),
            # gettext("Flight Stick"),
            gettext("Other Device"),
        ]

        self.type_field = fsui.Choice(panel, self.device_type_labels)
        self.type_field.changed.connect(self.on_change)
        panel.layout.add(self.type_field)

        panel.layout.add(fsui.PlainLabel(panel, gettext("Make:")),
                         margin_left=20)
        self.make_field = fsui.TextField(panel)
        self.make_field.set_min_width(140)
        self.make_field.changed.connect(self.on_change)
        panel.layout.add(self.make_field, margin_left=10)

        panel.layout.add(fsui.PlainLabel(panel, gettext("Model:")),
                         margin_left=20)
        self.model_field = fsui.TextField(panel)
        self.model_field.changed.connect(self.on_change)
        panel.layout.add(self.model_field, expand=True, margin_left=10)

        self.save_button = fsui.Button(panel, gettext("Save"))
        self.save_button.activated.connect(self.on_save_button)
        panel.layout.add(self.save_button, margin_left=20)

        # self.close_button = fsui.Button(panel, gettext("Close"))
        # self.close_button.activated.connect(self.on_close_button)
        # panel.layout.add(self.close_button, margin_left=10)

        self.device_name = device_name
        existing_config = self.read_existing_config()

        self.button_panels = []
        for x, y, direction, name in BUTTONS:
            b = MappingButton(self.joystick_panel, (x, y + 4), direction, name)
            self.button_panels.append(b)
            if name in existing_config:
                b.event_name = existing_config[name]

        self.save_button.disable()
        self.set_size(self.layout.get_min_size())
        self.center_on_parent()

        self.stopped = False
        self.current_state = {}
        self.initial_state = {}
        self.map_key_name = None

        fsui.call_later(100, self.on_timer_callback)
        thread = threading.Thread(target=event_thread,
                                  name="JoystickConfigEventThread",
                                  args=(
                                      self.device_name,
                                      weakref.ref(self),
                                  ))
        thread.start()
コード例 #5
0
ファイル: modelgroup.py プロジェクト: hroncok/fs-uae-launcher
    def __init__(self, parent, with_more_button=True):
        unused(with_more_button)
        fsui.Group.__init__(self, parent)
        self.layout = fsui.VerticalLayout()

        self.model_ids = [x["id"] for x in Amiga.models if "/" not in x["id"]]
        self.model_titles = [
            x["title"] for x in Amiga.models if "/" not in x["id"]
        ]

        self.sub_model_ids = []
        self.sub_model_titles = []
        self.sub_model_updating = False

        self.model_choice = fsui.Choice(self, self.model_titles)
        # AmigaEnableBehavior(self.model_choice)
        self.sub_model_choice = fsui.Choice(self, self.sub_model_titles)
        # AmigaEnableBehavior(self.sub_model_choice)
        self.accuracy_label = fsui.Label(self, gettext("Accuracy:"))
        self.accuracy_choice = fsui.Choice(
            self, [gettext("High"),
                   gettext("Medium"),
                   gettext("Low")])
        # AmigaEnableBehavior(self.accuracy_choice)
        self.ntsc_checkbox = ConfigCheckBox(self, "NTSC", Option.NTSC_MODE)

        AmigaShowBehavior(self.accuracy_label)
        AmigaShowBehavior(self.accuracy_choice)
        AmigaShowBehavior(self.ntsc_checkbox)

        # if fs_uae_launcher.ui.get_screen_size()[1] > 768:
        # self.layout.add(heading_label, margin=10)
        # self.layout.add_spacer(0)

        self.model_title_layout = fsui.HorizontalLayout()
        self.layout.add(self.model_title_layout, fill=True)

        if openretro or settings.get(Option.PLATFORMS_FEATURE) == "1":
            heading_label = fsui.HeadingLabel(self,
                                              gettext("Platform & Model"))
            self.model_title_layout.add(heading_label, margin=10)
            # platform_group = ConfigWidgetFactory(
            #     check=False, label=False).create(self, Option.PLATFORM)
            # self.model_title_layout.add(platform_group, margin_left=20)
            # Adding label to get the vertical spacing correct.
            # heading_label = fsui.HeadingLabel(self, "")
            # self.model_title_layout.add(heading_label, margin=10)
        else:
            heading_label = fsui.HeadingLabel(self, gettext("Amiga Model"))
            self.model_title_layout.add(heading_label, margin=10)

        self.model_title_layout.add_spacer(0, expand=True)
        self.model_title_layout.add(self.ntsc_checkbox,
                                    expand=False,
                                    margin_left=10,
                                    margin_right=10)
        self.model_title_layout.add_spacer(20)

        self.model_title_layout.add(self.accuracy_label, margin_right=10)
        self.model_title_layout.add(self.accuracy_choice, margin_right=10)
        self.model_title_layout.add(CustomConfigButton(self), margin_right=10)

        self.model_layout = fsui.HorizontalLayout()

        def dummy_min_width():
            return 0

        # Not sure why this is needed, but on startup, the min width
        # seems to be set too large due to something in the model layout.
        self.model_layout.get_min_width = dummy_min_width
        self.layout.add(self.model_layout, fill=True)

        if openretro or settings.get(Option.PLATFORMS_FEATURE) == "1":
            platform_group = ConfigWidgetFactory(check=False,
                                                 label=False).create(
                                                     self, Option.PLATFORM)
            self.model_layout.add(platform_group, margin=10)
            pass

        self.other_model_choice = ModelChoice(self)
        self.model_layout.add(self.other_model_choice, expand=True, margin=10)

        self.model_layout.add(self.model_choice, expand=False, margin=10)
        AmigaShowBehavior(self.model_choice)
        self.model_layout.add(self.sub_model_choice, expand=True, margin=10)
        AmigaShowBehavior(self.sub_model_choice)

        ConfigBehavior(self,
                       [Option.ACCURACY, Option.AMIGA_MODEL, Option.PLATFORM])

        self.model_choice.on_changed = self.on_model_changed
        self.sub_model_choice.on_changed = self.on_sub_model_changed
        self.accuracy_choice.on_changed = self.on_accuracy_changed
コード例 #6
0
ファイル: option_ui.py プロジェクト: bopopescu/fs-uae-debian
    def create_group(cls,
                     parent,
                     name,
                     description=None,
                     help_button=True,
                     thin=False):
        group = fsui.Group(parent)
        group.layout = fsui.HorizontalLayout()
        if thin:
            thin_layout = fsui.VerticalLayout()
            thin_layout.add(group.layout, fill=True)
        option = Option.get(name)
        if description == "":
            description = gettext(option["description"])
        if description:
            group.label = fsui.Label(group, description + ":")
            group.layout.add(group.label, margin_right=10)
            group.layout.add(OverrideWarning(group, name), margin_right=10)

        if thin:
            group.layout = fsui.HorizontalLayout()
            if description:
                thin_layout.add(group.layout, fill=True, margin_top=6)
            else:
                thin_layout.add(group.layout, fill=True, margin_top=0)

        choice_values = []

        if description:
            default_tmpl = "{0} (*)"
            # default_tmpl = "Default - {0}"
        else:
            default_tmpl = "{0} (*)"
            # default_tmpl = "Default - {0}"

        if option["type"].lower() == "boolean":
            if option["default"] == "1":
                default_desc = gettext(default_tmpl).format(gettext("On"))
            elif option["default"] == "0":
                default_desc = gettext(default_tmpl).format(gettext("Off"))
            else:
                default_desc = gettext("Default")
            choice_values.append(("", default_desc))
            choice_values.append(("1", gettext("On")))
            choice_values.append(("0", gettext("Off")))

        elif option["type"].lower() == "choice":
            for i, value in enumerate(option["values"]):
                if option["default"] == value[0]:
                    default_desc = gettext(default_tmpl).format(
                        gettext(value[1]))
                    break
            else:
                default_desc = gettext("Default")
            choice_values.append(("", default_desc))
            for option in option["values"]:
                choice_values.append((option[0], gettext(option[1])))

        elif option["type"].lower() == "string":

            def on_changed():
                val = text_field.get_text()
                LauncherSettings.set(name, val.strip())

            text_field = fsui.TextField(group)
            # text_field.set_min_width(400)
            text_field.set_text(LauncherSettings.get(name))
            text_field.on_changed = on_changed
            group.layout.add(text_field, expand=True)

        elif (option["type"].lower() == "integer" and "min" in option
              and "max" in option):
            current = LauncherSettings.get(name)

            if name == Option.LAUNCHER_FONT_SIZE:
                font = app.qapplication.font()
                Option.get(
                    Option.LAUNCHER_FONT_SIZE)["default"] = font.pointSize()

            current_int = int(option["default"])
            if current:
                try:
                    current_int = int(current)
                except ValueError:
                    pass
            current_int = max(option["min"], min(option["max"], current_int))
            check_box = fsui.CheckBox(group, gettext("Default"))
            spin_ctrl = fsui.SpinCtrl(group, option["min"], option["max"],
                                      current_int)
            if current == "":
                check_box.check()
                spin_ctrl.disable()

            def on_checkbox():
                if check_box.is_checked():
                    spin_ctrl.set_value(int(option["default"]))
                    spin_ctrl.disable()
                    LauncherSettings.set(name, "")
                else:
                    spin_ctrl.enable()

            check_box.on_changed = on_checkbox

            def on_spin():
                val = spin_ctrl.get_value()
                val = max(option["min"], min(option["max"], val))
                LauncherSettings.set(name, str(val))

            spin_ctrl.on_changed = on_spin
            group.layout.add_spacer(0, expand=True)
            group.layout.add(check_box)
            group.layout.add(spin_ctrl, margin_left=10)

        if choice_values:

            def on_changed():
                index = choice.get_index()
                LauncherSettings.set(name, choice_values[index][0])

            choice_labels = [x[1] for x in choice_values]
            choice = fsui.Choice(group, choice_labels)
            current = LauncherSettings.get(name)
            for i, value in enumerate(choice_values):
                if current == value[0]:
                    choice.set_index(i)
                    break
            choice.on_changed = on_changed
            if thin:
                group.layout.add(choice, expand=True)
            else:
                group.layout.add_spacer(0, expand=True)
                group.layout.add(choice)
            group.widget = choice

        if help_button:
            option_url = "https://fs-uae.net/docs/options/" + name.replace(
                "_", "-")
            group.help_button = HelpButton(parent, option_url)
            group.layout.add(group.help_button, margin_left=10)

        if thin:
            group.layout = thin_layout
        return group
コード例 #7
0
    def __init__(self, parent):
        super().__init__(
            parent, "Archive Extractor", maximizable=False, menu=True
        )
        self.create_menu()
        col = workspace.ui.Column(self)
        col.add_spacer(540, 0)

        self.source_area = SourceArea(self)
        col.add(self.source_area)
        col.add(workspace.ui.TitleSeparator(self))

        # row = workspace.gui.Row()
        # col.add(row, margin=20)
        #
        # self.image_view = workspace.gui.ImageView(self, workspace.gui.Image(
        #     workspace.Stream(__name__, "data/package-x-generic.png")))
        # row.add(self.image_view, top=-10, bottom=-10, right=10)
        #
        # col2 = workspace.gui.Column()
        # row.add(col2, expand=True, fill=False, top=0)
        #
        # self.module_label = workspace.gui.Label(
        #     self, "No archive loaded", font="16px Medium Roboto")
        # col2.add(self.module_label)
        #
        # col.spacer(2)
        #
        # self.format_row = workspace.gui.Row()
        # col2.add(self.format_row)
        #
        # self.format_label = workspace.gui.Label(self, "SOURCE ARCHIVE")
        # # font = self.format_label.get_font()
        # # font.adjust_size(-1)
        # font = workspace.gui.Font("Roboto", 12)
        # self.format_label.set_font(font)
        # self.format_label.set_text_color(workspace.gui.Color(0x80, 0x80, 0x80))
        # self.format_row.add(self.format_label, expand=True)
        # # self.play_button = workspace.gui.Button(self, "Song")
        # # row.add(self.play_button)
        #
        # self.format_label_2 = workspace.gui.Label(self, "ZIP")
        # self.format_label_2.set_font(font)
        # self.format_label_2.set_text_color(workspace.gui.Color(0x80, 0x80, 0x80))
        # self.format_row.add(self.format_label_2, right=20)

        row = col.row(margin=20, bottom=10)
        row.add(workspace.ui.Label(self, "Destination folder" + ":"))
        row.expand()
        self.items_label = workspace.ui.Label(
            self, "4 root items will be created"
        )
        row.add(self.items_label)

        row = col.row(margin=20, top=0)
        self.dir_field = workspace.ui.TextField(self, "Ram:Temp4")
        row.add(self.dir_field, expand=True)
        self.browse_button = BrowseButton(self)
        row.add(self.browse_button, left=10)

        row = col.row(margin=20, top=0)
        # FIXME
        import fsui

        row.add(workspace.ui.Label(self, "Character set:"))
        self.charset_choice = fsui.Choice(
            self, ["ISO-8859-1", "UTF-8", "CP437"]
        )
        row.add(self.charset_choice, left=10)
        row.expand()
        row.add(workspace.ui.Label(self, "Create .uaem metadata:"))
        self.meta_choice = fsui.Choice(
            self, ["When needed", "Always", "Never"]
        )
        row.add(self.meta_choice, left=10)
        # self.meta_checkbox = fsui.CheckBox(
        #     self, "Create .uaem files when needed", True)
        # row.add(self.meta_checkbox, left=20)

        # row = workspace.gui.Row()
        # col.add(row, margin=20)
        #
        # self.image_view = workspace.gui.ImageView(self, workspace.gui.Image(
        #     workspace.Stream(__name__, "data/folder.png")))
        # row.add(self.image_view, right=10)
        #
        # col2 = workspace.gui.Column()
        # row.add(col2, expand=True, fill=False, top=0)
        #
        # self.module_label = workspace.gui.Label(
        #     self, "No destination selected", font="16px Medium Roboto")
        # col2.add(self.module_label)

        row = workspace.ui.Row()
        # FIXME
        row.add_spacer(0, 32)
        col.add(row, margin=20)

        button = HelpButton(self, "")
        row.add(button)

        button = workspace.ui.Button(self, "Extract")
        row.expand()
        row.add(button)
コード例 #8
0
def create_option_group(parent,
                        options,
                        option,
                        key,
                        option_type,
                        option_text,
                        use_checkbox,
                        use_help_button,
                        label_spacing,
                        platforms=None,
                        use_label=True):
    # group = fsui.Group(parent)
    group = OptionPanel(parent, platforms)
    group.layout = fsui.HorizontalLayout()

    if use_checkbox:
        group.layout.add(OptionCheckBox(group, option_text + ":", options,
                                        key),
                         margin_right=label_spacing)
    elif use_label:
        group.layout.add(fsui.Label(group, option_text + ":"),
                         margin_right=label_spacing)

    choice_values = []

    if option_type == "boolean":
        group.layout.add_spacer(0, expand=True)
        group.layout.add(
            BooleanChoiceControl(group,
                                 options,
                                 key,
                                 use_checkbox=use_checkbox))

    elif option_type == "choice":
        group.layout.add_spacer(0, expand=True)
        choices = option["values"]
        # if use_checkbox:
        #     choices,insert(("", gettext("Auto")))
        group.layout.add(
            ChoiceControl(group,
                          options,
                          key,
                          choices,
                          use_checkbox=use_checkbox))

    elif option_type == "string":

        def on_changed():
            val = text_field.get_text()
            LauncherConfig.set(key, val.strip())

        text_field = fsui.TextField(group)
        # text_field.set_min_width(400)
        text_field.set_text(LauncherConfig.get(key))
        text_field.on_changed = on_changed
        group.layout.add(text_field, expand=True)

    elif option["type"].lower() == "integer" and "min" in option \
            and "max" in option:
        assert use_checkbox

        spin_ctrl = SpinValueControl(group, options, key, option["min"],
                                     option["max"])

        # current = LauncherConfig.get(key)
        # current_int = int(option["default"])
        # if current:
        #     try:
        #         current_int = int(current)
        #     except ValueError:
        #         pass
        # current_int = max(option["min"], min(option["max"], current_int))
        # check_box = fsui.CheckBox(group, gettext("Default"))
        # spin_ctrl = fsui.SpinCtrl(group, option["min"],
        #                           option["max"], current_int)
        # if current == "":
        #     check_box.check()
        #     spin_ctrl.disable()

        # def on_checkbox():
        #     if check_box.is_checked():
        #         spin_ctrl.set_value(int(option["default"]))
        #         spin_ctrl.disable()
        #         LauncherConfig.set(key, "")
        #     else:
        #         spin_ctrl.enable()
        #
        # check_box.on_changed = on_checkbox
        #
        # def on_spin():
        #     val = spin_ctrl.get_value()
        #     val = max(option["min"], min(option["max"], val))
        #     LauncherConfig.set(key, str(val))

        # spin_ctrl.on_changed = on_spin
        # group.layout.add(check_box)
        group.layout.add_spacer(0, expand=True)
        group.layout.add(spin_ctrl, margin_left=10)

    if choice_values:

        def on_changed():
            index = choice.get_index()
            LauncherConfig.set(key, choice_values[index][0])

        choice_labels = [x[1] for x in choice_values]
        choice = fsui.Choice(group, choice_labels)
        current = LauncherConfig.get(key)
        for i, value in enumerate(choice_values):
            if current == value[0]:
                choice.set_index(i)
                break
        choice.on_changed = on_changed
        group.layout.add_spacer(0, expand=True)
        group.layout.add(choice)
        group.widget = choice

    if use_help_button:
        help_button = HelpButton(parent, "https://fs-uae.net/options#" + key)
        group.layout.add(help_button, margin_left=10)

    return group