Skip to content

Commit

Permalink
Add hidden password example
Browse files Browse the repository at this point in the history
  • Loading branch information
eruvanos committed Aug 31, 2024
1 parent 46dde95 commit 8763efb
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 31 deletions.
105 changes: 77 additions & 28 deletions arcade/examples/gui/exp_hidden_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
This example demonstrates how to create a custom text input
which hides the contents behind a custom character, as often
required for login screens
required for login screens.
Due to a bug in the current version of pyglet, the example uses ENTER to switch
fields instead of TAB. This will be fixed in future versions.
(https://github.com/pyglet/pyglet/issues/1197)
If arcade and Python are properly installed, you can run this example with:
python -m arcade.examples.gui.exp_hidden_password
Expand All @@ -11,55 +15,100 @@
from __future__ import annotations

import arcade
from arcade.gui import UIManager, UIInputText, UIOnClickEvent
from arcade.gui import UIInputText, UIOnClickEvent, UIView
from arcade.gui.experimental.password_input import UIPasswordInput
from arcade.gui.widgets.buttons import UIFlatButton
from arcade.gui.widgets.layout import UIGridLayout, UIAnchorLayout
from arcade.gui.widgets.text import UILabel
from arcade import resources

# Load kenny fonts shipped with arcade
resources.load_system_fonts()


class MyView(arcade.View):
class MyView(UIView):
def __init__(self):
super().__init__()
self.ui = UIManager()
self.background_color = arcade.uicolor.BLUE_BELIZE_HOLE

grid = UIGridLayout(
size_hint=(0, 0), # wrap children
row_count=3, # user, pw and login button
row_count=5, # title | user, pw | login button
column_count=2, # label and input field
vertical_spacing=10,
horizontal_spacing=5,
)
grid.with_padding(all=50)
grid.with_background(color=arcade.uicolor.GREEN_GREEN_SEA)

title = grid.add(
UILabel(text="Login", width=150, font_size=20, font_name="Kenney Future"),
column=0,
row=0,
column_span=2,
)
title.with_padding(bottom=20)

grid.add(UILabel(text="Username:", width=80), column=0, row=0)
self.username_input = grid.add(UIInputText(), column=1, row=0)

grid.add(UILabel(text="Password:", width=80), column=0, row=1)
self.password_input = grid.add(UIPasswordInput(), column=1, row=1)
grid.add(UILabel(text="Username:", width=80, font_name="Kenney Future"), column=0, row=1)
self.username_input = grid.add(
UIInputText(width=150, font_name="Kenney Future"), column=1, row=1
)

self.login_button = grid.add(UIFlatButton(text="Login"), column=0, row=2, column_span=2)
grid.add(UILabel(text="Password:", width=80, font_name="Kenney Future"), column=0, row=2)
self.password_input = grid.add(
UIPasswordInput(width=150, font_name="Kenney Future"), column=1, row=2
)
self.password_input.with_background(color=arcade.uicolor.GREEN_GREEN_SEA)
# set background to prevent full render on blinking caret

self.login_button = grid.add(
UIFlatButton(text="Login", height=30, width=150, size_hint=(1, None)),
column=0,
row=3,
column_span=2,
)
self.login_button.on_click = self.on_login

# add warning label
self.warning_label = grid.add(
UILabel(
text="Use [enter] to switch fields, then enter to login",
width=150,
font_size=10,
font_name="Kenney Future",
),
column=0,
row=4,
column_span=2,
)

anchor = UIAnchorLayout() # to center grid on screen
anchor.add(grid)

self.ui.add(anchor)

def on_login(self, event: UIOnClickEvent):
print(f"User logged in with: {self.username_input.text} {self.password_input.text}")

def on_show_view(self):
self.window.background_color = arcade.color.DARK_BLUE_GRAY
# Enable UIManager when view is shown to catch window events
self.ui.enable()

def on_hide_view(self):
# Disable UIManager when view gets inactive
self.ui.disable()

def on_draw(self):
self.clear()
self.ui.draw()
self.add_widget(anchor)

# activate username input field
self.username_input.activate()

def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
# if username field active, switch fields with enter
if self.username_input.active:
if symbol == arcade.key.ENTER:
self.username_input.deactivate()
self.password_input.activate()
return True
# if password field active, login with enter
elif self.password_input.active:
if symbol == arcade.key.ENTER:
self.password_input.deactivate()
self.on_login(None)
return True
return False

def on_login(self, event: UIOnClickEvent | None):
username = self.username_input.text.strip()
password = self.password_input.text.strip()
print(f"User logged in with: {username} {password}")


if __name__ == "__main__":
Expand Down
7 changes: 6 additions & 1 deletion arcade/gui/experimental/password_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@


class UIPasswordInput(UIInputText):
"""A password input field. The text is hidden with asterisks."""
"""A password input field. The text is hidden with asterisks.
Hint: It is recommended to set a background color to prevent full render cycles
when the caret blinks.
"""

def on_event(self, event: UIEvent) -> Optional[bool]:
"""Remove new lines from the input, which are not allowed in passwords."""
Expand Down
4 changes: 2 additions & 2 deletions arcade/gui/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class UIWidget(EventDispatcher, ABC):
rect: Rect = Property(LBWH(0, 0, 1, 1)) # type: ignore
visible: bool = Property(True) # type: ignore

size_hint: Optional[Tuple[float, float]] = Property(None) # type: ignore
size_hint: Optional[Tuple[float | None, float | None]] = Property(None) # type: ignore
size_hint_min: Optional[Tuple[float, float]] = Property(None) # type: ignore
size_hint_max: Optional[Tuple[float, float]] = Property(None) # type: ignore

Expand All @@ -83,7 +83,7 @@ def __init__(
height: float = 100,
children: Iterable["UIWidget"] = tuple(),
# Properties which might be used by layouts
size_hint: Optional[Tuple[float, float]] = None, # in percentage
size_hint: Optional[Tuple[float | None, float | None]] = None, # in percentage
size_hint_min: Optional[Tuple[float, float]] = None, # in pixel
size_hint_max: Optional[Tuple[float, float]] = None, # in pixel
**kwargs,
Expand Down
17 changes: 17 additions & 0 deletions doc/example_code/gui_exp_hidden_password.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
:orphan:

.. _gui_exp_hidden_password:

GUI Hidden Password
===================

The following example demonstrates how to make use of the experimental widget.

.. image:: images/gui_exp_hidden_password.png
:width: 600px
:align: center
:alt: Screen shot

.. literalinclude:: ../../arcade/examples/gui/exp_hidden_password.py
:caption: exp_hidden_password.py
:linenos:
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions doc/example_code/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,23 @@ Graphical User Interface

Not all existing examples made it into this section. You can find more under `Arcade GUI Examples <https://github.com/pythonarcade/arcade/tree/development/arcade/examples/gui>`_

Graphical User Interface (Experimentals)
----------------------------------------

.. figure:: images/thumbs/gui_exp_hidden_password.png
:figwidth: 170px
:target: gui_exp_hidden_password.html

:ref:`gui_exp_hidden_password`


.. note::

Experimental widgets are not yet part of the official release.
They are subject to change and may not be fully functional.

Feedback is very welcome, please let us know what you think about them.



Grid-Based Games
Expand Down

0 comments on commit 8763efb

Please sign in to comment.