Ejemplo n.º 1
0
def widget_color_multi(parent: tk.Frame,
                       values: List[Tuple[str, tk.StringVar]], conf: Property):
    """For color swatches, display in a more compact form."""
    for row, column, tim_text, var in multi_grid(values):
        swatch = make_color_swatch(parent, var, 16)
        swatch.grid(row=row, column=column)
        add_tooltip(swatch, tim_text, delay=0)
Ejemplo n.º 2
0
def widget_minute_seconds_multi(parent: tk.Frame,
                                values: List[Tuple[str, tk.StringVar]],
                                conf: Property):
    """For timers, display in a more compact form."""
    for row, column, tim_text, var in multi_grid(values, columns=5):
        timer = widget_minute_seconds(parent, var, conf)
        timer.grid(row=row, column=column)
        add_tooltip(timer, tim_text, delay=0)
Ejemplo n.º 3
0
    def __init__(
        self,
        parent: tk.Misc,
        *,
        tool_frame: tk.Frame,
        tool_img: str,
        menu_bar: tk.Menu,
        tool_col: int=0,
        title: str='',
        resize_x: bool=False,
        resize_y: bool=False,
        name: str='',
    ) -> None:
        self.visible = tk.BooleanVar(parent, True)
        self.win_name = name
        self.allow_snap = False
        self.can_save = False
        self.parent = parent
        self.relX = 0
        self.relY = 0
        self.can_resize_x = resize_x
        self.can_resize_y = resize_y
        super().__init__(parent, name='pane_' + name)
        self.withdraw()  # Hide by default

        self.tool_button = make_tool_button(
            frame=tool_frame,
            img=tool_img,
            command=self._toggle_win,
        )
        self.tool_button.state(('pressed',))
        self.tool_button.grid(
            row=0,
            column=tool_col,
            # Contract the spacing to allow the icons to fit.
            padx=(2 if utils.MAC else (5, 2)),
        )
        tooltip.add_tooltip(
            self.tool_button,
            text=gettext('Hide/Show the "{}" window.').format(title))
        menu_bar.add_checkbutton(
            label=title,
            variable=self.visible,
            command=self._set_state_from_menu,
        )

        self.transient(master=parent)
        self.resizable(resize_x, resize_y)
        self.title(title)
        tk_tools.set_window_icon(self)

        self.protocol("WM_DELETE_WINDOW", self.hide_win)
        parent.bind('<Configure>', self.follow_main, add=True)
        self.bind('<Configure>', self.snap_win)
        self.bind('<FocusIn>', self.enable_snap)
Ejemplo n.º 4
0
def widget_checkmark_multi(
    parent: tk.Frame,
    values: List[Tuple[str, tk.StringVar]],
    conf: Property,
) -> tk.Widget:
    """For checkmarks, display in a more compact form."""
    for row, column, tim_text, var in multi_grid(values):
        checkbox = widget_checkmark(parent, var, conf)
        checkbox.grid(row=row, column=column)
        add_tooltip(checkbox, tim_text, delay=0)
    return parent
Ejemplo n.º 5
0
def make_checkbox(
    frame: Misc,
    section: str,
    item: str,
    desc: str,
    default: bool = False,
    var: BooleanVar = None,
    tooltip='',
) -> ttk.Checkbutton:
    """Add a checkbox to the given frame which toggles an option.

    section and item are the location in GEN_OPTS for this config.
    If var is set, it'll be used instead of an auto-created variable.
    desc is the text put next to the checkbox.
    default is the default value of the variable, if var is None.
    frame is the parent frame.
    """
    if var is None:
        var = BooleanVar(
            value=default,
            name='opt_' + section.casefold() + '_' + item,
        )
    else:
        default = var.get()

    VARS[section, item] = var

    def save_opt():
        """Save the checkbox's values."""
        GEN_OPTS[section][item] = srctools.bool_as_int(var.get())

    def load_opt():
        """Load the checkbox's values."""
        var.set(GEN_OPTS.get_bool(
            section,
            item,
            default,
        ))

    load_opt()

    var.save = save_opt
    var.load = load_opt
    widget = ttk.Checkbutton(
        frame,
        variable=var,
        text=desc,
    )

    if tooltip:
        add_tooltip(widget, tooltip)

    UI[section, item] = widget
    return widget
Ejemplo n.º 6
0
    def make_widgets(self, master: 'CheckDetails'):
        if self.master is not None:
            # If we let items move between lists, the old widgets will become
            # orphaned!
            raise ValueError("Can't move Item objects between lists!")

        self.master = master
        self.check = ttk.Checkbutton(
            master.wid_frame,
            variable=self.state_var,
            onvalue=1,
            offvalue=0,
            takefocus=False,
            width=0,
            style='CheckDetails.TCheckbutton',
            command=self.master.update_allcheck,
        )
        if self.locked:
            self.check.state(['disabled'])

        self.val_widgets = []
        for value in self.values:
            wid = tk.Label(
                master.wid_frame,
                text=value,
                justify=tk.LEFT,
                anchor=tk.W,
                background='white',
            )
            add_tooltip(wid)
            if self.hover_text:
                set_tooltip(wid, self.hover_text)
                wid.hover_override = True
            else:
                set_tooltip(wid)
                wid.hover_override = False

            if not self.locked:
                # Allow clicking on the row to toggle the checkbox
                wid.bind('<Enter>', self.hover_start, add=True)
                wid.bind('<Leave>', self.hover_stop, add=True)
                tk_tools.bind_leftclick(wid, self.row_click, add=True)
                wid.bind(tk_tools.EVENTS['LEFT_RELEASE'],
                         self.row_unclick,
                         add=True)

            self.val_widgets.append(wid)

        tk_tools.add_mousewheel(self.master.wid_canvas, self.check,
                                *self.val_widgets)
