From 94c59bed6fc84d49383c39b749a4b8549e3cf3d6 Mon Sep 17 00:00:00 2001 From: Eric Froemling Date: Sat, 25 Jan 2025 22:41:39 -0800 Subject: [PATCH] playlist customize window is now screen-size-adaptive --- .efrocachemap | 40 +-- .../bascenev1lib/activity/multiteamvictory.py | 2 +- .../ba_data/python/bascenev1lib/game/race.py | 4 +- .../python/bauiv1lib/playlist/browser.py | 14 +- .../bauiv1lib/playlist/customizebrowser.py | 267 +++++++----------- 5 files changed, 136 insertions(+), 191 deletions(-) diff --git a/.efrocachemap b/.efrocachemap index 9f577b30..4f147521 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -432,7 +432,7 @@ "build/assets/ba_data/audio/zoeOw.ogg": "b2d705c31c9dcc1efdc71394764c3beb", "build/assets/ba_data/audio/zoePickup01.ogg": "e9366dc2d2b8ab8b0c4e2c14c02d0789", "build/assets/ba_data/audio/zoeScream01.ogg": "903e0e45ee9b3373e9d9ce20c814374e", - "build/assets/ba_data/data/langdata.json": "1155a380ac3dafb994b4f7438a6efa4f", + "build/assets/ba_data/data/langdata.json": "aada367fb1ea687a5891aaa78e755d29", "build/assets/ba_data/data/languages/arabic.json": "955758fcbd6ceaa19c8984ec04dc409b", "build/assets/ba_data/data/languages/belarussian.json": "009b452aa308bf2b2f7e92d9b78ba5ff", "build/assets/ba_data/data/languages/chinese.json": "5363a79f843e6be7ef47a840f47cc17d", @@ -441,12 +441,12 @@ "build/assets/ba_data/data/languages/czech.json": "3418bee44e69be13b7f72996abe96921", "build/assets/ba_data/data/languages/danish.json": "8e57db30c5250df2abff14a822f83ea7", "build/assets/ba_data/data/languages/dutch.json": "4ba5bbcc0fecddd0aac6ee2c165d1e40", - "build/assets/ba_data/data/languages/english.json": "856ba013c0680d69038988df7f04caa1", + "build/assets/ba_data/data/languages/english.json": "77fb833372b208b6f15e1c8442033ac7", "build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880", "build/assets/ba_data/data/languages/filipino.json": "1894fc331dcad7ce9cf4c180843f548f", "build/assets/ba_data/data/languages/french.json": "6d20655730b1017ef187fd828b91d43c", "build/assets/ba_data/data/languages/german.json": "bc656f1ada467161c23546f48d0dacc5", - "build/assets/ba_data/data/languages/gibberish.json": "e6c107495d2788f8a2f876986ca1af35", + "build/assets/ba_data/data/languages/gibberish.json": "8eca04f0bf36f2d306dc2f7d550d8339", "build/assets/ba_data/data/languages/greek.json": "d28d1092fbb00ed857cbd53124c0dc78", "build/assets/ba_data/data/languages/hindi.json": "567e6976b3c72f891431ad7fcc62ab16", "build/assets/ba_data/data/languages/hungarian.json": "af801baffb2c06460635dfb04c34bb3e", @@ -459,17 +459,17 @@ "build/assets/ba_data/data/languages/polish.json": "993b612c5854fc42a78726ed09c65251", "build/assets/ba_data/data/languages/portuguese.json": "99eaba2900ab66b05f0e9f22da4792a2", "build/assets/ba_data/data/languages/romanian.json": "b04345d8c7631d657a69c73eb7be755a", - "build/assets/ba_data/data/languages/russian.json": "f2d5569c5924f21d02cfa45eabac758c", + "build/assets/ba_data/data/languages/russian.json": "7164b2e1f9f2c16604a0c6eac968577c", "build/assets/ba_data/data/languages/serbian.json": "623fa4129a1154c2f32ed7867e56ff6a", "build/assets/ba_data/data/languages/slovak.json": "c11c29708b3742cdc2a92b4fa0d6d29f", - "build/assets/ba_data/data/languages/spanish.json": "1e429102b451ccb8c392a352624e8a6c", + "build/assets/ba_data/data/languages/spanish.json": "950c0d90697849d393e334e50ca8cba8", "build/assets/ba_data/data/languages/swedish.json": "3b179e7333183c70adb0811246b09959", "build/assets/ba_data/data/languages/tamil.json": "ead39b864228696a9b0d19344bc4b5ec", "build/assets/ba_data/data/languages/thai.json": "383540a1e9c7c131ac579f51afc87471", "build/assets/ba_data/data/languages/turkish.json": "f4c3e07275180114d951c002be5ef101", "build/assets/ba_data/data/languages/ukrainian.json": "0db55824759119aca74d2ee8ffe6daae", "build/assets/ba_data/data/languages/venetian.json": "acea003316bef657925d776dd460e713", - "build/assets/ba_data/data/languages/vietnamese.json": "017d8aa346d0c23a229a8a9acccf79a1", + "build/assets/ba_data/data/languages/vietnamese.json": "cc578c670bfb9855f93bbdc51922d267", "build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054", "build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422", "build/assets/ba_data/data/maps/courtyard.json": "4b836554c8949bcd2ae382f5e3c1a9cc", @@ -4182,14 +4182,14 @@ "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "6d664808b8c7644b32c41b25eeca0143", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "6c0ba3f4be9540c30fc09f13e8524fa4", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "378faae22a13d5ef4446fc3c6bb1ef90", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "407e1f9508f9cfeb72bea651c231e66f", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f41cd399c703dc097d5321a39fcc0ac3", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "b23c4ad8cc56228d58dab66e1387b615", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "a6c73f1589742e19c0e9fd19a8bb4e52", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "331c5320a5a5d0908d1ac36e2ad01242", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "48fed8f481ad390c12b094a41a0d07ac", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "e5f1362531bab5b937545e149b6d4135", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "8d83a22f2fedcebc2b6327a81e02e7c8", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "fc2cf0933aaee17916c46cec57bff439", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "dc5927b762d684df558dfedf25e4fb70", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "94c6c1ea2bda02fe7f1fa156248692c9", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "2525b8c3ab7e5cb2481f6d8157f10042", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "9325b076f45b5cbe258344ce01adb2cc", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "51a1b1ba2304004bf4d08642e3e2cca8", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "2ee184af9b80d60afea7f97aba29cb16", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "37c5f795ccbaeffeb5d6ede4fe9f3c19", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "2ee184af9b80d60afea7f97aba29cb16", @@ -4202,14 +4202,14 @@ "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d258789fd7bdc5092aab87ccb4601921", "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "314516411d5ac8e991a6f742f399d4de", "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d258789fd7bdc5092aab87ccb4601921", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "728e7999dec1017d1e9f0e9e521885e4", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "6052cd644522f3e174f27db3d51396b1", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "2fe7bf4e33e98872e9f52c71fc061134", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "692d2b8e0e68ded67a935fa72f17f8d2", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a94e443fe7d96eb9d9e1684fc3a7af0b", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "e8323acf80bcc48b27beab167f4f70f9", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "76645f01806e43775d376e45c84ef855", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "594f4d538e9e910a21c73a8b71e8aaab", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "df2bc18bde4af2d0aa05d2696ed28060", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "ddb90a0e869711ca95b53e44f97ed06e", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "0f1e187828da6902b69f0fb87bda4e56", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "b850a9e3104f7624360c0b72e4bd64be", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "3e85e624989f770aefbdbb1c2e6fcaae", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "d9e8a6fbf09896edd8cddf906ccc6afc", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "43c98fe2b7735cf63a1e24b74259b623", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "68974f508f5328f37f675c42c06b406a", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "794d258d59fd17a61752843a9a0551ad", "src/ballistica/base/mgen/pyembed/binding_base.inc": "06042d31df0ff9af96b99477162e2a91", diff --git a/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py b/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py index 8c0ad202..058ffd43 100644 --- a/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py +++ b/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py @@ -323,7 +323,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity): most_killed = entry[2].killed_count if mkp is not None: Text( - bs.Lstr(resource='mostViolatedPlayerText'), + bs.Lstr(resource='mostDestroyedPlayerText'), color=(0.5, 0.5, 0.5, 1.0), v_align=Text.VAlign.CENTER, maxwidth=300, diff --git a/src/assets/ba_data/python/bascenev1lib/game/race.py b/src/assets/ba_data/python/bascenev1lib/game/race.py index bbdd1225..766afa79 100644 --- a/src/assets/ba_data/python/bascenev1lib/game/race.py +++ b/src/assets/ba_data/python/bascenev1lib/game/race.py @@ -138,7 +138,9 @@ class RaceGame(bs.TeamGameActivity[Player, Team]): @override @classmethod def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool: - return issubclass(sessiontype, bs.MultiTeamSession) + return issubclass(sessiontype, bs.MultiTeamSession) or issubclass( + sessiontype, bs.CoopSession + ) @override @classmethod diff --git a/src/assets/ba_data/python/bauiv1lib/playlist/browser.py b/src/assets/ba_data/python/bauiv1lib/playlist/browser.py index e41bec1d..917289b7 100644 --- a/src/assets/ba_data/python/bauiv1lib/playlist/browser.py +++ b/src/assets/ba_data/python/bauiv1lib/playlist/browser.py @@ -409,11 +409,20 @@ class PlaylistBrowserWindow(bui.MainWindow): for child in children: child.delete() + # On small ui-scale, nudge 'Playlists' text to the right when + # we're small enough so that the back button doesn't partly + # obscure it. + uiscale = bui.app.ui_v1.uiscale + screensize = bui.get_virtual_screen_size() + xoffs = ( + 40 if uiscale is bui.UIScale.SMALL and screensize[0] < 1400 else 0 + ) + assert bui.app.classic is not None bui.textwidget( parent=self._subcontainer, text=bui.Lstr(resource='playlistsText'), - position=(40, self._sub_height - 26), + position=(40 + xoffs, self._sub_height - 26), size=(0, 0), scale=1.0, maxwidth=400, @@ -429,7 +438,8 @@ class PlaylistBrowserWindow(bui.MainWindow): mesh_transparent = bui.getmesh('level_select_button_transparent') mask_tex = bui.gettexture('mapPreviewMask') - h_offs = 225 if count == 1 else 115 if count == 2 else 0 + # h_offs = 225 if count == 1 else 115 if count == 2 else 0 + h_offs = 2 h_offs_bottom = 0 uiscale = bui.app.ui_v1.uiscale diff --git a/src/assets/ba_data/python/bauiv1lib/playlist/customizebrowser.py b/src/assets/ba_data/python/bauiv1lib/playlist/customizebrowser.py index cf65bb0c..51975808 100644 --- a/src/assets/ba_data/python/bauiv1lib/playlist/customizebrowser.py +++ b/src/assets/ba_data/python/bauiv1lib/playlist/customizebrowser.py @@ -7,14 +7,12 @@ from __future__ import annotations import copy import time -# import logging from typing import TYPE_CHECKING, override -# import bascenev1 as bs import bauiv1 as bui if TYPE_CHECKING: - from typing import Any + from typing import Any, Callable import bascenev1 as bs @@ -31,7 +29,6 @@ class PlaylistCustomizeBrowserWindow(bui.MainWindow): origin_widget: bui.Widget | None = None, select_playlist: str | None = None, ): - # Yes this needs tidying. # pylint: disable=too-many-locals # pylint: disable=too-many-statements # pylint: disable=cyclic-import @@ -43,34 +40,51 @@ class PlaylistCustomizeBrowserWindow(bui.MainWindow): self._r = 'gameListWindow' assert bui.app.classic is not None uiscale = bui.app.ui_v1.uiscale - self._width = 970.0 if uiscale is bui.UIScale.SMALL else 650.0 - x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0 - yoffs = -51 if uiscale is bui.UIScale.SMALL else 0.0 + self._width = 1200.0 if uiscale is bui.UIScale.SMALL else 650.0 self._height = ( - 440.0 + 800.0 if uiscale is bui.UIScale.SMALL else 420.0 if uiscale is bui.UIScale.MEDIUM else 500.0 ) + # Do some fancy math to fill all available screen area up to the + # size of our backing container. This lets us fit to the exact + # screen shape at small ui scale. + screensize = bui.get_virtual_screen_size() + scale = ( + 1.8 + if uiscale is bui.UIScale.SMALL + else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 + ) + # Calc screen size in our local container space and clamp to a + # bit smaller than our container size. + target_width = min(self._width - 70, screensize[0] / scale) + target_height = min(self._height - 40, screensize[1] / scale) + + # To get top/left coords, go to the center of our window and + # offset by half the width/height of our target area. + yoffs = 0.5 * self._height + 0.5 * target_height + 30.0 + + self._button_width = 90 + self._scroll_width = target_width - self._button_width + self._scroll_height = target_height - 70 + self._scroll_bottom = yoffs - 98 - self._scroll_height + self._button_height = self._scroll_height / 6.0 + super().__init__( root_widget=bui.containerwidget( size=(self._width, self._height), - scale=( - 1.8 - if uiscale is bui.UIScale.SMALL - else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 - ), + scale=scale, toolbar_visibility=( 'menu_minimal' if uiscale is bui.UIScale.SMALL else 'menu_full' ), - stack_offset=( - (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0) - ), ), transition=transition, origin_widget=origin_widget, + # We're affected by screen size only at small ui-scale. + refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, ) self._back_button: bui.Widget | None @@ -82,29 +96,18 @@ class PlaylistCustomizeBrowserWindow(bui.MainWindow): else: self._back_button = bui.buttonwidget( parent=self._root_widget, - position=(43 + x_inset, self._height - 60 + yoffs), - size=(160, 68), + position=(43, yoffs - 82), + size=(60, 60), scale=0.77, autoselect=True, text_scale=1.3, - label=bui.Lstr(resource='backText'), - button_type='back', - ) - bui.buttonwidget( - edit=self._back_button, - button_type='backSmall', - size=(60, 60), label=bui.charstr(bui.SpecialChar.BACK), + button_type='backSmall', ) bui.textwidget( parent=self._root_widget, - position=( - 0, - self._height - - (47 if uiscale is bui.UIScale.SMALL else 47) - + yoffs, - ), + position=(0, yoffs - (77 if uiscale is bui.UIScale.SMALL else 72)), size=(self._width, 25), text=bui.Lstr( resource=f'{self._r}.titleText', @@ -116,163 +119,86 @@ class PlaylistCustomizeBrowserWindow(bui.MainWindow): v_align='center', ) - v = self._height - 59.0 + yoffs - h = 41 + x_inset + h = self._width * 0.5 - (self._scroll_width + self._button_width) * 0.5 b_color = (0.6, 0.53, 0.63) b_textcolor = (0.75, 0.7, 0.8) self._lock_images: list[bui.Widget] = [] - lock_tex = bui.gettexture('lock') + xmargin = 0.06 + ymargin = 0.05 - scl = ( - 1.1 - if uiscale is bui.UIScale.SMALL - else 1.27 if uiscale is bui.UIScale.MEDIUM else 1.57 - ) - scl *= 0.63 - v -= 65.0 * scl - new_button = btn = bui.buttonwidget( - parent=self._root_widget, - position=(h, v), - size=(90, 58.0 * scl), - on_activate_call=self._new_playlist, - color=b_color, - autoselect=True, - button_type='square', - textcolor=b_textcolor, - text_scale=0.7, - label=bui.Lstr( + def _make_button( + i: int, label: bui.Lstr, call: Callable[[], None] + ) -> bui.Widget: + v = self._scroll_bottom + self._button_height * i + return bui.buttonwidget( + parent=self._root_widget, + position=( + h + xmargin * self._button_width, + v + ymargin * self._button_height, + ), + size=( + self._button_width * (1.0 - 2.0 * xmargin), + self._button_height * (1.0 - 2.0 * ymargin), + ), + on_activate_call=call, + color=b_color, + autoselect=True, + button_type='square', + textcolor=b_textcolor, + text_scale=0.7, + label=label, + ) + + new_button = _make_button( + 5, + bui.Lstr( resource='newText', fallback_resource=f'{self._r}.newText' ), + self._new_playlist, ) - self._lock_images.append( - bui.imagewidget( - parent=self._root_widget, - size=(30, 30), - draw_controller=btn, - position=(h - 10, v + 58.0 * scl - 28), - texture=lock_tex, - ) - ) - - v -= 65.0 * scl - self._edit_button = edit_button = btn = bui.buttonwidget( - parent=self._root_widget, - position=(h, v), - size=(90, 58.0 * scl), - on_activate_call=self._edit_playlist, - color=b_color, - autoselect=True, - textcolor=b_textcolor, - button_type='square', - text_scale=0.7, - label=bui.Lstr( - resource='editText', fallback_resource=f'{self._r}.editText' + self._edit_button = _make_button( + 4, + bui.Lstr( + resource='editText', + fallback_resource=f'{self._r}.editText', ), - ) - self._lock_images.append( - bui.imagewidget( - parent=self._root_widget, - size=(30, 30), - draw_controller=btn, - position=(h - 10, v + 58.0 * scl - 28), - texture=lock_tex, - ) + self._edit_playlist, ) - v -= 65.0 * scl - duplicate_button = btn = bui.buttonwidget( - parent=self._root_widget, - position=(h, v), - size=(90, 58.0 * scl), - on_activate_call=self._duplicate_playlist, - color=b_color, - autoselect=True, - textcolor=b_textcolor, - button_type='square', - text_scale=0.7, - label=bui.Lstr( + duplicate_button = _make_button( + 3, + bui.Lstr( resource='duplicateText', fallback_resource=f'{self._r}.duplicateText', ), - ) - self._lock_images.append( - bui.imagewidget( - parent=self._root_widget, - size=(30, 30), - draw_controller=btn, - position=(h - 10, v + 58.0 * scl - 28), - texture=lock_tex, - ) + self._duplicate_playlist, ) - v -= 65.0 * scl - delete_button = btn = bui.buttonwidget( - parent=self._root_widget, - position=(h, v), - size=(90, 58.0 * scl), - on_activate_call=self._delete_playlist, - color=b_color, - autoselect=True, - textcolor=b_textcolor, - button_type='square', - text_scale=0.7, - label=bui.Lstr( + delete_button = _make_button( + 2, + bui.Lstr( resource='deleteText', fallback_resource=f'{self._r}.deleteText' ), - ) - self._lock_images.append( - bui.imagewidget( - parent=self._root_widget, - size=(30, 30), - draw_controller=btn, - position=(h - 10, v + 58.0 * scl - 28), - texture=lock_tex, - ) - ) - v -= 65.0 * scl - self._import_button = bui.buttonwidget( - parent=self._root_widget, - position=(h, v), - size=(90, 58.0 * scl), - on_activate_call=self._import_playlist, - color=b_color, - autoselect=True, - textcolor=b_textcolor, - button_type='square', - text_scale=0.7, - label=bui.Lstr(resource='importText'), - ) - v -= 65.0 * scl - btn = bui.buttonwidget( - parent=self._root_widget, - position=(h, v), - size=(90, 58.0 * scl), - on_activate_call=self._share_playlist, - color=b_color, - autoselect=True, - textcolor=b_textcolor, - button_type='square', - text_scale=0.7, - label=bui.Lstr(resource='shareText'), - ) - self._lock_images.append( - bui.imagewidget( - parent=self._root_widget, - size=(30, 30), - draw_controller=btn, - position=(h - 10, v + 58.0 * scl - 28), - texture=lock_tex, - ) + self._delete_playlist, ) - v = self._height - 75 + yoffs - self._scroll_height = self._height - ( - 180 if uiscale is bui.UIScale.SMALL else 119 + self._import_button = _make_button( + 1, bui.Lstr(resource='importText'), self._import_playlist ) + + share_button = _make_button( + 0, bui.Lstr(resource='shareText'), self._share_playlist + ) + scrollwidget = bui.scrollwidget( parent=self._root_widget, - position=(140 + x_inset, v - self._scroll_height), - size=(self._width - (180 + 2 * x_inset), self._scroll_height + 10), + size=(self._scroll_width, self._scroll_height), + position=( + self._width * 0.5 + - (self._scroll_width + self._button_width) * 0.5 + + self._button_width, + self._scroll_bottom, + ), highlight=False, border_opacity=0.4, ) @@ -291,7 +217,14 @@ class PlaylistCustomizeBrowserWindow(bui.MainWindow): h += 210 - for btn in [new_button, delete_button, edit_button, duplicate_button]: + for btn in [ + new_button, + delete_button, + self._edit_button, + duplicate_button, + self._import_button, + share_button, + ]: bui.widget(edit=btn, right_widget=scrollwidget) bui.widget( edit=scrollwidget,