def make_help_menu(parent: tk.Menu): """Create the application 'Help' menu.""" # Using this name displays this correctly in OS X help = tk.Menu(parent, name='help') parent.add_cascade(menu=help, label=_('Help')) invis_icon = img.invis_square(16) icons = { icon: img.png('icons/' + icon.value, resize_to=16, error=invis_icon) for icon in ResIcon if icon is not ResIcon.NONE } icons[ResIcon.NONE] = invis_icon credits = Dialog( title=_('BEE2 Credits'), text=CREDITS_TEXT, ) for res in WEB_RESOURCES: if res is SEPERATOR: help.add_separator() else: help.add_command( label=res.name, command=functools.partial(webbrowser.open, res.url), compound='left', image=icons[res.icon], ) help.add_separator() help.add_command( label=_('Credits...'), command=credits.show, )
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['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=2, column=0, columnspan=3, sticky="EW") sub_frame = ttk.Frame(f, borderwidth=4, relief="sunken") sub_frame.grid(column=0, columnspan=3, row=3) 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=4, 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=5, 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=6, 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(): snd.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=6, 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=6, column=0, sticky=W) itemPropWin.init(hide_item_props)
def load_item_data(): """Refresh the window to use the selected item's data.""" global version_lookup item_data = selected_item.data for ind, pos in enumerate(SUBITEM_POS[selected_item.num_sub]): if pos == -1: wid['subitem', ind]['image'] = img.invis_square(64) else: wid['subitem', ind]['image'] = selected_item.get_icon(pos) wid['subitem', ind]['relief'] = 'flat' wid['subitem', pos_for_item()]['relief'] = 'raised' wid['author']['text'] = ', '.join(item_data.authors) wid['name']['text'] = selected_sub_item.name wid['ent_count']['text'] = item_data.ent_count or '??' wid['desc'].set_text( get_description( global_last=selected_item.item.glob_desc_last, glob_desc=selected_item.item.glob_desc, style_desc=item_data.desc, )) if itemPropWin.can_edit(selected_item.properties()): wid['changedefaults'].state(['!disabled']) else: wid['changedefaults'].state(['disabled']) version_lookup = set_version_combobox(wid['variant'], selected_item) if selected_item.url is None: wid['moreinfo'].state(['disabled']) else: wid['moreinfo'].state(['!disabled']) wid['moreinfo'].tooltip_text = selected_item.url editor_data = item_data.editor has_inputs = False has_polarity = False has_outputs = False for inp_list in editor_data.find_all("Exporting", "Inputs"): for inp in inp_list: if inp.name == "connection_standard": has_inputs = True elif inp.name == "connection_tbeam_polarity": has_polarity = True for out_list in editor_data.find_all("Exporting", "Outputs"): for out in out_list: if out.name == "connection_standard": has_outputs = True break has_timer = any(editor_data.find_all("Properties", "TimerDelay")) editor_bit = next(editor_data.find_all("Editor")) rot_type = editor_bit["MovementHandle", "HANDLE_NONE"].casefold() facing_type = editor_bit["InvalidSurface", ""].casefold() surf_wall = "wall" in facing_type surf_floor = "floor" in facing_type surf_ceil = "ceiling" in facing_type is_embed = any(editor_data.find_all("Exporting", "EmbeddedVoxels")) if has_inputs: if has_polarity: set_sprite(SPR.INPUT, 'in_polarity') else: set_sprite(SPR.INPUT, 'in_norm') else: set_sprite(SPR.INPUT, 'in_none') if has_outputs: if has_timer: set_sprite(SPR.OUTPUT, 'out_tim') else: set_sprite(SPR.OUTPUT, 'out_norm') else: set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, ROT_TYPES.get( rot_type.casefold(), 'rot_none', )) if is_embed: set_sprite(SPR.COLLISION, 'space_embed') else: set_sprite(SPR.COLLISION, 'space_none') face_spr = "surf" if not surf_wall: face_spr += "_wall" if not surf_floor: face_spr += "_floor" if not surf_ceil: face_spr += "_ceil" if face_spr == "surf": # This doesn't seem right - this item won't be placeable at all... LOGGER.warning( "Item <{}> disallows all orientations. Is this right?", selected_item.id, ) face_spr += "_none" set_sprite(SPR.FACING, face_spr) # Now some special overrides for certain classes. if selected_item.id == "ITEM_CUBE": # Cubes - they should show info for the dropper. set_sprite(SPR.FACING, 'surf_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_embed') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_36') wid['sprite', SPR.ROTATION].tooltip_text += _('\n(Reflection Cube only)') item_class = editor_data['ItemClass', ''].casefold() if item_class == "itempaintsplat": # Reflection or normal gel.. set_sprite(SPR.FACING, 'surf_wall_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_none') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_paint') elif item_class == 'itemrailplatform': # Track platform - always embeds into the floor. set_sprite(SPR.COLLISION, 'space_embed')
def load_item_data(): """Refresh the window to use the selected item's data.""" global version_lookup item_data = selected_item.data for ind, pos in enumerate(SUBITEM_POS[selected_item.num_sub]): if pos == -1: wid['subitem', ind]['image'] = img.invis_square(64) else: wid['subitem', ind]['image'] = selected_item.get_icon(pos) wid['subitem', ind]['relief'] = 'flat' wid['subitem', pos_for_item()]['relief'] = 'raised' wid['author']['text'] = ', '.join(item_data.authors) wid['name']['text'] = selected_sub_item.name wid['ent_count']['text'] = item_data.ent_count or '??' wid['desc'].set_text( get_description( global_last=selected_item.item.glob_desc_last, glob_desc=selected_item.item.glob_desc, style_desc=item_data.desc, )) if itemPropWin.can_edit(selected_item.properties()): wid['changedefaults'].state(['!disabled']) else: wid['changedefaults'].state(['disabled']) version_lookup = set_version_combobox(wid['variant'], selected_item) if selected_item.url is None: wid['moreinfo'].state(['disabled']) else: wid['moreinfo'].state(['!disabled']) tooltip.set_tooltip(wid['moreinfo'], selected_item.url) editor_data = item_data.editor.copy() comm_block = Property(editor_data['Type'], []) ( has_inputs, has_outputs, has_secondary, ) = packageLoader.Item.convert_item_io(comm_block, editor_data) del comm_block # We don't use the config. has_timer = any(editor_data.find_all("Properties", "TimerDelay")) editor_bit = next(editor_data.find_all("Editor")) rot_type = editor_bit["MovementHandle", "HANDLE_NONE"].casefold() facing_type = editor_bit["InvalidSurface", ""].casefold() surf_wall = "wall" in facing_type surf_floor = "floor" in facing_type surf_ceil = "ceiling" in facing_type is_embed = any(editor_data.find_all("Exporting", "EmbeddedVoxels")) if has_inputs: if has_secondary: set_sprite(SPR.INPUT, 'in_dual') # Real funnels work slightly differently. if selected_item.id.casefold() == 'item_tbeam': wid['sprite', SPR.INPUT].tooltip_text = _( 'Excursion Funnels accept a on/off ' 'input and a directional input.') else: set_sprite(SPR.INPUT, 'in_norm') else: set_sprite(SPR.INPUT, 'in_none') if has_outputs: if has_timer: set_sprite(SPR.OUTPUT, 'out_tim') else: set_sprite(SPR.OUTPUT, 'out_norm') else: set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, ROT_TYPES.get( rot_type.casefold(), 'rot_none', )) if is_embed: set_sprite(SPR.COLLISION, 'space_embed') else: set_sprite(SPR.COLLISION, 'space_none') face_spr = "surf" if not surf_wall: face_spr += "_wall" if not surf_floor: face_spr += "_floor" if not surf_ceil: face_spr += "_ceil" if face_spr == "surf": # This doesn't seem right - this item won't be placeable at all... LOGGER.warning( "Item <{}> disallows all orientations. Is this right?", selected_item.id, ) face_spr += "_none" set_sprite(SPR.FACING, face_spr) # Now some special overrides for certain classes. if selected_item.id == "ITEM_CUBE": # Cubes - they should show info for the dropper. set_sprite(SPR.FACING, 'surf_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_embed') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_36') tooltip.set_tooltip( wid['sprite', SPR.ROTATION], SPRITE_TOOL['rot_36'] + _('This item can be rotated on the floor to face 360 ' 'degrees, for Reflection Cubes only.'), ) item_class = editor_data['ItemClass', ''].casefold() if item_class == "itempaintsplat": # Reflection or normal gel.. set_sprite(SPR.FACING, 'surf_wall_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_none') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_paint') elif item_class == 'itemrailplatform': # Track platform - always embeds into the floor. set_sprite(SPR.COLLISION, 'space_embed')
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['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=2, column=0, columnspan=3, sticky="EW") sub_frame = ttk.Frame(f, borderwidth=4, relief="sunken") sub_frame.grid(column=0, columnspan=3, row=3) 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=4, 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=5, 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=6, 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(): snd.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=6, 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=6, column=0, sticky=W) itemPropWin.init(hide_item_props)
def load_item_data(): """Refresh the window to use the selected item's data.""" global version_lookup item_data = selected_item.data for ind, pos in enumerate(SUBITEM_POS[selected_item.num_sub]): if pos == -1: wid['subitem', ind]['image'] = img.invis_square(64) else: wid['subitem', ind]['image'] = selected_item.get_icon(pos) wid['subitem', ind]['relief'] = 'flat' wid['subitem', pos_for_item()]['relief'] = 'raised' wid['author']['text'] = ', '.join(item_data.authors) wid['name']['text'] = selected_sub_item.name wid['ent_count']['text'] = item_data.ent_count or '??' wid['desc'].set_text( get_description( global_last=selected_item.item.glob_desc_last, glob_desc=selected_item.item.glob_desc, style_desc=item_data.desc, ) ) if itemPropWin.can_edit(selected_item.properties()): wid['changedefaults'].state(['!disabled']) else: wid['changedefaults'].state(['disabled']) version_lookup = set_version_combobox(wid['variant'], selected_item) if selected_item.url is None: wid['moreinfo'].state(['disabled']) else: wid['moreinfo'].state(['!disabled']) tooltip.set_tooltip(wid['moreinfo'], selected_item.url) editor_data = item_data.editor.copy() comm_block = Property(selected_item.id, []) ( has_inputs, has_outputs, has_secondary, ) = packageLoader.Item.convert_item_io(comm_block, editor_data) del comm_block # We don't use the config. has_timer = any(editor_data.find_all("Properties", "TimerDelay")) editor_bit = next(editor_data.find_all("Editor")) rot_type = editor_bit["MovementHandle", "HANDLE_NONE"].casefold() facing_type = editor_bit["InvalidSurface", ""].casefold() surf_wall = "wall" in facing_type surf_floor = "floor" in facing_type surf_ceil = "ceiling" in facing_type is_embed = any(editor_data.find_all("Exporting", "EmbeddedVoxels")) if has_inputs: if has_secondary: set_sprite(SPR.INPUT, 'in_dual') # Real funnels work slightly differently. if selected_item.id.casefold() == 'item_tbeam': wid['sprite', SPR.INPUT].tooltip_text = _( 'Excursion Funnels accept a on/off ' 'input and a directional input.' ) else: set_sprite(SPR.INPUT, 'in_norm') else: set_sprite(SPR.INPUT, 'in_none') if has_outputs: if has_timer: set_sprite(SPR.OUTPUT, 'out_tim') else: set_sprite(SPR.OUTPUT, 'out_norm') else: set_sprite(SPR.OUTPUT, 'out_none') set_sprite( SPR.ROTATION, ROT_TYPES.get( rot_type.casefold(), 'rot_none', ) ) if is_embed: set_sprite(SPR.COLLISION, 'space_embed') else: set_sprite(SPR.COLLISION, 'space_none') face_spr = "surf" if not surf_wall: face_spr += "_wall" if not surf_floor: face_spr += "_floor" if not surf_ceil: face_spr += "_ceil" if face_spr == "surf": # This doesn't seem right - this item won't be placeable at all... LOGGER.warning( "Item <{}> disallows all orientations. Is this right?", selected_item.id, ) face_spr += "_none" set_sprite(SPR.FACING, face_spr) # Now some special overrides for certain classes. if selected_item.id == "ITEM_CUBE": # Cubes - they should show info for the dropper. set_sprite(SPR.FACING, 'surf_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_embed') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_36') tooltip.set_tooltip( wid['sprite', SPR.ROTATION], SPRITE_TOOL['rot_36'] + _( 'This item can be rotated on the floor to face 360 ' 'degrees, for Reflection Cubes only.' ), ) item_class = editor_data['ItemClass', ''].casefold() if item_class == "itempaintsplat": # Reflection or normal gel.. set_sprite(SPR.FACING, 'surf_wall_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_none') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_paint') elif item_class == 'itemrailplatform': # Track platform - always embeds into the floor. set_sprite(SPR.COLLISION, 'space_embed')