Ejemplo n.º 7
0
def init_dev_tab(f: ttk.Frame) -> None:
    f.columnconfigure(0, weight=1)
    f.columnconfigure(2, weight=1)

    make_checkbox(
        f,
        section='Debug',
        item='log_missing_ent_count',
        desc=gettext('Log missing entity counts'),
        tooltip=gettext(
            'When loading items, log items with missing entity counts '
            'in their properties.txt file.'),
    ).grid(row=0, column=0, columnspan=2, sticky=W)

    make_checkbox(
        f,
        section='Debug',
        item='log_missing_styles',
        desc=gettext("Log when item doesn't have a style"),
        tooltip=gettext(
            'Log items have no applicable version for a particular style.'
            'This usually means it will look very bad.'),
    ).grid(row=1, column=0, columnspan=2, sticky=W)

    make_checkbox(
        f,
        section='Debug',
        item='log_item_fallbacks',
        desc=gettext("Log when item uses parent's style"),
        tooltip=gettext(
            'Log when an item reuses a variant from a parent style '
            '(1970s using 1950s items, for example). This is usually '
            'fine, but may need to be fixed.'),
    ).grid(row=2, column=0, columnspan=2, sticky=W)

    make_checkbox(
        f,
        section='Debug',
        item='log_incorrect_packfile',
        desc=gettext("Log missing packfile resources"),
        tooltip=gettext(
            'Log when the resources a "PackList" refers to are not '
            'present in the zip. This may be fine (in a prerequisite zip),'
            ' but it often indicates an error.'),
    ).grid(row=3, column=0, columnspan=2, sticky=W)

    make_checkbox(
        f,
        section='Debug',
        item='development_mode',
        var=DEV_MODE,
        desc=gettext("Development Mode"),
        tooltip=gettext(
            'Enables displaying additional UI specific for '
            'development purposes. Requires restart to have an effect.'),
    ).grid(row=0, column=2, columnspan=2, sticky=W)

    make_checkbox(
        f,
        section='General',
        item='preserve_bee2_resource_dir',
        desc=gettext('Preserve Game Directories'),
        var=PRESERVE_RESOURCES,
        tooltip=gettext(
            'When exporting, do not copy resources to \n"bee2/" and'
            ' "sdk_content/maps/bee2/".\n'
            "Only enable if you're"
            ' developing new content, to ensure it is not '
            'overwritten.'),
    ).grid(row=1, column=2, columnspan=2, sticky=W)

    make_checkbox(
        f,
        section='Debug',
        item='show_log_win',
        desc=gettext('Show Log Window'),
        var=SHOW_LOG_WIN,
        tooltip=gettext('Show the log file in real-time.'),
    ).grid(row=2, column=2, columnspan=2, sticky=W)

    make_checkbox(
        f,
        section='Debug',
        item='force_all_editor_models',
        desc=gettext("Force Editor Models"),
        tooltip=gettext(
            'Make all props_map_editor models available for use. '
            'Portal 2 has a limit of 1024 models loaded in memory at '
            'once, so we need to disable unused ones to free this up.'),
    ).grid(row=3, column=2, columnspan=2, sticky='w')

    ttk.Separator(orient='horizontal').grid(row=9,
                                            column=0,
                                            columnspan=3,
                                            sticky='ew')

    ttk.Button(
        f,
        text=gettext('Dump All objects'),
        command=report_all_obj,
    ).grid(row=10, column=0)

    ttk.Button(
        f,
        text=gettext('Dump Items list'),
        command=report_items,
    ).grid(row=10, column=1)
    reload_img = ttk.Button(
        f,
        text=gettext('Reload Images'),
        command=img.refresh_all,
    )
    add_tooltip(
        reload_img,
        gettext(
            'Reload all images in the app. Expect the app to freeze momentarily.'
        ))
    reload_img.grid(row=10, column=2)
Ejemplo n.º 8
0
    def __init__(
        self,
        master,
        is_dir: bool = False,
        loc: str = '',
        callback: Callable[[str], None] = None,
    ) -> None:
        """Initialise the field.

        - Set is_dir to true to look for directories, instead of files.
        - width sets the number of characters to display.
        - callback is a function to be called with the new path whenever it
          changes.
        """
        from app.tooltip import add_tooltip

        super(FileField, self).__init__(master)

        self._location = loc
        self.is_dir = is_dir

        self._text_var = tk.StringVar(master=self, value='')
        if is_dir:
            self.browser = filedialog.Directory(
                self,
                initialdir=loc,
            )
        else:
            self.browser = filedialog.SaveAs(
                self,
                initialdir=loc,
            )

        if callback is None:
            callback = self._nop_callback

        self.callback = callback

        self.textbox = ReadOnlyEntry(
            self,
            textvariable=self._text_var,
            font=_file_field_font,
            cursor=Cursors.REGULAR,
        )
        self.textbox.grid(row=0, column=0, sticky='ew')
        self.columnconfigure(0, weight=1)
        bind_leftclick(self.textbox, self.browse)
        # The full location is displayed in a tooltip.
        add_tooltip(self.textbox, self._location)
        self.textbox.bind('<Configure>', self._text_configure)

        self.browse_btn = ttk.Button(
            self,
            text="...",
            command=self.browse,
        )
        self.browse_btn.grid(row=0, column=1)
        # It should be this narrow, but perhaps this doesn't accept floats?
        try:
            self.browse_btn['width'] = 1.5
        except tk.TclError:
            self.browse_btn['width'] = 2

        self._text_var.set(self._truncate(loc))
Ejemplo n.º 9
0
def init() -> None:
    """Initialise all widgets in the given window."""
    for cat, btn_text in [
        ('back_', _('Restore:')),
        ('game_', _('Backup:')),
    ]:
        UI[cat + 'frame'] = frame = ttk.Frame(window, )
        UI[cat + 'title_frame'] = title_frame = ttk.Frame(frame, )
        title_frame.grid(row=0, column=0, sticky='EW')
        UI[cat + 'title'] = ttk.Label(
            title_frame,
            font='TkHeadingFont',
        )
        UI[cat + 'title'].grid(row=0, column=0)
        title_frame.rowconfigure(0, weight=1)
        title_frame.columnconfigure(0, weight=1)

        UI[cat + 'details'] = CheckDetails(
            frame,
            headers=HEADERS,
        )
        UI[cat + 'details'].grid(row=1, column=0, sticky='NSEW')
        frame.rowconfigure(1, weight=1)
        frame.columnconfigure(0, weight=1)

        button_frame = ttk.Frame(frame, )
        button_frame.grid(column=0, row=2)
        ttk.Label(button_frame, text=btn_text).grid(row=0, column=0)
        UI[cat + 'btn_all'] = ttk.Button(
            button_frame,
            text='All',
            width=3,
        )
        UI[cat + 'btn_sel'] = ttk.Button(
            button_frame,
            text=_('Checked'),
            width=8,
        )
        UI[cat + 'btn_all'].grid(row=0, column=1)
        UI[cat + 'btn_sel'].grid(row=0, column=2)

        UI[cat + 'btn_del'] = ttk.Button(
            button_frame,
            text=_('Delete Checked'),
            width=14,
        )
        UI[cat + 'btn_del'].grid(row=1, column=0, columnspan=3)

        utils.add_mousewheel(
            UI[cat + 'details'].wid_canvas,
            UI[cat + 'frame'],
        )

    UI['game_refresh'] = ttk.Button(
        UI['game_title_frame'],
        image=img.png('icons/tool_sub'),
        command=ui_refresh_game,
    )
    UI['game_refresh'].grid(row=0, column=1, sticky='E')
    add_tooltip(
        UI['game_refresh'],
        "Reload the map list.",
    )

    UI['game_title']['textvariable'] = game_name
    UI['back_title']['textvariable'] = backup_name

    UI['game_btn_all']['command'] = ui_backup_all
    UI['game_btn_sel']['command'] = ui_backup_sel
    UI['game_btn_del']['command'] = ui_delete_game

    UI['back_btn_all']['command'] = ui_restore_all
    UI['back_btn_sel']['command'] = ui_restore_sel
    UI['back_btn_del']['command'] = ui_delete_backup

    UI['back_frame'].grid(row=1, column=0, sticky='NSEW')
    ttk.Separator(orient=tk.VERTICAL).grid(
        row=1,
        column=1,
        sticky='NS',
        padx=5,
    )
    UI['game_frame'].grid(row=1, column=2, sticky='NSEW')

    window.rowconfigure(1, weight=1)
    window.columnconfigure(0, weight=1)
    window.columnconfigure(2, weight=1)
