Skip to content

Commit 10143b9

Browse files
eruvanosJaecktec
andauthored
GUI push for 3.0 (#2351)
- New FlatUI colors under arcade.uicolor - Combined gui examples and proper documentation - Rewrite box layout to address sizing issues, when a child provided size_hint_min - GUI assets are now consistent in style and properly structured - Apply feedback about UIGridLayout parameter names - UIView provides add_widget method and a background property, which will be used for clearing screen - UIFlatButton provides multiple default styles - Consistent UITextureSlider parameter names, better default for height - UIAnchorLayouts do not restrict a child size to its own size, to allow scrolling use cases - UITextArea provides the option for pyglet attributed or html text formatting - UIInputText emits a on_change event, when text changed - Buttons default to Kenny font, when loaded --------- Co-authored-by: Jaecktec <constantin@jaeck.eu.org>
1 parent 3808448 commit 10143b9

File tree

111 files changed

+1987
-1765
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+1987
-1765
lines changed

CHANGELOG.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,14 @@ API in a way that is not compatible with how it was used in 2.6.
6767
* Removed `arcade.gui.widgets.UIPadding` this is now general available in `arcade.gui.widgets.UIWidget`
6868
* Removed `arcade.gui.widgets.UITexturePane` this is now general available in `arcade.gui.widgets.UIWidget`
6969
* Removed `arcade.gui.widgets.UIAnchorWidget` replaced by `arcade.gui.widgets.UIAnchorLayout`
70-
70+
* Resources
71+
* removed unsused resources from `resources/gui_basic_assets`
72+
* `items/shield_gold.png`
73+
* `items/sword_gold.png`
74+
* `slider_thumb.png`
75+
* `slider_track.png`
76+
* `toggle/switch_green.png`
77+
* `toggle/switch_red.png`
7178
### Featured Updates
7279

7380
* The texture atlas has been heavily reworked to be more efficient.
@@ -131,6 +138,10 @@ API in a way that is not compatible with how it was used in 2.6.
131138
* Update and add example code.
132139
* Iterable (providing direct children)
133140

141+
* Updated widgets
142+
* `arcade.gui.widgets.text.UIInputText` emits `on_change` event when new text input or set
143+
* `arcade.gui.widgets.slider.UITextureSlider` texture names changed to fit general naming pattern
144+
134145
* New widgets:
135146

136147
* `arcade.gui.widgets.dropdown.UIDropdown`
@@ -152,6 +163,8 @@ API in a way that is not compatible with how it was used in 2.6.
152163
* `arcade.gui.UIAnchorLayout`
153164
* `arcade.gui.UIGridLayout` [PR1478](https://github.com/pythonarcade/arcade/pull/1478)
154165

166+
* Added color consistent assets to `arcade.resources.gui_basic_assets`
167+
* Provide GUI friendly color constants in `arcade.uicolor`
155168
* Replace deprecated usage of `arcade.draw_text`
156169

157170
### Misc Changes

arcade/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ def configure_logging(level: int | None = None):
228228
# Module imports
229229
from arcade import color as color
230230
from arcade import csscolor as csscolor
231+
from arcade import uicolor as uicolor
231232
from arcade import camera as camera
232233
from arcade import key as key
233234
from arcade import resources as resources
@@ -387,6 +388,7 @@ def configure_logging(level: int | None = None):
387388
"rect",
388389
"color",
389390
"csscolor",
391+
"uicolor",
390392
"key",
391393
"resources",
392394
"types",
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""Demonstrates general setup.
2+
3+
If arcade and Python are properly installed, you can run this example with:
4+
python -m arcade.examples.gui.0_basic_setup
5+
6+
Content:
7+
- create a view manually creating UIManager and adding widgets
8+
- create a second view extending UIView and adding widgets
9+
10+
"""
11+
12+
import arcade
13+
from arcade.gui import (
14+
UIManager,
15+
UITextureButton,
16+
UIAnchorLayout,
17+
)
18+
from arcade.types import Color
19+
20+
# Preload textures, because they are mostly used multiple times, so they are not
21+
# loaded multiple times
22+
TEX_RED_BUTTON_NORMAL = arcade.load_texture(":resources:gui_basic_assets/button/red_normal.png")
23+
TEX_RED_BUTTON_HOVER = arcade.load_texture(":resources:gui_basic_assets/button/red_hover.png")
24+
TEX_RED_BUTTON_PRESS = arcade.load_texture(":resources:gui_basic_assets/button/red_press.png")
25+
26+
27+
class GreenView(arcade.View):
28+
"""Uses the arcade.View and shows how to integrate UIManager."""
29+
30+
def __init__(self):
31+
super().__init__()
32+
33+
# Create a UIManager
34+
self.ui = UIManager()
35+
36+
# Create a anchor layout, which can be used to position widgets on screen
37+
anchor = self.ui.add(UIAnchorLayout())
38+
39+
# Add a button switch to the other View.
40+
button = anchor.add(
41+
UITextureButton(
42+
text="Switch to blue view",
43+
texture=TEX_RED_BUTTON_NORMAL,
44+
texture_hovered=TEX_RED_BUTTON_HOVER,
45+
texture_pressed=TEX_RED_BUTTON_PRESS,
46+
on_click=lambda: self.window.show_view(self.window.views["other"]),
47+
)
48+
)
49+
50+
# add a button to switch to the blue view
51+
@button.event("on_click")
52+
def on_click(event):
53+
self.window.show_view(BlueView())
54+
55+
def on_show_view(self) -> None:
56+
self.ui.enable()
57+
58+
def on_hide_view(self) -> None:
59+
self.ui.disable()
60+
61+
def on_draw(self):
62+
# Clear the screen
63+
self.clear(color=Color(46, 204, 113))
64+
65+
# Add draw commands that should be below the UI
66+
# ...
67+
68+
self.ui.draw()
69+
70+
# Add draw commands that should be on top of the UI (uncommon)
71+
# ...
72+
73+
74+
class BlueView(arcade.gui.UIView):
75+
"""Uses the arcade.gui.UIView which takes care about the UIManager setup."""
76+
77+
def __init__(self):
78+
super().__init__()
79+
self.background_color = Color(52, 152, 219)
80+
81+
# Create a anchor layout, which can be used to position widgets on screen
82+
anchor = self.add_widget(UIAnchorLayout())
83+
84+
# Add a button switch to the other View.
85+
button = anchor.add(
86+
UITextureButton(
87+
text="Switch to green view",
88+
texture=TEX_RED_BUTTON_NORMAL,
89+
texture_hovered=TEX_RED_BUTTON_HOVER,
90+
texture_pressed=TEX_RED_BUTTON_PRESS,
91+
on_click=lambda: self.window.show_view(self.window.views["my"]),
92+
)
93+
)
94+
95+
# add a button to switch to the green view
96+
@button.event("on_click")
97+
def on_click(event):
98+
self.window.show_view(GreenView())
99+
100+
def on_draw_before_ui(self):
101+
# Add draw commands that should be below the UI
102+
pass
103+
104+
def on_draw_after_ui(self):
105+
# Add draw commands that should be on top of the UI (uncommon)
106+
pass
107+
108+
109+
if __name__ == "__main__":
110+
window = arcade.Window(title="GUI Example: Basic Setup")
111+
window.show_view(GreenView())
112+
window.run()

arcade/examples/gui/1_layouts.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
"""Demonstrates the use of layouts.
2+
3+
If arcade and Python are properly installed, you can run this example with:
4+
python -m arcade.examples.gui.1_layouts
5+
6+
Content:
7+
- Create a view with a description of layouts
8+
- Use UIAnchorLayout to position widgets relative to the screen edges
9+
- Use UIBoxLayout to position widgets in a horizontal or vertical line
10+
- Use UIGridLayout to position widgets in a grid
11+
12+
"""
13+
14+
from datetime import datetime
15+
16+
import arcade
17+
from arcade.gui import UIAnchorLayout
18+
from arcade.types import Color
19+
20+
arcade.resources.load_system_fonts()
21+
22+
DESCRIPTION = """How to place widgets on the screen?
23+
24+
Widgets can be manually placed on the screen by using the `add` method of the UIManager
25+
and setting the rect of a widget. This is useful for simple ui setups.
26+
27+
This approach requires to manually calculate the position of the
28+
widgets and handling screen resizes.
29+
30+
Another approach is to use layouts. Layouts are containers that automatically
31+
position widgets based on the layout rules. This example shows how to use
32+
the UIAnchorLayout, UIBoxLayout, and UIGridLayout.
33+
34+
UIAnchorLayout:
35+
- UIAnchorLayout is a layout that positions widgets relative to the screen edges.
36+
- Widgets can be anchored to the top, bottom, left, right, center, or any combination of these.
37+
- Widgets can be aligned to the anchor position with a pixel offset.
38+
39+
UIBoxLayout:
40+
- UIBoxLayout is a layout that positions widgets in a horizontal or vertical line.
41+
- Widgets can be aligned on the orthogonal axis.
42+
- Widgets can have a space between them.
43+
44+
UIGridLayout:
45+
- UIGridLayout is a layout that positions widgets in a grid.
46+
- Widgets can be placed in rows and columns.
47+
- Widgets can have a vertical and horizontal spacing.
48+
49+
The layouts calculate the size of the widgets based on the size_hint,
50+
size_hint_min, and size_hint_max.
51+
52+
- size_hint: A tuple of two floats that represent the relative size of the widget.
53+
- size_hint_min: A tuple of two floats that represent the minimum size of the widget.
54+
- size_hint_max: A tuple of two floats that represent the maximum size of the widget.
55+
56+
Layouts only resize widgets if the size_hint is set for the resprecitve axis.
57+
If the size_hint is not set, the widget will use its current size.
58+
59+
Some widgets calculate their minimum size based on their content like UILabel
60+
and layouts in general.
61+
"""
62+
63+
64+
class LayoutView(arcade.gui.UIView):
65+
"""This view demonstrates the use of layouts."""
66+
67+
def __init__(self):
68+
super().__init__()
69+
self.background_color = Color(52, 152, 219)
70+
71+
# Create a anchor layout, which can be used to position widgets on screen
72+
self.anchor = self.add_widget(UIAnchorLayout())
73+
74+
# Add describing text in center
75+
text_area = arcade.gui.UITextArea(
76+
text=DESCRIPTION,
77+
text_color=Color(236, 240, 241),
78+
font_name=("Lato", "proxima-nova", "Helvetica Neue", "Arial", "sans-serif"),
79+
font_size=12,
80+
size_hint=(0.5, 0.8),
81+
)
82+
self.anchor.add(text_area, anchor_x="center_x", anchor_y="center_y")
83+
text_area.with_border(color=Color(149, 165, 166))
84+
text_area.with_background(color=Color(149, 165, 166, 125))
85+
text_area.with_padding(left=5)
86+
87+
# add a grid layout with the window and grid size and grid position
88+
self.grid = arcade.gui.UIGridLayout(
89+
column_count=2,
90+
row_count=2,
91+
align_horizontal="left",
92+
)
93+
self.grid.with_background(color=Color(149, 165, 166))
94+
self.grid.with_border(color=Color(127, 140, 141))
95+
self.grid.with_padding(all=10)
96+
self.anchor.add(self.grid, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
97+
self.grid.add(
98+
arcade.gui.UILabel(text="Arcade: "),
99+
row=0,
100+
column=0,
101+
)
102+
self.grid.add(
103+
arcade.gui.UILabel(text=arcade.VERSION),
104+
row=0,
105+
column=1,
106+
)
107+
self.grid.add(
108+
arcade.gui.UILabel(text="Today: "),
109+
row=1,
110+
column=0,
111+
)
112+
self.grid.add(
113+
arcade.gui.UILabel(text=datetime.utcnow().isoformat()[:10]),
114+
row=1,
115+
column=1,
116+
)
117+
118+
# add a horizontal boxlayout with buttons
119+
h_box = arcade.gui.UIBoxLayout(space_between=20, vertical=False)
120+
self.anchor.add(h_box, anchor_x="center_x", anchor_y="bottom", align_y=5)
121+
122+
# Add a button to move the grid layout to top_right
123+
# top left
124+
move_left_button = arcade.gui.UIFlatButton(text="Top Left", width=150)
125+
move_left_button.disabled = True
126+
h_box.add(move_left_button)
127+
128+
@move_left_button.event("on_click")
129+
def _(event):
130+
self.anchor.remove(self.grid)
131+
self.anchor.add(self.grid, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
132+
move_left_button.disabled = True
133+
move_right_button.disabled = False
134+
135+
move_right_button = arcade.gui.UIFlatButton(text="Top Right", width=150)
136+
h_box.add(move_right_button)
137+
138+
@move_right_button.event("on_click")
139+
def _(event):
140+
self.anchor.remove(self.grid)
141+
self.anchor.add(self.grid, anchor_x="right", anchor_y="top", align_x=-10, align_y=-10)
142+
move_right_button.disabled = True
143+
move_left_button.disabled = False
144+
145+
def on_draw_before_ui(self):
146+
# Add draw commands that should be below the UI
147+
pass
148+
149+
def on_draw_after_ui(self):
150+
# Add draw commands that should be on top of the UI (uncommon)
151+
pass
152+
153+
154+
if __name__ == "__main__":
155+
window = arcade.Window(title="GUI Example: Layouts")
156+
window.show_view(LayoutView())
157+
window.run()

0 commit comments

Comments
 (0)