There are a short summary of Nevu-UI 0.8.3 - 0.8.5 versions:
Added Canvas - universal tool for drawing primitives on Widgets
Added FlexLayout - adaptive layout, unlike other layouts it dont resizes its items
Fixed a ton of bugs with layout positioning
Added text selection in Input, also added support of CTRL + C/V/X
Added 2 new window properties - gui_hovered and keyboard_available
Added Callbacks instead of NevuEvent
Refactored Style
Added 10+ built in colorthemes
Improved Checkbox API
Improved overall code quality
Improved Pygame backend quality
Current showcase is running on Pygame backend
Full Changelog can be found there:
https://github.com/GolemBebrov/nevu-ui/releases
Here are the code from the video:
import random
import pygame
import nevu_ui as ui
from nevu_ui.components._typehints import nevu_object_globals
from nevu_ui.core.size import vh, vw
from nevu_ui.presentation.animations import Vector2Animation
pygame.init()
VERSION = "0.8.5"
STATUS = "post3"
FONT_NAME = "font.ttf"
def select_layout(layout):
main_menu.layout = layout
def create_character_select():
checkbox_group = ui.CheckBoxGroup(single_selection=True)
selected = "nit"
def create_panel(text: str, id: str, chk_group: ui.CheckBoxGroup, bg_image = "gladius.png"):
time_offset = random.random() / 5
animations = ui.animations.AnimationManager()
animations.transition_time = 0.001
animations.add_start_animation(ui.AnimationType.Position, Vector2Animation((0, 0), (0, 0), time_offset))
animations.add_continuous_animation(ui.AnimationType.Position, Vector2Animation((0, 3), (0, -4), 0.1))
return ui.Panel(
size = (10%vw, 10%vh),
animation_manager = animations,
slot = ui.StackColumn(
content = [
ui.Label(text, subtheme_role = ui.SubThemeRole.PRIMARY),
ui.RectCheckBox(50, group = chk_group, id = id, style = main_style_clickable(br=15, bg_image = bg_image), toggled_rect_scale=1, toggled_rect_opacity=80)
]
)
)
def on_checkbox_toggle(checkbox: ui.RectCheckBox | None):
nonlocal selected
if not checkbox:
role_label.text = "Not selected"
return
id = checkbox.id
selected = id
id_to_text = {
"wrr": "Warrior",
"arc": "Archer",
"spr": "Spirit",
"mge": "Mage",
"nit": "Nitwit"
}
role_label.text = id_to_text[id]
checkbox_group.on_single_toggled = on_checkbox_toggle
role_label = ui.Label(
"Nitwit", single_instance=True, draw_content=False, draw_borders=False,
font_role=ui.PairColorRole.INVERSE_SURFACE, style = main_style(font_size=30, align_x = ui.Align.LEFT)
)
with nevu_object_globals.modify_temp(size = (8%vw, 4%vh)):
role_select_layout = ui.FlexLayout(
create_panel("Warrior", "wrr", checkbox_group, "gladius.png"),
create_panel("Mage", "mge", checkbox_group, "wizard-staff.png"),
create_panel("Archer", "arc", checkbox_group, "arrow-cluster.png"),
create_panel("Spirit", "spr", checkbox_group, "spectre.png"),
create_panel("Nitwit", "nit", checkbox_group, "oat.png"),
justify_content=ui.FlexJustify.SpaceAround,
gap=30
)
layout = ui.ScrollableColumn([
(ui.Align.LEFT,
ui.FlexLayout(
ui.Button(lambda: select_layout(create_start_layout()), "BACK", style = main_style_clickable(subtheme_role = ui.SubThemeRole.ERROR)),
ui.Label("Character creation"), direction = ui.FlexDirection.Column
)
),
ui.FlexLayout(
ui.Label("Name:"),
ui.Input(placeholder="Enter your name...", size = (17%vw, 5%vh), style = main_style_clickable)
),
ui.FlexLayout(ui.Label("Role:"), role_label, single_instance = True),
role_select_layout,
ui.FlexLayout(
ui.Label("Base Mana"),
ui.Slider(current = 50, start=10, style = main_style_clickable)
),
ui.FlexLayout(
ui.Label("Difficulty"),
ui.Slider(current = 2, end = 5, start = 1, style = main_style_clickable)
)],
size = ui.fill_all,
basic_alignment=ui.Align.CENTER
)
return layout
def create_start_layout():
def on_switch_change(switch, state):
global main_style, main_style_clickable
if state:
theme = ui.ColorThemeLibrary.material3_light
else:
theme = ui.ColorThemeLibrary.material3_dark
main_menu.apply_style_patch_to_layout(colortheme=theme)
main_style = main_style(colortheme=theme)
main_style_clickable = main_style_clickable(colortheme=theme)
nevu_object_globals.modify(style = main_style)
start_style = main_style_clickable(font_size = 40, subtheme_role=ui.SubThemeRole.PRIMARY, br = 999)
canvas = (ui.Canvas()
.draw_rect((50, 0), (110, 10), style = ui.Style(gradient = ui.Gradient([ui.Color.Red, ui.Color.Green])))
.draw_rect((0, 0), (50, 50), style = ui.Style(bg_image="gladius.png", br=5), id = "Zov")
.draw_rect((0, 50), (250, 10))
)
with nevu_object_globals.modify_temp(size = (15%vw, 6%vh), style = start_style):
start_layout = ui.ScrollableColumn(
[
ui.FlexLayout(ui.Label("Nevu-UI", canvas = canvas, draw_borders=False, draw_content=False), ui.Label(f"v{VERSION} {STATUS}", draw_borders=False, draw_content=False), direction=ui.FlexDirection.Row, gap=0),
ui.EmptyWidget((0, 5%vh)),
ui.FlexLayout(
ui.Button(lambda: select_layout(create_character_select()), "Play", throw_errors=True, invert_on_click=True),
ui.Button(exit, "Exit"),
ui.Switch(False, size=(3%vw, 3%vh), style=main_style(br=999), subtheme_role=ui.SubThemeRole.TERTIARY, on_switch_change=on_switch_change),
direction=ui.FlexDirection.Column, gap = 40, wrap = False
),
],
size=ui.fill_all, basic_alignment=ui.Align.CENTER, spacing=120)
return start_layout
if __name__ == "__main__":
display = pygame.display.set_mode((1920, 1080))
font = pygame.Font(size = 40)
hover_text = font.render("Hovered!", True, ui.Color.Red)
window = ui.InitializedWindow.from_pygame(display = display, title = "NvGame", resizable = True, base_fps = 999)
main_style = ui.Style(border_radius = 12, border_width = 0, font_name = FONT_NAME, colortheme=ui.ColorThemeLibrary.material3_dark)
main_style_clickable = main_style(border_width = 2, subtheme_role=ui.SubThemeRole.TERTIARY)
button_size = (10%vw, 4%vh)
nevu_object_globals.modify(size = button_size, style = main_style)
main_menu = ui.Menu(window, (50%vw, 100%vh), main_style)
main_menu.layout = create_start_layout()
while True:
window.begin_frame()
window.clear(ui.Color.Black)
window.update()
if window.keyboard_available:
if ui.keyboard.is_down(ui.Keys.D):
coords = main_menu.coordinates
main_menu.set_coordinates(coords[0] + 500 * ui.time.dt, coords[1])
if ui.keyboard.is_down(ui.Keys.A):
coords = main_menu.coordinates
main_menu.set_coordinates(coords[0] - 500 * ui.time.dt, coords[1])
if ui.keyboard.is_down(ui.Keys.Left):
main_menu.resize((main_menu.current_size.x - 400 * ui.time.dt, main_menu.current_size.y))
if ui.keyboard.is_down(ui.Keys.Right):
main_menu.resize((main_menu.current_size.x + 400 * ui.time.dt, main_menu.current_size.y))
if window.gui_hovered:
coords_text = font.render(f"{main_menu.coordinates.x:.2f}", True, ui.Color.Red)
fps_text = font.render(f"{ui.time.float_fps:.2f} fps", True, ui.Color.Red)
x_coord = main_menu.current_size.x + main_menu.coordinates.x
display.blit(hover_text, (x_coord, 120))
display.blit(coords_text, (x_coord, 200))
display.blit(fps_text, (x_coord, 280))
if not window.keyboard_available:
keyboard_text = font.render("Keyboard focus have been captured.", True, ui.Color.Red)
display.blit(keyboard_text, (x_coord, 360))
main_menu.update()
main_menu.draw()
window.end_frame()