Ejemplo n.º 10
0
def make_comp_widgets(frame: ttk.Frame):
    """Create widgets for the compiler settings pane.

    These are generally things that are aesthetic, and to do with the file and
    compilation process.
    """
    frame.columnconfigure(0, weight=1)

    thumb_frame = ttk.LabelFrame(
        frame,
        text=_('Thumbnail'),
        labelanchor=N,
    )
    thumb_frame.grid(row=0, column=0, sticky=EW)
    thumb_frame.columnconfigure(0, weight=1)

    UI['thumb_auto'] = ttk.Radiobutton(
        thumb_frame,
        text=_('Auto'),
        value='AUTO',
        variable=chosen_thumb,
        command=set_screen_type,
    )

    UI['thumb_peti'] = ttk.Radiobutton(
        thumb_frame,
        text=_('PeTI'),
        value='PETI',
        variable=chosen_thumb,
        command=set_screen_type,
    )

    UI['thumb_custom'] = ttk.Radiobutton(
        thumb_frame,
        text=_('Custom:'),
        value='CUST',
        variable=chosen_thumb,
        command=set_screen_type,
    )

    UI['thumb_label'] = ttk.Label(
        thumb_frame,
        anchor=CENTER,
        cursor=utils.CURSORS['link'],
    )
    UI['thumb_label'].bind(
        utils.EVENTS['LEFT'],
        find_screenshot,
    )

    UI['thumb_cleanup'] = ttk.Checkbutton(
        thumb_frame,
        text=_('Cleanup old screenshots'),
        variable=cleanup_screenshot,
    )

    UI['thumb_auto'].grid(row=0, column=0, sticky='W')
    UI['thumb_peti'].grid(row=0, column=1, sticky='W')
    UI['thumb_custom'].grid(row=1, column=0, columnspan=2, sticky='NEW')
    UI['thumb_cleanup'].grid(row=3, columnspan=2, sticky='W')
    add_tooltip(
        UI['thumb_auto'],
        _("Override the map image to use a screenshot automatically taken "
          "from the beginning of a chamber. Press F5 to take a new "
          "screenshot. If the map has not been previewed recently "
          "(within the last few hours), the default PeTI screenshot "
          "will be used instead."))
    add_tooltip(UI['thumb_peti'],
                _("Use the normal editor view for the map preview image."))
    custom_tooltip = _(
        "Use a custom image for the map preview image. Click the "
        "screenshot to select.\n"
        "Images will be converted to JPEGs if needed.")
    add_tooltip(
        UI['thumb_custom'],
        custom_tooltip,
    )

    add_tooltip(
        UI['thumb_label'],
        custom_tooltip,
    )

    add_tooltip(
        UI['thumb_cleanup'],
        _('Automatically delete unused Automatic screenshots. '
          'Disable if you want to keep things in "portal2/screenshots". '))

    if chosen_thumb.get() == 'CUST':
        # Show this if the user has set it before
        UI['thumb_label'].grid(row=2, column=0, columnspan=2, sticky='EW')
    set_screenshot()  # Load the last saved screenshot

    vrad_frame = ttk.LabelFrame(
        frame,
        text=_('Lighting:'),
        labelanchor=N,
    )
    vrad_frame.grid(row=1, column=0, sticky=EW)

    UI['light_fast'] = ttk.Radiobutton(
        vrad_frame,
        text=_('Fast'),
        value=0,
        variable=vrad_light_type,
    )
    UI['light_fast'].grid(row=0, column=0)
    UI['light_full'] = ttk.Radiobutton(
        vrad_frame,
        text=_('Full'),
        value=1,
        variable=vrad_light_type,
    )
    UI['light_full'].grid(row=0, column=1)

    add_tooltip(
        UI['light_fast'],
        _("Compile with lower-quality, fast lighting. This speeds "
          "up compile times, but does not appear as good. Some "
          "shadows may appear wrong.\n"
          "When publishing, this is ignored."))
    add_tooltip(
        UI['light_full'],
        _("Compile with high-quality lighting. This looks correct, "
          "but takes longer to compute. Use if you're arranging lights.\n"
          "When publishing, this is always used."))

    packfile_enable = ttk.Checkbutton(
        frame,
        text=_('Dump packed files to:'),
        variable=packfile_dump_enable,
        command=set_pack_dump_enabled,
    )

    packfile_frame = ttk.LabelFrame(
        frame,
        labelwidget=packfile_enable,
    )
    packfile_frame.grid(row=2, column=0, sticky=EW)

    UI['packfile_filefield'] = packfile_filefield = FileField(
        packfile_frame,
        is_dir=True,
        loc=COMPILE_CFG.get_val('General', 'packfile_dump_dir', ''),
        callback=set_pack_dump_dir,
    )
    packfile_filefield.grid(row=0, column=0, sticky=EW)
    packfile_frame.columnconfigure(0, weight=1)
    ttk.Frame(packfile_frame).grid(row=1)

    set_pack_dump_enabled()

    add_tooltip(
        packfile_enable,
        _("When compiling, dump all files which were packed into the map. Useful"
          " if you're intending to edit maps in Hammer."))

    count_frame = ttk.LabelFrame(
        frame,
        text=_('Last Compile:'),
        labelanchor=N,
    )

    count_frame.grid(row=7, column=0, sticky=EW)
    count_frame.columnconfigure(0, weight=1)
    count_frame.columnconfigure(2, weight=1)

    ttk.Label(
        count_frame,
        text=_('Entity'),
        anchor=N,
    ).grid(row=0, column=0, columnspan=3, sticky=EW)

    UI['count_entity'] = ttk.Progressbar(
        count_frame,
        maximum=100,
        variable=count_entity,
        length=120,
    )
    UI['count_entity'].grid(
        row=1,
        column=0,
        columnspan=3,
        sticky=EW,
        padx=5,
    )

    ttk.Label(
        count_frame,
        text=_('Overlay'),
        anchor=CENTER,
    ).grid(row=2, column=0, sticky=EW)
    UI['count_overlay'] = ttk.Progressbar(
        count_frame,
        maximum=100,
        variable=count_overlay,
        length=50,
    )
    UI['count_overlay'].grid(row=3, column=0, sticky=EW, padx=5)

    UI['refresh_counts'] = SubPane.make_tool_button(
        count_frame,
        img.png('icons/tool_sub', resize_to=16),
        refresh_counts,
    )
    UI['refresh_counts'].grid(row=3, column=1)
    add_tooltip(
        UI['refresh_counts'],
        _("Refresh the compile progress bars. Press after a compile has been "
          "performed to show the new values."),
    )

    ttk.Label(
        count_frame,
        text=_('Brush'),
        anchor=CENTER,
    ).grid(row=2, column=2, sticky=EW)
    UI['count_brush'] = ttk.Progressbar(
        count_frame,
        maximum=100,
        variable=count_brush,
        length=50,
    )
    UI['count_brush'].grid(row=3, column=2, sticky=EW, padx=5)

    for wid_name in ('count_overlay', 'count_entity', 'count_brush'):
        # Add in tooltip logic to the widgets.
        add_tooltip(UI[wid_name])

    refresh_counts(reload=False)
