diff --git a/arcade/resources/gui_basic_assets/toggle/circle_switch_off.png b/arcade/resources/gui_basic_assets/toggle/circle_switch_off.png new file mode 100644 index 0000000000..3bd05f2510 Binary files /dev/null and b/arcade/resources/gui_basic_assets/toggle/circle_switch_off.png differ diff --git a/arcade/resources/gui_basic_assets/toggle/circle_switch_on.png b/arcade/resources/gui_basic_assets/toggle/circle_switch_on.png new file mode 100644 index 0000000000..dd9d40a8ed Binary files /dev/null and b/arcade/resources/gui_basic_assets/toggle/circle_switch_on.png differ diff --git a/arcade/resources/gui_basic_assets/window/dark_blue_gray_panel.png b/arcade/resources/gui_basic_assets/window/dark_blue_gray_panel.png new file mode 100644 index 0000000000..89ac84d093 Binary files /dev/null and b/arcade/resources/gui_basic_assets/window/dark_blue_gray_panel.png differ diff --git a/doc/index.rst b/doc/index.rst index db89d23893..d27036b573 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -62,6 +62,7 @@ The Python Arcade Library tutorials/compiling_with_nuitka/index tutorials/framebuffer/index tutorials/shader_tutorials + tutorials/menu/index .. image:: images/example_games.svg :alt: Programming guide icon diff --git a/doc/tutorials/menu/index.rst b/doc/tutorials/menu/index.rst new file mode 100644 index 0000000000..573d6c17d9 --- /dev/null +++ b/doc/tutorials/menu/index.rst @@ -0,0 +1,324 @@ +.. include:: + +.. _menu_tutorial: + + +Making a Menu with Arcade's GUI +=============================== + +.. image:: menu.gif + :width: 80% + +This tutorial shows how to use most of arcade's gui's widgets. + +Step 1: Open a Window +--------------------- + +.. image:: menu_01.png + :width: 50% + +First, let's start a blank window with a view. + +.. literalinclude:: menu_01.py + :caption: Opening a Window + :linenos: + +Step 2: Switching to Menu View +------------------------------- + +.. image:: menu_02.png + :width: 50% + +For this section we will switch the current view of the window to the menu view. + + +Imports +~~~~~~~ + +First we will import the arcade gui: + +.. literalinclude:: menu_02.py + :caption: Importing arcade.gui + :lines: 4-5 + :emphasize-lines: 2 + +Modify the MainView +~~~~~~~~~~~~~~~~~~~~ + +We are going to add a button to change the view. For drawing a button we would +need a ``UIManager``. + +.. literalinclude:: menu_02.py + :caption: Intialising the Manager + :lines: 16-19 + :emphasize-lines: 4 + +After initialising the manager we need to enable it when the view is shown and +disable it when the view is hiddien. + +.. literalinclude:: menu_02.py + :caption: Enabling the Manager + :pyobject: MainView.on_show_view + :emphasize-lines: 5-6 + +.. literalinclude:: menu_02.py + :caption: Disabling the Manager + :pyobject: MainView.on_hide_view + +We also need to draw the childrens of the menu in ``on_draw``. + + .. literalinclude:: menu_02.py + :caption: Drawing Children's of the Manager + :pyobject: MainView.on_draw + :emphasize-lines: 6-7 + +Now we have successfully setup the manager, only thing left it to add the button. +We are using ``UIAnchorLayout`` to position the button. We also setup a function +which is called when the button is clicked. + + .. literalinclude:: menu_02.py + :caption: Initialising the Button + :lines: 21-37 + +Initialise the Menu View +~~~~~~~~~~~~~~~~~~~~~~~~ + +We make a boiler plate view just like we did in Step-1 for switiching the view +when the pause button is clicked. + +.. literalinclude:: menu_02.py + :caption: Initialise the Menu View + :pyobject: MenuView + +Program Listings +~~~~~~~~~~~~~~~~ + +* :ref:`menu_02` |larr| Where we are right now +* :ref:`menu_02_diff` |larr| What we changed to get here + + +Step 3: Setting Up the Menu View +-------------------------------- + +.. image:: menu_03.png + :width: 50% + +In this step we will setup the display buttons of the actual menu. The code +written in this section is written for ``MenuView`` + +Initialising the Buttons +~~~~~~~~~~~~~~~~~~~~~~~~ + +First we setup buttons for resume, starting a new game, volume, options and exit. + +.. literalinclude:: menu_03.py + :caption: Initialising the Buttons + :lines: 67-72 + +Displaying the Buttons in a Grid +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After setting up the buttons we add them to ``UIGridLayout``, so that they can +displayed in a grid like manner. + +.. literalinclude:: menu_03.py + :caption: Setting up the Grid + :lines: 74-90 + +Final code for the ``__init__`` method after these. + +.. literalinclude:: menu_03.py + :caption: __init__ + :pyobject: MenuView.__init__ + +Program Listings +~~~~~~~~~~~~~~~~ + +* :ref:`menu_03` |larr| Where we are right now +* :ref:`menu_03_diff` |larr| What we changed to get here + + +Step 4: Configuring the Menu Buttons +------------------------------------ + +.. image:: menu_04.png + :width: 50% + +We basically add event listener for ``on_click`` for buttons. + +Adding ``on_click`` Callback for Resume, Start New Game and Exit +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First we will add the event listener to resume, start_new_game and exit button +as they don't have much to explain. + +.. literalinclude:: menu_04.py + :caption: Adding callback for button events 1 + :lines: 94-107 + +Adding ``on_click`` Callback for Volume and Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now we need to implement an actual menu for volume and options, for that we have +to make a class that acts like a window. Using ``UIMouseFilterMixin`` we catch +all the events happening for the parent and respond nothing to them. Thus +making it act like a window/view. + +.. literalinclude:: menu_04.py + :caption: Making a Fake Window. + :pyobject: SubMenu + +We have got ourselves a fake window currently. We now, pair it up with the +volume and options button to trigger it when they are clicked. + +.. literalinclude:: menu_04.py + :caption: Adding callback for button events 2 + :lines: 109-123 + +Program Listings +~~~~~~~~~~~~~~~~ + +* :ref:`menu_04` |larr| Where we are right now +* :ref:`menu_04_diff` |larr| What we changed to get here + +Step 5: Finalising the Fake Window aka the Sub Menu +--------------------------------------------------- + +.. image:: menu_05.png + :width: 50% + +We finalise the menu or you can call it the last step! + +Editing the Parameters for the Sub Menu +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We will edit the parameters for the sub menu to suit our needs. Will explain +later why are those parameters needed. + +.. literalinclude:: menu_05.py + :caption: Editing parameters + :lines: 153-156 + +We also need to change accordingly the places where we have used this class i.e +options and volume ``on_click`` event listener. The layer parameter being set +1, means that this layer is always drawn on top i.e its the first layer. + +.. literalinclude:: menu_05.py + :caption: Editing arguments + :lines: 109-131 + +Now you might be getting a little idea why we have edited the parameters but + follow on to actually know the reason. + + +Adding a Title label +-------------------- + +We will be adding a ``UILabel`` that explains the menu. ``UISpace`` is a widget +that can be used to add space around some widget, you can set its color to the +background color so it appears invisible. + +.. literalinclude:: menu_05.py + :caption: Adding title label + :lines: 179-181 + +Adding it to the widget layout. + +.. literalinclude:: menu_05.py + :caption: Adding title label to the layout + :lines: 213-215 + + +Adding a Input Field +~~~~~~~~~~~~~~~~~~~~~ + +We will use ``UIInputText`` to add an input field. The ``with_border()`` +function creates a border around the widget with color(default argument is +black) black and thickness(default argument is 2px) 2px. Add this just below +the title label. + +.. literalinclude:: menu_05.py + :caption: Adding input field + :lines: 183 + +Adding it to the widget layout. + +.. literalinclude:: menu_05.py + :caption: Adding input field to the layout + :lines: 213-216 + :emphasize-lines: 4 + +If you paid attention when we defined the ``input_text`` variable we passed the +``text`` parameter with our ``input_text_default`` argument. We basically added +those parameters in our sub menu so that it can be used by both volume and +options button, with texts respecting their names. We will repeat this again +in the last also for those of you who are skipping through this section :P. + +Adding a Toggle Button +~~~~~~~~~~~~~~~~~~~~~~ + +Don't go on the section title much, in arcade the ``UITextureToggle`` is not +really a button it switches between two textures when clicked. Yes, it +functions like a button but by "is not really a button" we meant that it +doesn't inherits the button class. We also pair it up horizontally with the +toggle label. + +.. literalinclude:: menu_05.py + :caption: Adding toggle button + :lines: 189-201 + +Adding it to the widget layout. Add this line after you have added the input +field. + +.. literalinclude:: menu_05.py + :caption: Adding toggle button to the layout + :lines: 217 + +Adding a Dropdowm +~~~~~~~~~~~~~~~~~ + +We add a dropdowm by using ``UIDropdown``. + +.. literalinclude:: menu_05.py + :caption: Adding dropdown + :lines: 203-204 + +Adding it to the widget layout. + +.. literalinclude:: menu_05.py + :caption: Adding dropdown to the layout + :lines: 218 + +Adding a Slider +~~~~~~~~~~~~~~~ + +The final widget. In arcade you can use ``UISlider`` to implement a slider. +Theres a functionality to style the slider, this is also present for +``UIFlatButton`` and ``UITextureButton``. + +.. literalinclude:: menu_05.py + :caption: Adding slider + :lines: 206-207 + +Adding it to the widget layout. + +.. literalinclude:: menu_05.py + :caption: Adding slider to the layout + :lines: 219-220 + +Finishing touches +~~~~~~~~~~~~~~~~~ + +As we mentioned earlier, to explain the use of those parameters to the class. +We basically used them so it can be used by both options and volume as we +wanted to have different text for both. +For those who have read the full tutorial line-by-line; +'They will never know'. :D. +We also recommend to see the full code for this section. + + +Program Listings +~~~~~~~~~~~~~~~~ + +* :ref:`menu_05` |larr| Where we are right now +* :ref:`menu_05_diff` |larr| What we changed to get here diff --git a/doc/tutorials/menu/menu.gif b/doc/tutorials/menu/menu.gif new file mode 100644 index 0000000000..66f84bd86f Binary files /dev/null and b/doc/tutorials/menu/menu.gif differ diff --git a/doc/tutorials/menu/menu_01.png b/doc/tutorials/menu/menu_01.png new file mode 100644 index 0000000000..c8f2990eee Binary files /dev/null and b/doc/tutorials/menu/menu_01.png differ diff --git a/doc/tutorials/menu/menu_01.py b/doc/tutorials/menu/menu_01.py new file mode 100644 index 0000000000..513e1581e4 --- /dev/null +++ b/doc/tutorials/menu/menu_01.py @@ -0,0 +1,38 @@ +""" +Menu. + +Shows the usage of almost every gui widget, switching views and making a modal. +""" +import arcade + +# Screen title and size +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 +SCREEN_TITLE = "Making a Menu" + + +class MainView(arcade.View): + """ Main application class.""" + + def __init__(self): + super().__init__() + + def on_show_view(self): + """ This is run once when we switch to this view """ + arcade.set_background_color(arcade.color.DARK_BLUE_GRAY) + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + + +def main(): + window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True) + main_view = MainView() + window.show_view(main_view) + arcade.run() + + +if __name__ == "__main__": + main() diff --git a/doc/tutorials/menu/menu_01.rst b/doc/tutorials/menu/menu_01.rst new file mode 100644 index 0000000000..8aafbe8742 --- /dev/null +++ b/doc/tutorials/menu/menu_01.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_01: + +menu_01.py Full Listing +------------------------------------- + +.. literalinclude:: menu_01.py + :caption: menu_01.py + :linenos: diff --git a/doc/tutorials/menu/menu_02.png b/doc/tutorials/menu/menu_02.png new file mode 100644 index 0000000000..a0291b6cd8 Binary files /dev/null and b/doc/tutorials/menu/menu_02.png differ diff --git a/doc/tutorials/menu/menu_02.py b/doc/tutorials/menu/menu_02.py new file mode 100644 index 0000000000..e0e5357f36 --- /dev/null +++ b/doc/tutorials/menu/menu_02.py @@ -0,0 +1,99 @@ +""" +Menu. + +Shows the usage of almost every gui widget, switching views and making a modal. +""" +import arcade +import arcade.gui + +# Screen title and size +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 +SCREEN_TITLE = "Making a Menu" + + +class MainView(arcade.View): + """This is the class where your normal game would go.""" + + def __init__(self): + super().__init__() + + self.manager = arcade.gui.UIManager() + + switch_menu_button = arcade.gui.UIFlatButton(text="Pause", width=250) + + # Initialise the button with an on_click event. + @switch_menu_button.event("on_click") + def on_click_switch_button(event): + # Passing the main view into menu view as an argument. + menu_view = MenuView(self) + self.window.show_view(menu_view) + + # Use the anchor to position the button on the screen. + self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) + + self.anchor.add( + anchor_x="center_x", + anchor_y="center_y", + child=switch_menu_button, + ) + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + arcade.set_background_color(arcade.color.DARK_BLUE_GRAY) + + # Enable the UIManager when the view is showm. + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + + # Draw the manager. + self.manager.draw() + + +class MenuView(arcade.View): + """Main menu view class.""" + + def __init__(self, main_view): + super().__init__() + + self.manager = arcade.gui.UIManager() + + self.main_view = main_view + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + + # Makes the background darker + arcade.set_background_color([rgb - 50 for rgb in arcade.color.DARK_BLUE_GRAY]) + + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + + # Clear the screen + self.clear() + self.manager.draw() + + +def main(): + window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True) + main_view = MainView() + window.show_view(main_view) + arcade.run() + + +if __name__ == "__main__": + main() diff --git a/doc/tutorials/menu/menu_02.rst b/doc/tutorials/menu/menu_02.rst new file mode 100644 index 0000000000..40b1dd86e5 --- /dev/null +++ b/doc/tutorials/menu/menu_02.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_02: + +menu_02.py Full Listing +------------------------------------- + +.. literalinclude:: menu_02.py + :caption: menu_02.py + :linenos: diff --git a/doc/tutorials/menu/menu_02_diff.rst b/doc/tutorials/menu/menu_02_diff.rst new file mode 100644 index 0000000000..462319ed73 --- /dev/null +++ b/doc/tutorials/menu/menu_02_diff.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_02_diff: + +menu_02.py Diff +------------------------------------- + +.. literalinclude:: menu_02.py + :caption: menu_02.py + :diff: menu_01.py diff --git a/doc/tutorials/menu/menu_03.png b/doc/tutorials/menu/menu_03.png new file mode 100644 index 0000000000..053aa29d76 Binary files /dev/null and b/doc/tutorials/menu/menu_03.png differ diff --git a/doc/tutorials/menu/menu_03.py b/doc/tutorials/menu/menu_03.py new file mode 100644 index 0000000000..563f5e19a2 --- /dev/null +++ b/doc/tutorials/menu/menu_03.py @@ -0,0 +1,124 @@ +""" +Menu. + +Shows the usage of almost every gui widget, switching views and making a modal. +""" +import arcade +import arcade.gui + +# Screen title and size +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 +SCREEN_TITLE = "Making a Menu" + + +class MainView(arcade.View): + """This is the class where your normal game would go.""" + + def __init__(self): + super().__init__() + + self.manager = arcade.gui.UIManager() + + switch_menu_button = arcade.gui.UIFlatButton(text="Pause", width=150) + + # Initialise the button with an on_click event. + @switch_menu_button.event("on_click") + def on_click_switch_button(event): + # Passing the main view into menu view as an argument. + menu_view = MenuView(self) + self.window.show_view(menu_view) + + # Use the anchor to position the button on the screen. + self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) + + self.anchor.add( + anchor_x="center_x", + anchor_y="center_y", + child=switch_menu_button, + ) + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + arcade.set_background_color(arcade.color.DARK_BLUE_GRAY) + + # Enable the UIManager when the view is showm. + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + + # Draw the manager. + self.manager.draw() + + +class MenuView(arcade.View): + """Main menu view class.""" + + def __init__(self, main_view): + super().__init__() + + self.manager = arcade.gui.UIManager() + + resume = arcade.gui.UIFlatButton(text="Resume", width=150) + start_new_game = arcade.gui.UIFlatButton(text="Start New Game", width=150) + volume = arcade.gui.UIFlatButton(text="Volume", width=150) + options = arcade.gui.UIFlatButton(text="Options", width=150) + + exit = arcade.gui.UIFlatButton(text="Exit", width=320) + + # Initialise a grid in which widgets can be arranged. + self.grid = arcade.gui.UIGridLayout(column_count=2, row_count=3, horizontal_spacing=20, vertical_spacing=20) + + # Adding the buttons to the layout. + self.grid.add(resume, col_num=0, row_num=0) + self.grid.add(start_new_game, col_num=1, row_num=0) + self.grid.add(volume, col_num=0, row_num=1) + self.grid.add(options, col_num=1, row_num=1) + self.grid.add(exit, col_num=0, row_num=2, col_span=2) + + self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) + + self.anchor.add( + anchor_x="center_x", + anchor_y="center_y", + child=self.grid, + ) + + self.main_view = main_view + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + + # Makes the background darker + arcade.set_background_color([rgb - 50 for rgb in arcade.color.DARK_BLUE_GRAY]) + + # Enable the UIManager when the view is showm. + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + self.manager.draw() + + +def main(): + window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True) + main_view = MainView() + window.show_view(main_view) + arcade.run() + + +if __name__ == "__main__": + main() diff --git a/doc/tutorials/menu/menu_03.rst b/doc/tutorials/menu/menu_03.rst new file mode 100644 index 0000000000..e6a599a906 --- /dev/null +++ b/doc/tutorials/menu/menu_03.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_03: + +menu_03.py Full Listing +------------------------------------- + +.. literalinclude:: menu_03.py + :caption: menu_03.py + :linenos: diff --git a/doc/tutorials/menu/menu_03_diff.rst b/doc/tutorials/menu/menu_03_diff.rst new file mode 100644 index 0000000000..31fb748e93 --- /dev/null +++ b/doc/tutorials/menu/menu_03_diff.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_03_diff: + +menu_03.py Diff +------------------------------------- + +.. literalinclude:: menu_03.py + :caption: menu_03.py + :diff: menu_02.py diff --git a/doc/tutorials/menu/menu_04.png b/doc/tutorials/menu/menu_04.png new file mode 100644 index 0000000000..5e2b2fbc7d Binary files /dev/null and b/doc/tutorials/menu/menu_04.png differ diff --git a/doc/tutorials/menu/menu_04.py b/doc/tutorials/menu/menu_04.py new file mode 100644 index 0000000000..b0fdd37b14 --- /dev/null +++ b/doc/tutorials/menu/menu_04.py @@ -0,0 +1,193 @@ +""" +Menu. + +Shows the usage of almost every gui widget, switching views and making a modal. +""" +import arcade +import arcade.gui + +# Screen title and size +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 +SCREEN_TITLE = "Making a Menu" + + +class MainView(arcade.View): + """This is the class where your normal game would go.""" + + def __init__(self): + super().__init__() + + self.manager = arcade.gui.UIManager() + + switch_menu_button = arcade.gui.UIFlatButton(text="Pause", width=150) + + # Initialise the button with an on_click event. + @switch_menu_button.event("on_click") + def on_click_switch_button(event): + # Passing the main view into menu view as an argument. + menu_view = MenuView(self) + self.window.show_view(menu_view) + + # Use the anchor to position the button on the screen. + self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) + + self.anchor.add( + anchor_x="center_x", + anchor_y="center_y", + child=switch_menu_button, + ) + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + arcade.set_background_color(arcade.color.DARK_BLUE_GRAY) + + # Enable the UIManager when the view is showm. + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + + # Draw the manager. + self.manager.draw() + + +class MenuView(arcade.View): + """Main menu view class.""" + + def __init__(self, main_view): + super().__init__() + + self.manager = arcade.gui.UIManager() + + resume_button = arcade.gui.UIFlatButton(text="Resume", width=150) + start_new_game_button = arcade.gui.UIFlatButton(text="Start New Game", width=150) + volume_button = arcade.gui.UIFlatButton(text="Volume", width=150) + options_button = arcade.gui.UIFlatButton(text="Options", width=150) + + exit_button = arcade.gui.UIFlatButton(text="Exit", width=320) + + # Initialise a grid in which widgets can be arranged. + self.grid = arcade.gui.UIGridLayout(column_count=2, row_count=3, horizontal_spacing=20, vertical_spacing=20) + + # Adding the buttons to the layout. + self.grid.add(resume_button, col_num=0, row_num=0) + self.grid.add(start_new_game_button, col_num=1, row_num=0) + self.grid.add(volume_button, col_num=0, row_num=1) + self.grid.add(options_button, col_num=1, row_num=1) + self.grid.add(exit_button, col_num=0, row_num=2, col_span=2) + + self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) + + self.anchor.add( + anchor_x="center_x", + anchor_y="center_y", + child=self.grid, + ) + + self.main_view = main_view + + @resume_button.event("on_click") + def on_click_resume_button(event): + # Pass already created view because we are resuming. + self.window.show_view(self.main_view) + + @start_new_game_button.event("on_click") + def on_click_start_new_game_button(event): + # Create a new view because we are starting a new game. + main_view = MainView() + self.window.show_view(main_view) + + @exit_button.event("on_click") + def on_click_exit_button(event): + arcade.exit() + + @volume_button.event("on_click") + def on_click_volume_button(event): + volume_menu = SubMenu() + self.manager.add( + volume_menu, + layer=1 + ) + + @options_button.event("on_click") + def on_click_options_button(event): + options_menu = SubMenu() + self.manager.add( + options_menu, + layer=1 + ) + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + + # Makes the background darker + arcade.set_background_color([rgb - 50 for rgb in arcade.color.DARK_BLUE_GRAY]) + + # Enable the UIManager when the view is showm. + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + self.manager.draw() + + +class SubMenu(arcade.gui.UIMouseFilterMixin, arcade.gui.UIAnchorLayout): + """Acts like a fake view/window.""" + + def __init__(self, ): + super().__init__(size_hint=(1, 1)) + + # Setup frame which will act like the window. + frame = self.add(arcade.gui.UIAnchorLayout(width=300, height=400, size_hint=None)) + frame.with_padding(all=20) + + # Add a background to the window. + frame.with_background(texture=arcade.gui.NinePatchTexture( + left=7, + right=7, + bottom=7, + top=7, + texture=arcade.load_texture( + ":resources:gui_basic_assets/window/dark_blue_gray_panel.png" + ) + )) + + back_button = arcade.gui.UIFlatButton(text="Back", width=250) + # The type of event listener we used earlier for the button will not work here. + back_button.on_click = self.on_click_back_button + + # Internal widget layout to handle widgets in this class. + widget_layout = arcade.gui.UIBoxLayout(align="left", space_between=10) + + widget_layout.add(back_button) + + frame.add(child=widget_layout, anchor_x="center_x", anchor_y="top") + + def on_click_back_button(self, event): + # Removes the widget from the manager. + # After this the manager will respond to its events like it previously did. + self.parent.remove(self) + + +def main(): + window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True) + main_view = MainView() + window.show_view(main_view) + arcade.run() + + +if __name__ == "__main__": + main() diff --git a/doc/tutorials/menu/menu_04.rst b/doc/tutorials/menu/menu_04.rst new file mode 100644 index 0000000000..f4491dfda9 --- /dev/null +++ b/doc/tutorials/menu/menu_04.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_04: + +menu_04.py Full Listing +------------------------------------- + +.. literalinclude:: menu_04.py + :caption: menu_04.py + :linenos: diff --git a/doc/tutorials/menu/menu_04_diff.rst b/doc/tutorials/menu/menu_04_diff.rst new file mode 100644 index 0000000000..03915608c4 --- /dev/null +++ b/doc/tutorials/menu/menu_04_diff.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_04_diff: + +menu_04.py Diff +------------------------------------- + +.. literalinclude:: menu_04.py + :caption: menu_04.py + :diff: menu_04.py diff --git a/doc/tutorials/menu/menu_05.png b/doc/tutorials/menu/menu_05.png new file mode 100644 index 0000000000..5aa2c929f3 Binary files /dev/null and b/doc/tutorials/menu/menu_05.png differ diff --git a/doc/tutorials/menu/menu_05.py b/doc/tutorials/menu/menu_05.py new file mode 100644 index 0000000000..d5c5ba6dcf --- /dev/null +++ b/doc/tutorials/menu/menu_05.py @@ -0,0 +1,242 @@ +""" +Menu. + +Shows the usage of almost every gui widget, switching views and making a modal. +""" +import arcade +import arcade.gui + +# Screen title and size +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 +SCREEN_TITLE = "Making a Menu" + + +class MainView(arcade.View): + """This is the class where your normal game would go.""" + + def __init__(self): + super().__init__() + + self.manager = arcade.gui.UIManager() + + switch_menu_button = arcade.gui.UIFlatButton(text="Pause", width=150) + + # Initialise the button with an on_click event. + @switch_menu_button.event("on_click") + def on_click_switch_button(event): + # Passing the main view into menu view as an argument. + menu_view = MenuView(self) + self.window.show_view(menu_view) + + # Use the anchor to position the button on the screen. + self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) + + self.anchor.add( + anchor_x="center_x", + anchor_y="center_y", + child=switch_menu_button, + ) + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + arcade.set_background_color(arcade.color.DARK_BLUE_GRAY) + + # Enable the UIManager when the view is showm. + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + + # Draw the manager. + self.manager.draw() + + +class MenuView(arcade.View): + """Main menu view class.""" + + def __init__(self, main_view): + super().__init__() + + self.manager = arcade.gui.UIManager() + + resume_button = arcade.gui.UIFlatButton(text="Resume", width=150) + start_new_game_button = arcade.gui.UIFlatButton(text="Start New Game", width=150) + volume_button = arcade.gui.UIFlatButton(text="Volume", width=150) + options_button = arcade.gui.UIFlatButton(text="Options", width=150) + + exit_button = arcade.gui.UIFlatButton(text="Exit", width=320) + + # Initialise a grid in which widgets can be arranged. + self.grid = arcade.gui.UIGridLayout(column_count=2, row_count=3, horizontal_spacing=20, vertical_spacing=20) + + # Adding the buttons to the layout. + self.grid.add(resume_button, col_num=0, row_num=0) + self.grid.add(start_new_game_button, col_num=1, row_num=0) + self.grid.add(volume_button, col_num=0, row_num=1) + self.grid.add(options_button, col_num=1, row_num=1) + self.grid.add(exit_button, col_num=0, row_num=2, col_span=2) + + self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) + + self.anchor.add( + anchor_x="center_x", + anchor_y="center_y", + child=self.grid, + ) + + self.main_view = main_view + + @resume_button.event("on_click") + def on_click_resume_button(event): + # Pass already created view because we are resuming. + self.window.show_view(self.main_view) + + @start_new_game_button.event("on_click") + def on_click_start_new_game_button(event): + # Create a new view because we are starting a new game. + main_view = MainView() + self.window.show_view(main_view) + + @exit_button.event("on_click") + def on_click_exit_button(event): + arcade.exit() + + @volume_button.event("on_click") + def on_click_volume_button(event): + volume_menu = SubMenu( + "Volume Menu", "How do you like your volume?", "Enable Sound", + ["Play: Rock", "Play: Punk", "Play: Pop"], + "Adjust Volume", + ) + self.manager.add( + volume_menu, + layer=1 + ) + + @options_button.event("on_click") + def on_click_options_button(event): + options_menu = SubMenu( + "Funny Menu", "Too much fun here", "Fun?", + ["Make Fun", "Enjoy Fun", "Like Fun"], + "Adjust Fun", + ) + self.manager.add( + options_menu, + layer=1 + ) + + def on_hide_view(self): + # Disable the UIManager when the view is hidden. + self.manager.disable() + + def on_show_view(self): + """ This is run once when we switch to this view """ + + # Makes the background darker + arcade.set_background_color([rgb - 50 for rgb in arcade.color.DARK_BLUE_GRAY]) + + # Enable the UIManager when the view is showm. + self.manager.enable() + + def on_draw(self): + """ Render the screen. """ + # Clear the screen + self.clear() + self.manager.draw() + + +class SubMenu(arcade.gui.UIMouseFilterMixin, arcade.gui.UIAnchorLayout): + """Acts like a fake view/window.""" + + def __init__(self, title: str, input_text: str, toggle_label: str, dropdown_options: list[str], slider_label: str): + super().__init__(size_hint=(1, 1)) + + # Setup frame which will act like the window. + frame = self.add(arcade.gui.UIAnchorLayout(width=300, height=400, size_hint=None)) + frame.with_padding(all=20) + + # Add a background to the window. + # Nine patch smoothes the edges. + frame.with_background(texture=arcade.gui.NinePatchTexture( + left=7, + right=7, + bottom=7, + top=7, + texture=arcade.load_texture( + ":resources:gui_basic_assets/window/dark_blue_gray_panel.png" + ) + )) + + back_button = arcade.gui.UIFlatButton(text="Back", width=250) + # The type of event listener we used earlier for the button will not work here. + back_button.on_click = self.on_click_back_button + + title_label = arcade.gui.UILabel(text=title, align="center", font_size=20, multiline=False) + # Adding some extra space around the title. + title_label_space = arcade.gui.UISpace(height=30, color=arcade.color.DARK_BLUE_GRAY) + + input_text_widget = arcade.gui.UIInputText(text=input_text, width=250).with_border() + + # Load the on-off textures. + on_texture = arcade.load_texture(":resources:gui_basic_assets/toggle/circle_switch_on.png") + off_texture = arcade.load_texture(":resources:gui_basic_assets/toggle/circle_switch_off.png") + + # Create the on-off toggle and a label + toggle_label = arcade.gui.UILabel(text=toggle_label) + toggle = arcade.gui.UITextureToggle( + on_texture=on_texture, + off_texture=off_texture, + width=20, + height=20 + ) + + # Align toggle and label horizontally next to each other + toggle_group = arcade.gui.UIBoxLayout(vertical=False, space_between=5) + toggle_group.add(toggle) + toggle_group.add(toggle_label) + + # Create dropdown with a specified default. + dropdown = arcade.gui.UIDropdown(default=dropdown_options[0], options=dropdown_options, height=20, width=250) + + slider_label = arcade.gui.UILabel(text=slider_label) + pressed_style = arcade.gui.UISlider.UIStyle(filled_bar=arcade.color.GREEN, unfilled_bar=arcade.color.RED) + default_style = arcade.gui.UISlider.UIStyle() + style_dict = {"press": pressed_style, "normal": default_style, "hover": default_style, "disabled": default_style} + # Configuring the styles is optional. + slider = arcade.gui.UISlider(value=50, width=250, style=style_dict) + + widget_layout = arcade.gui.UIBoxLayout(align="left", space_between=10) + widget_layout.add(title_label) + widget_layout.add(title_label_space) + widget_layout.add(input_text_widget) + widget_layout.add(toggle_group) + widget_layout.add(dropdown) + widget_layout.add(slider_label) + widget_layout.add(slider) + + widget_layout.add(back_button) + + frame.add(child=widget_layout, anchor_x="center_x", anchor_y="top") + + def on_click_back_button(self, event): + # Removes the widget from the manager. + # After this the manager will respond to its events like it previously did. + self.parent.remove(self) + + +def main(): + window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True) + main_view = MainView() + window.show_view(main_view) + arcade.run() + + +if __name__ == "__main__": + main() diff --git a/doc/tutorials/menu/menu_05.rst b/doc/tutorials/menu/menu_05.rst new file mode 100644 index 0000000000..faab4a0091 --- /dev/null +++ b/doc/tutorials/menu/menu_05.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_05: + +menu_05.py Full Listing +------------------------------------- + +.. literalinclude:: menu_05.py + :caption: menu_05.py + :linenos: diff --git a/doc/tutorials/menu/menu_05_diff.rst b/doc/tutorials/menu/menu_05_diff.rst new file mode 100644 index 0000000000..1a5846b54c --- /dev/null +++ b/doc/tutorials/menu/menu_05_diff.rst @@ -0,0 +1,10 @@ +:orphan: + +.. _menu_05_diff: + +menu_05.py Diff +------------------------------------- + +.. literalinclude:: menu_05.py + :caption: menu_05.py + :diff: menu_04.py