Ejemplo n.º 11
0
def make_tab(group, config: ConfigFile, tab_type):
    """Create all the widgets for a tab."""
    if tab_type is TabTypes.MIDCHAMBER:
        # Mid-chamber voice lines have predefined values.
        group_name = _('Mid - Chamber')
        group_id = 'MIDCHAMBER'
        group_desc = _('Lines played during the actual chamber, '
                       'after specific events have occurred.')
    elif tab_type is TabTypes.RESPONSE:
        # Note: 'Response' tab header, and description
        group_name = _('Responses')
        group_id = None
        group_desc = _('Lines played in response to certain events in Coop.')
    elif tab_type is TabTypes.NORM:
        group_name = group['name', 'No Name!']
        group_id = group_name.upper()
        group_desc = group['desc', ''] + ':'
    else:
        raise ValueError('Invalid tab type!')

    # This is just to hold the canvas and scrollbar
    outer_frame = ttk.Frame(UI['tabs'])
    outer_frame.columnconfigure(0, weight=1)
    outer_frame.rowconfigure(0, weight=1)

    TABS[group_name] = outer_frame
    # We add this attribute so the refresh() method knows all the
    # tab names
    outer_frame.nb_text = group_name
    outer_frame.nb_type = tab_type

    # We need a canvas to make the list scrollable.
    canv = Canvas(
        outer_frame,
        highlightthickness=0,
    )
    scroll = tk_tools.HidingScroll(
        outer_frame,
        orient=VERTICAL,
        command=canv.yview,
    )
    canv['yscrollcommand'] = scroll.set
    canv.grid(row=0, column=0, sticky='NSEW')
    scroll.grid(row=0, column=1, sticky='NS')

    UI['tabs'].add(outer_frame)

    # This holds the actual elements
    frame = ttk.Frame(canv, )
    frame.columnconfigure(0, weight=1)
    canv.create_window(0, 0, window=frame, anchor="nw")

    ttk.Label(
        frame,
        text=group_name,
        anchor='center',
        font='tkHeadingFont',
    ).grid(
        row=0,
        column=0,
        sticky='EW',
    )

    ttk.Label(
        frame,
        text=group_desc,
    ).grid(
        row=1,
        column=0,
        sticky='EW',
    )

    ttk.Separator(frame, orient=HORIZONTAL).grid(
        row=2,
        column=0,
        sticky='EW',
    )

    if tab_type is TabTypes.RESPONSE:
        sorted_quotes = sorted(group, key=lambda prop: prop.real_name)
    else:
        sorted_quotes = sorted(
            group.find_all('Quote'),
            key=quote_sort_func,
            reverse=True,
        )

    for quote in sorted_quotes:  # type: Property
        if not quote.has_children():
            continue  # Skip over config commands..

        if tab_type is TabTypes.RESPONSE:
            try:
                name = RESPONSE_NAMES[quote.name]
            except KeyError:
                # Convert channels of the form 'death_goo' into 'Death - Goo'.
                channel, ch_arg = quote.name.split('_', 1)
                name = channel.title() + ' - ' + ch_arg.title()
                del channel, ch_arg

            group_id = quote.name
        else:
            # note: default for quote names
            name = quote['name', _('No Name!')]

        ttk.Label(
            frame,
            text=name,
            font=QUOTE_FONT,
        ).grid(
            column=0,
            sticky=W,
        )

        if tab_type is TabTypes.RESPONSE:
            line_iter = find_resp_lines(quote)
        else:
            line_iter = find_lines(quote)

        for badges, line, line_id in line_iter:
            line_frame = ttk.Frame(frame, )
            line_frame.grid(
                column=0,
                padx=(10, 0),
                sticky=W,
            )
            for x, (img, ctx) in enumerate(badges):
                label = ttk.Label(line_frame, image=img, padding=0)
                label.grid(row=0, column=x)
                add_tooltip(label, ctx)

            line_frame.columnconfigure(len(badges), weight=1)

            check = ttk.Checkbutton(
                line_frame,
                # note: default voice line name next to checkbox.
                text=line['name', _('No Name?')],
            )

            check.quote_var = IntVar(value=config.get_bool(
                group_id, line_id, True), )

            check['variable'] = check.quote_var

            check['command'] = functools.partial(
                check_toggled,
                var=check.quote_var,
                config_section=config[group_id],
                quote_id=line_id,
            )
            check.transcript = list(get_trans_lines(line))
            check.grid(
                row=0,
                column=len(badges),
            )
            check.bind("<Enter>", show_trans)

    def configure_canv(e):
        """Allow resizing the windows."""
        canv['scrollregion'] = (
            4,
            0,
            canv.winfo_reqwidth(),
            frame.winfo_reqheight(),
        )
        frame['width'] = canv.winfo_reqwidth()

    canv.bind('<Configure>', configure_canv)

    return outer_frame
Ejemplo n.º 12
0
def init_gen_tab(f: ttk.Frame) -> None:
    """Make widgets in the 'General' tab."""
    def load_after_export():
        """Read the 'After Export' radio set."""
        AFTER_EXPORT_ACTION.set(
            GEN_OPTS.get_int('General', 'after_export_action',
                             AFTER_EXPORT_ACTION.get()))

    def save_after_export():
        """Save the 'After Export' radio set."""
        GEN_OPTS['General']['after_export_action'] = str(
            AFTER_EXPORT_ACTION.get())

    after_export_frame = ttk.LabelFrame(
        f,
        text=_('After Export:'),
    )
    after_export_frame.grid(
        row=0,
        rowspan=2,
        column=0,
        sticky='NS',
        padx=(0, 10),
    )

    VARS['General', 'after_export_action'] = AFTER_EXPORT_ACTION
    AFTER_EXPORT_ACTION.load = load_after_export
    AFTER_EXPORT_ACTION.save = save_after_export
    load_after_export()

    exp_nothing = ttk.Radiobutton(
        after_export_frame,
        text=_('Do Nothing'),
        variable=AFTER_EXPORT_ACTION,
        value=AfterExport.NORMAL.value,
    )
    exp_minimise = ttk.Radiobutton(
        after_export_frame,
        text=_('Minimise BEE2'),
        variable=AFTER_EXPORT_ACTION,
        value=AfterExport.MINIMISE.value,
    )
    exp_quit = ttk.Radiobutton(
        after_export_frame,
        text=_('Quit BEE2'),
        variable=AFTER_EXPORT_ACTION,
        value=AfterExport.QUIT.value,
    )
    exp_nothing.grid(row=0, column=0, sticky='w')
    exp_minimise.grid(row=1, column=0, sticky='w')
    exp_quit.grid(row=2, column=0, sticky='w')

    add_tooltip(exp_nothing,
                _('After exports, do nothing and '
                  'keep the BEE2 in focus.'))
    add_tooltip(exp_minimise,
                _('After exports, minimise to the taskbar/dock.'))
    add_tooltip(exp_quit, _('After exports, quit the BEE2.'))

    make_checkbox(
        after_export_frame,
        section='General',
        item='launch_Game',
        var=LAUNCH_AFTER_EXPORT,
        desc=_('Launch Game'),
        tooltip=_('After exporting, launch the selected game automatically.'),
    ).grid(row=3, column=0, sticky='W', pady=(10, 0))

    if sound.initiallised:
        mute = make_checkbox(
            f,
            section='General',
            item='play_sounds',
            desc=_('Play Sounds'),
            var=PLAY_SOUND,
        )
    else:
        mute = ttk.Checkbutton(
            f,
            text=_('Play Sounds'),
            state='disabled',
        )
        add_tooltip(
            mute,
            _('Pyglet is either not installed or broken.\n'
              'Sound effects have been disabled.'))
    mute.grid(row=0, column=1, sticky='E')

    UI['reset_cache'] = reset_cache = ttk.Button(
        f,
        text=_('Reset Package Caches'),
        command=clear_caches,
    )
    reset_cache.grid(row=1, column=1, sticky='EW')
    add_tooltip(
        reset_cache,
        _('Force re-extracting all package resources.'),
    )
Ejemplo n.º 13
0
def make_pane(parent: ttk.Frame):
    """Create all the widgets we use."""
    CONFIG_ORDER.sort(key=lambda grp: grp.name)

    parent.columnconfigure(0, weight=1)

    # Need to use a canvas to allow scrolling
    canvas = tk.Canvas(parent, highlightthickness=0)
    canvas.grid(row=0, column=0, sticky='NSEW')
    parent.rowconfigure(0, weight=1)

    scrollbar = ttk.Scrollbar(
        parent,
        orient='vertical',
        command=canvas.yview,
    )
    scrollbar.grid(column=1, row=0, sticky="ns")
    canvas['yscrollcommand'] = scrollbar.set

    tk_tools.add_mousewheel(canvas, canvas, parent)
    canvas_frame = ttk.Frame(canvas)
    canvas.create_window(0, 0, window=canvas_frame, anchor="nw")
    canvas_frame.rowconfigure(0, weight=1)

    sign_button = signage_ui.init_widgets(canvas_frame)
    if sign_button is not None:
        sign_button.grid(row=0, column=0, sticky='ew')

    for conf_row, config in enumerate(CONFIG_ORDER, start=1):
        frame = ttk.LabelFrame(canvas_frame, text=config.name)
        frame.columnconfigure(0, weight=1)
        frame.grid(row=conf_row, column=0, sticky='nsew')

        row = 0

        widget_count = len(config.widgets) + len(config.multi_widgets)

        # Now make the widgets.
        if config.widgets:
            for row, wid in enumerate(config.widgets):
                wid_frame = ttk.Frame(frame)
                wid_frame.grid(row=row, column=0, sticky='ew')
                wid_frame.columnconfigure(1, weight=1)

                try:
                    widget = wid.create_func(wid_frame, wid.values, wid.config)
                except Exception:
                    LOGGER.exception('Could not construct widget {}.{}',
                                     config.id, wid.id)
                    continue

                label = ttk.Label(wid_frame, text=wid.name + ': ')
                label.grid(row=0, column=0)
                widget.grid(row=0, column=1, sticky='e')

                if wid.tooltip:
                    add_tooltip(widget, wid.tooltip)
                    add_tooltip(label, wid.tooltip)
                    add_tooltip(wid_frame, wid.tooltip)

        if config.widgets and config.multi_widgets:
            ttk.Separator(orient='horizontal').grid(
                row=1,
                column=0,
                sticky='ew',
            )

        # Skip if no timer widgets
        if not config.multi_widgets:
            continue

        # Continue from wherever we were.
        for row, wid in enumerate(config.multi_widgets, start=row + 1):
            # If we only have 1 widget, don't add a redundant title.
            if widget_count == 1:
                wid_frame = ttk.Frame(frame)
            else:
                wid_frame = ttk.LabelFrame(frame, text=wid.name)

            wid_frame.grid(row=row, column=0, sticky='ew')
            wid.multi_func(
                wid_frame,
                wid.values,
                wid.config,
            )

            if wid.tooltip:
                add_tooltip(wid_frame, wid.tooltip)

    canvas.update_idletasks()
    canvas.config(
        scrollregion=canvas.bbox('ALL'),
        width=canvas_frame.winfo_reqwidth(),
    )

    def canvas_reflow(e):
        canvas['scrollregion'] = canvas.bbox('all')

    canvas.bind('<Configure>', canvas_reflow)
Ejemplo n.º 14
0
def make_map_widgets(frame: ttk.Frame):
    """Create widgets for the map settings pane.

    These are things which mainly affect the geometry or gameplay of the map.
    """

    frame.columnconfigure(0, weight=1)

    voice_frame = ttk.LabelFrame(
        frame,
        text=_('Voicelines:'),
        labelanchor=NW,
    )
    voice_frame.grid(row=1, column=0, sticky=EW)

    UI['voice_priority'] = voice_priority = ttk.Checkbutton(
        voice_frame,
        text=_("Use voiceline priorities"),
        variable=VOICE_PRIORITY_VAR,
    )
    voice_priority.grid(row=0, column=0)
    add_tooltip(
        voice_priority,
        _("Only choose the highest-priority voicelines. This means more "
          "generic lines will can only be chosen if few test elements are in "
          "the map. If disabled any applicable lines will be used."),
    )

    elev_frame = ttk.LabelFrame(
        frame,
        text=_('Spawn at:'),
        labelanchor=N,
    )

    elev_frame.grid(row=2, column=0, sticky=EW)
    elev_frame.columnconfigure(0, weight=1)
    elev_frame.columnconfigure(1, weight=1)

    UI['elev_preview'] = ttk.Radiobutton(
        elev_frame,
        text=_('Entry Door'),
        value=0,
        variable=start_in_elev,
    )

    UI['elev_elevator'] = ttk.Radiobutton(
        elev_frame,
        text=_('Elevator'),
        value=1,
        variable=start_in_elev,
    )

    UI['elev_preview'].grid(row=0, column=0, sticky=W)
    UI['elev_elevator'].grid(row=0, column=1, sticky=W)

    add_tooltip(
        UI['elev_elevator'],
        _("When previewing in SP, spawn inside the entry elevator. "
          "Use this to examine the entry and exit corridors."))
    add_tooltip(UI['elev_preview'],
                _("When previewing in SP, spawn just before the entry door."))

    corr_frame = ttk.LabelFrame(
        frame,
        width=18,
        text=_('Corridor:'),
        labelanchor=N,
    )
    corr_frame.grid(row=3, column=0, sticky=EW)
    corr_frame.columnconfigure(1, weight=1)

    make_corr_wid('sp_entry')
    make_corr_wid('sp_exit')
    make_corr_wid('coop')

    load_corridors()

    CORRIDOR['sp_entry'].widget(corr_frame).grid(row=0, column=1, sticky='ew')
    CORRIDOR['sp_exit'].widget(corr_frame).grid(row=1, column=1, sticky='ew')
    CORRIDOR['coop'].widget(corr_frame).grid(row=2, column=1, sticky='ew')

    ttk.Label(
        corr_frame,
        text=_('SP Entry:'),
        anchor='e',
    ).grid(row=0, column=0, sticky='ew', padx=2)
    ttk.Label(
        corr_frame,
        text=_('SP Exit:'),
        anchor='e',
    ).grid(row=1, column=0, sticky='ew', padx=2)
    ttk.Label(
        corr_frame,
        text=_('Coop Exit:'),
        anchor='e',
    ).grid(row=2, column=0, sticky='ew', padx=2)

    model_frame = ttk.LabelFrame(
        frame,
        text=_('Player Model (SP):'),
        labelanchor='n',
    )
    model_frame.grid(row=4, column=0, sticky='ew')
    UI['player_mdl'] = ttk.Combobox(
        model_frame,
        exportselection=0,
        textvariable=player_model_var,
        values=[PLAYER_MODELS[mdl] for mdl in PLAYER_MODEL_ORDER],
        width=20,
    )
    # Users can only use the dropdown
    UI['player_mdl'].state(['readonly'])
    UI['player_mdl'].grid(row=0, column=0, sticky=EW)

    UI['player_mdl'].bind('<<ComboboxSelected>>', set_model)
    model_frame.columnconfigure(0, weight=1)
Ejemplo n.º 15
0
def make_map_widgets(frame: ttk.Frame):
    """Create widgets for the map settings pane.

    These are things which mainly affect the geometry or gameplay of the map.
    """

    frame.columnconfigure(0, weight=1)

    voice_frame = ttk.LabelFrame(
        frame,
        text=gettext('Voicelines:'),
        labelanchor='nw',
    )
    voice_frame.grid(row=1, column=0, sticky='ew')

    voice_priority = ttk.Checkbutton(
        voice_frame,
        text=gettext("Use voiceline priorities"),
        variable=VOICE_PRIORITY_VAR,
    )
    voice_priority.grid(row=0, column=0)
    add_tooltip(
        voice_priority,
        gettext(
            "Only choose the highest-priority voicelines. This means more generic "
            "lines will can only be chosen if few test elements are in the map. "
            "If disabled any applicable lines will be used."))

    elev_frame = ttk.LabelFrame(
        frame,
        text=gettext('Spawn at:'),
        labelanchor='n',
    )

    elev_frame.grid(row=2, column=0, sticky='ew')
    elev_frame.columnconfigure(0, weight=1)
    elev_frame.columnconfigure(1, weight=1)

    elev_preview = ttk.Radiobutton(
        elev_frame,
        text=gettext('Entry Door'),
        value=0,
        variable=start_in_elev,
    )
    elev_elevator = ttk.Radiobutton(
        elev_frame,
        text=gettext('Elevator'),
        value=1,
        variable=start_in_elev,
    )

    elev_preview.grid(row=0, column=0, sticky='w')
    elev_elevator.grid(row=0, column=1, sticky='w')

    elev_conf_swap = gettext(
        "You can hold down Shift during the start of the Geometry stage to quickly swap which"
        "location you spawn at on the fly.")
    add_tooltip(
        elev_elevator,
        gettext(
            "When previewing in SP, spawn inside the entry elevator. Use this to "
            "examine the entry and exit corridors.") + "\n\n" + elev_conf_swap)
    add_tooltip(
        elev_preview,
        gettext("When previewing in SP, spawn just before the entry door.") +
        "\n\n" + elev_conf_swap)

    corr_frame = ttk.LabelFrame(
        frame,
        width=18,
        text=gettext('Corridor:'),
        labelanchor='n',
    )
    corr_frame.grid(row=3, column=0, sticky='ew')
    corr_frame.columnconfigure(1, weight=1)

    make_corr_wid('sp_entry', gettext('Singleplayer Entry Corridor')
                  )  # i18n: corridor selector window title.
    make_corr_wid('sp_exit', gettext(
        'Singleplayer Exit Corridor'))  # i18n: corridor selector window title.
    make_corr_wid(
        'coop',
        gettext('Coop Exit Corridor'))  # i18n: corridor selector window title.

    load_corridors()

    CORRIDOR['sp_entry'].widget(corr_frame).grid(row=0, column=1, sticky='ew')
    CORRIDOR['sp_exit'].widget(corr_frame).grid(row=1, column=1, sticky='ew')
    CORRIDOR['coop'].widget(corr_frame).grid(row=2, column=1, sticky='ew')

    ttk.Label(
        corr_frame,
        text=gettext('SP Entry:'),
        anchor='e',
    ).grid(row=0, column=0, sticky='ew', padx=2)
    ttk.Label(
        corr_frame,
        text=gettext('SP Exit:'),
        anchor='e',
    ).grid(row=1, column=0, sticky='ew', padx=2)
    ttk.Label(
        corr_frame,
        text=gettext('Coop Exit:'),
        anchor='e',
    ).grid(row=2, column=0, sticky='ew', padx=2)

    model_frame = ttk.LabelFrame(
        frame,
        text=gettext('Player Model (SP):'),
        labelanchor='n',
    )
    model_frame.grid(row=4, column=0, sticky='ew')
    player_mdl = ttk.Combobox(
        model_frame,
        exportselection=False,
        textvariable=player_model_var,
        values=[PLAYER_MODELS[mdl] for mdl in PLAYER_MODEL_ORDER],
        width=20,
    )
    # Users can only use the dropdown
    player_mdl.state(['readonly'])
    player_mdl.grid(row=0, column=0, sticky=tk.EW)

    def set_model(e: tk.Event) -> None:
        """Save the selected player model."""
        text = player_model_var.get()
        COMPILE_CFG['General']['player_model'] = PLAYER_MODELS_REV[text]
        COMPILE_CFG.save()

    player_mdl.bind('<<ComboboxSelected>>', set_model)

    model_frame.columnconfigure(0, weight=1)
Ejemplo n.º 16
0
    def __init__(self, parent, items=(), headers=(), add_sizegrip=False):
        """Initialise a CheckDetails pane.

        parent is the parent widget.
        items is a list of Items objects.
        headers is a list of the header strings.
        If add_sizegrip is True, add a sizegrip object between the scrollbars.
        """
        super(CheckDetails, self).__init__(parent)

        self.parent = parent
        self.headers = list(headers)
        self.items = []  # type: List[Item]
        self.sort_ind = None
        self.rev_sort = False  # Should we sort in reverse?

        self.head_check_var = tk.IntVar(value=False)
        self.wid_head_check = ttk.Checkbutton(
            self,
            variable=self.head_check_var,
            command=self.toggle_allcheck,
            takefocus=False,
            width=0,
        )
        self.wid_head_check.grid(row=0, column=0)

        add_tooltip(self.wid_head_check, gettext("Toggle all checkboxes."))

        def checkbox_enter(e):
            """When hovering over the 'all' checkbox, highlight the others."""
            for item in self.items:
                item.check.state(['active'])

        self.wid_head_check.bind('<Enter>', checkbox_enter)

        def checkbox_leave(e):
            for item in self.items:
                item.check.state(['!active'])

        self.wid_head_check.bind('<Leave>', checkbox_leave)

        self.wid_header = tk.PanedWindow(
            self,
            orient=tk.HORIZONTAL,
            sashrelief=tk.RAISED,
            sashpad=2,
            showhandle=False,
        )
        self.wid_header.grid(row=0, column=1, sticky='EW')
        self.wid_head_frames = [0] * len(self.headers)  # type: List[ttk.Frame]
        self.wid_head_label = [0] * len(self.headers)  # type: List[ttk.Label]
        self.wid_head_sort = [0] * len(self.headers)  # type: List[ttk.Label]
        self.make_headers()

        self.wid_canvas = tk.Canvas(self, )
        self.wid_canvas.grid(row=1, column=0, columnspan=2, sticky='NSEW')
        self.columnconfigure(1, weight=1)
        self.rowconfigure(1, weight=1)

        self.horiz_scroll = ttk.Scrollbar(
            self,
            orient=tk.HORIZONTAL,
            command=self.wid_canvas.xview,
        )
        self.vert_scroll = ttk.Scrollbar(
            self,
            orient=tk.VERTICAL,
            command=self.wid_canvas.yview,
        )
        self.wid_canvas['xscrollcommand'] = self.horiz_scroll.set
        self.wid_canvas['yscrollcommand'] = self.vert_scroll.set

        self.horiz_scroll.grid(row=2, column=0, columnspan=2, sticky='EWS')
        self.vert_scroll.grid(row=1, column=2, sticky='NSE')
        if add_sizegrip and tk_tools.USE_SIZEGRIP:
            self.sizegrip = ttk.Sizegrip(self)
            self.sizegrip.grid(row=2, column=2)
        else:
            self.sizegrip = None

        self.wid_frame = tk.Frame(self.wid_canvas,
                                  background='white',
                                  border=0)
        self.wid_canvas.create_window(0, 0, window=self.wid_frame, anchor='nw')

        self.bind('<Configure>', self.refresh)
        self.bind('<Map>', self.refresh)  # When added to a window, refresh

        self.wid_header.bind('<ButtonRelease-1>', self.refresh)
        self.wid_header.bind('<B1-Motion>', self.refresh)
        self.wid_header.bind('<Configure>', self.refresh)

        self.add_items(*items)

        tk_tools.add_mousewheel(
            self.wid_canvas,
            self.wid_canvas,
            self.wid_frame,
            self.wid_header,
        )
Ejemplo n.º 17
0
def make_pane(tool_frame: Frame, menu_bar: Menu,
              update_item_vis: Callable[[], None]) -> None:
    """Create the styleVar pane.

    update_item_vis is the callback fired whenever change defaults changes.
    """
    global window, _load_cback
    _load_cback = update_item_vis

    window = SubPane(
        TK_ROOT,
        title=gettext('Style/Item Properties'),
        name='style',
        menu_bar=menu_bar,
        resize_y=True,
        tool_frame=tool_frame,
        tool_img='icons/win_stylevar',
        tool_col=3,
    )

    UI['nbook'] = nbook = ttk.Notebook(window)

    nbook.grid(row=0, column=0, sticky=NSEW)
    window.rowconfigure(0, weight=1)
    window.columnconfigure(0, weight=1)
    nbook.enable_traversal()

    stylevar_frame = ttk.Frame(nbook)
    stylevar_frame.rowconfigure(0, weight=1)
    stylevar_frame.columnconfigure(0, weight=1)
    nbook.add(stylevar_frame, text=gettext('Styles'))

    canvas = Canvas(stylevar_frame, highlightthickness=0)
    # need to use a canvas to allow scrolling
    canvas.grid(sticky='NSEW')
    window.rowconfigure(0, weight=1)

    UI['style_scroll'] = ttk.Scrollbar(
        stylevar_frame,
        orient=VERTICAL,
        command=canvas.yview,
    )
    UI['style_scroll'].grid(column=1, row=0, rowspan=2, sticky="NS")
    canvas['yscrollcommand'] = UI['style_scroll'].set

    tk_tools.add_mousewheel(canvas, stylevar_frame)

    canvas_frame = ttk.Frame(canvas)

    frame_all = ttk.Labelframe(canvas_frame, text=gettext("All:"))
    frame_all.grid(row=0, sticky='EW')

    frm_chosen = ttk.Labelframe(canvas_frame, text=gettext("Selected Style:"))
    frm_chosen.grid(row=1, sticky='EW')

    ttk.Separator(
        canvas_frame,
        orient=HORIZONTAL,
    ).grid(row=2, sticky='EW', pady=(10, 5))

    frm_other = ttk.Labelframe(canvas_frame, text=gettext("Other Styles:"))
    frm_other.grid(row=3, sticky='EW')

    UI['stylevar_chosen_none'] = ttk.Label(
        frm_chosen,
        text=gettext('No Options!'),
        font='TkMenuFont',
        justify='center',
    )
    UI['stylevar_other_none'] = ttk.Label(
        frm_other,
        text=gettext('None!'),
        font='TkMenuFont',
        justify='center',
    )

    VAR_LIST[:] = sorted(StyleVar.all(), key=operator.attrgetter('id'))

    all_pos = 0
    for all_pos, var in enumerate(styleOptions):
        # Add the special stylevars which apply to all styles
        tk_vars[var.id] = int_var = IntVar(value=var.default)
        checkbox_all[var.id] = ttk.Checkbutton(
            frame_all,
            variable=int_var,
            text=var.name,
        )
        checkbox_all[var.id].grid(row=all_pos, column=0, sticky="W", padx=3)

        # Special case - this needs to refresh the filter when swapping,
        # so the items disappear or reappear.
        if var.id == 'UnlockDefault':
            checkbox_all[var.id]['command'] = lambda: update_item_vis()

        tooltip.add_tooltip(checkbox_all[var.id], make_desc(var))

    for var in VAR_LIST:
        tk_vars[var.id] = IntVar(value=var.enabled)
        args = {
            'variable': tk_vars[var.id],
            'text': var.name,
        }
        desc = make_desc(var)
        if var.applies_to_all():
            # Available in all styles - put with the hardcoded variables.
            all_pos += 1

            checkbox_all[var.id] = check = ttk.Checkbutton(frame_all, **args)
            check.grid(row=all_pos, column=0, sticky="W", padx=3)
            tooltip.add_tooltip(check, desc)
        else:
            # Swap between checkboxes depending on style.
            checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args)
            checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args)

            tooltip.add_tooltip(checkbox_chosen[var.id], desc)
            tooltip.add_tooltip(checkbox_other[var.id], desc)

    canvas.create_window(0, 0, window=canvas_frame, anchor="nw")
    canvas.update_idletasks()
    canvas.config(
        scrollregion=canvas.bbox(ALL),
        width=canvas_frame.winfo_reqwidth(),
    )

    if tk_tools.USE_SIZEGRIP:
        ttk.Sizegrip(
            window,
            cursor=tk_tools.Cursors.STRETCH_VERT,
        ).grid(row=1, column=0)

    canvas.bind('<Configure>',
                lambda e: canvas.configure(scrollregion=canvas.bbox(ALL)))

    item_config_frame = ttk.Frame(nbook)
    nbook.add(item_config_frame, text=gettext('Items'))
    itemconfig.make_pane(item_config_frame)
Ejemplo n.º 18
0
def init_widgets():
    """Initiallise all the window components."""
    global prop_window
    prop_window = Toplevel(TK_ROOT)
    prop_window.overrideredirect(1)
    prop_window.resizable(False, False)
    prop_window.transient(master=TK_ROOT)
    prop_window.attributes('-topmost', 1)
    prop_window.withdraw()  # starts hidden

    f = ttk.Frame(prop_window, relief="raised", borderwidth="4")
    f.grid(row=0, column=0)

    ttk.Label(
        f,
        text=_("Properties:"),
        anchor="center",
    ).grid(
        row=0,
        column=0,
        columnspan=3,
        sticky="EW",
    )

    wid['name'] = ttk.Label(f, text="", anchor="center")
    wid['name'].grid(row=1, column=0, columnspan=3, sticky="EW")

    wid['item_id'] = ttk.Label(f, text="", anchor="center")
    wid['item_id'].grid(row=2, column=0, columnspan=3, sticky="EW")
    tooltip.add_tooltip(wid['item_id'])

    wid['ent_count'] = ttk.Label(
        f,
        text="",
        anchor="e",
        compound="left",
        image=img.spr('gear_ent'),
    )
    wid['ent_count'].grid(row=0, column=2, rowspan=2, sticky=E)
    tooltip.add_tooltip(
        wid['ent_count'],
        _('The number of entities used for this item. The Source engine '
          'limits this to 2048 in total. This provides a guide to how many of '
          'these items can be placed in a map at once.'))

    wid['author'] = ttk.Label(f, text="", anchor="center", relief="sunken")
    wid['author'].grid(row=3, column=0, columnspan=3, sticky="EW")

    sub_frame = ttk.Frame(f, borderwidth=4, relief="sunken")
    sub_frame.grid(column=0, columnspan=3, row=4)
    for i in range(5):
        wid['subitem', i] = ttk.Label(
            sub_frame,
            image=img.invis_square(64),
        )
        wid['subitem', i].grid(row=0, column=i)
        utils.bind_leftclick(
            wid['subitem', i],
            functools.partial(sub_sel, i),
        )
        utils.bind_rightclick(
            wid['subitem', i],
            functools.partial(sub_open, i),
        )

    ttk.Label(f, text=_("Description:"), anchor="sw").grid(
        row=4,
        column=0,
        sticky="SW",
    )

    spr_frame = ttk.Frame(f, borderwidth=4, relief="sunken")
    spr_frame.grid(column=1, columnspan=2, row=5, sticky=W)
    # sprites: inputs, outputs, rotation handle, occupied/embed state,
    # desiredFacing
    for spr_id in SPR:
        wid['sprite', spr_id] = sprite = ttk.Label(
            spr_frame,
            image=img.spr('ap_grey'),
            relief="raised",
        )
        sprite.grid(row=0, column=spr_id.value)
        tooltip.add_tooltip(sprite)

    desc_frame = ttk.Frame(f, borderwidth=4, relief="sunken")
    desc_frame.grid(row=6, column=0, columnspan=3, sticky="EW")
    desc_frame.columnconfigure(0, weight=1)

    wid['desc'] = tkRichText(desc_frame, width=40, height=16)
    wid['desc'].grid(row=0, column=0, sticky="EW")

    desc_scroll = tk_tools.HidingScroll(
        desc_frame,
        orient=VERTICAL,
        command=wid['desc'].yview,
    )
    wid['desc']['yscrollcommand'] = desc_scroll.set
    desc_scroll.grid(row=0, column=1, sticky="NS")

    def show_more_info():
        url = selected_item.url
        if url is not None:
            try:
                webbrowser.open(url, new=OPEN_IN_TAB, autoraise=True)
            except webbrowser.Error:
                if messagebox.askyesno(
                        icon="error",
                        title="BEE2 - Error",
                        message=_('Failed to open a web browser. Do you wish '
                                  'for the URL to be copied to the clipboard '
                                  'instead?'),
                        detail='"{!s}"'.format(url),
                        parent=prop_window):
                    LOGGER.info("Saving {} to clipboard!", url)
                    TK_ROOT.clipboard_clear()
                    TK_ROOT.clipboard_append(url)
            # Either the webbrowser or the messagebox could cause the
            # properties to move behind the main window, so hide it
            # so it doesn't appear there.
            hide_context(None)

    wid['moreinfo'] = ttk.Button(f,
                                 text=_("More Info>>"),
                                 command=show_more_info)
    wid['moreinfo'].grid(row=7, column=2, sticky=E)
    tooltip.add_tooltip(wid['moreinfo'])

    menu_info = Menu(wid['moreinfo'])
    menu_info.add_command(label='', state='disabled')

    def show_item_props():
        sound.fx('expand')
        itemPropWin.show_window(
            selected_item.get_properties(),
            wid['changedefaults'],
            selected_sub_item.name,
        )

    wid['changedefaults'] = ttk.Button(
        f,
        text=_("Change Defaults..."),
        command=show_item_props,
    )
    wid['changedefaults'].grid(row=7, column=1)
    tooltip.add_tooltip(
        wid['changedefaults'],
        _('Change the default settings for this item when placed.'))

    wid['variant'] = ttk.Combobox(
        f,
        values=['VERSION'],
        exportselection=0,
        # On Mac this defaults to being way too wide!
        width=7 if utils.MAC else None,
    )
    wid['variant'].state(['readonly'])  # Prevent directly typing in values
    wid['variant'].bind('<<ComboboxSelected>>', set_item_version)
    wid['variant'].current(0)
    wid['variant'].grid(row=7, column=0, sticky=W)

    itemPropWin.init(hide_item_props)