Skip to content

Commit 47fcbcb

Browse files
committed
Window controls/settings.
1 parent 42e8050 commit 47fcbcb

8 files changed

Lines changed: 1093 additions & 7 deletions

File tree

crates/processing_glfw/src/lib.rs

Lines changed: 360 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
from mewnala import *
2+
3+
TITLES = ["window controls", "🪟 hello", "still alive"]
4+
POSITIONS = [(100, 100), (300, 200), (50, 50)]
5+
SIZES = [(640, 480), (800, 600), (320, 240)]
6+
OPACITIES = [1.0, 0.85, 0.5]
7+
8+
state = {
9+
"title": 0,
10+
"position": 0,
11+
"size": 0,
12+
"opacity": 0,
13+
"resizable": True,
14+
"decorated": True,
15+
"on_top": False,
16+
"fullscreen": False,
17+
"show_at": -1,
18+
}
19+
20+
21+
def setup():
22+
size(640, 480)
23+
window_title(TITLES[0])
24+
25+
26+
def log(label):
27+
print(f"[frame {frame_count}] {label}")
28+
29+
30+
def cycle(key, label):
31+
state[key] = (state[key] + 1) % len(label)
32+
return state[key]
33+
34+
35+
def draw():
36+
if 0 <= state["show_at"] <= frame_count:
37+
window_visible(True)
38+
state["show_at"] = -1
39+
log("re-shown")
40+
41+
if key_just_pressed(KEY_T):
42+
idx = cycle("title", TITLES)
43+
window_title(TITLES[idx])
44+
log(f"title -> {TITLES[idx]!r}")
45+
46+
if key_just_pressed(KEY_M):
47+
idx = cycle("position", POSITIONS)
48+
x, y = POSITIONS[idx]
49+
window_move(x, y)
50+
log(f"moved to ({x}, {y})")
51+
52+
if key_just_pressed(KEY_W):
53+
idx = cycle("size", SIZES)
54+
w, h = SIZES[idx]
55+
window_resize(w, h)
56+
log(f"resized to {w}x{h}")
57+
58+
if key_just_pressed(KEY_O):
59+
idx = cycle("opacity", OPACITIES)
60+
window_opacity(OPACITIES[idx])
61+
log(f"opacity -> {OPACITIES[idx]}")
62+
63+
if key_just_pressed(KEY_R):
64+
state["resizable"] = not state["resizable"]
65+
window_resizable(state["resizable"])
66+
log(f"resizable -> {state['resizable']}")
67+
68+
if key_just_pressed(KEY_D):
69+
state["decorated"] = not state["decorated"]
70+
window_decorated(state["decorated"])
71+
log(f"decorated -> {state['decorated']}")
72+
73+
if key_just_pressed(KEY_A):
74+
state["on_top"] = not state["on_top"]
75+
window_always_on_top(state["on_top"])
76+
log(f"always-on-top -> {state['on_top']}")
77+
78+
if key_just_pressed(KEY_V):
79+
window_visible(False)
80+
state["show_at"] = frame_count + 60
81+
log("hidden for ~1s")
82+
83+
if key_just_pressed(KEY_I):
84+
window_iconify()
85+
log("iconified")
86+
87+
if key_just_pressed(KEY_X):
88+
window_maximize()
89+
log("maximized")
90+
91+
if key_just_pressed(KEY_N):
92+
window_restore()
93+
log("restored")
94+
95+
if key_just_pressed(KEY_F):
96+
state["fullscreen"] = not state["fullscreen"]
97+
full_screen(primary_monitor() if state["fullscreen"] else None)
98+
log(f"fullscreen -> {state['fullscreen']}")
99+
100+
if key_just_pressed(KEY_C):
101+
if (m := primary_monitor()) is not None:
102+
window_center_on(m)
103+
log(f"centered on {m.name!r}")
104+
105+
if key_just_pressed(KEY_P):
106+
if (m := primary_monitor()) is not None:
107+
window_position_on(m, 10, 10)
108+
log(f"workarea +(10, 10) — workarea={m.workarea}")
109+
110+
background(24)
111+
no_stroke()
112+
fill(80, 200, 200)
113+
rect(20, 20, width - 40, 40)
114+
fill(200, 80, 120)
115+
rect(20, 80, width - 40, 60)
116+
fill(120, 80, 200)
117+
rect(20, 160, width - 40, 60)
118+
fill(80, 200, 120)
119+
rect(20, 240, width - 40, 60)
120+
121+
fill(255, 220, 60)
122+
if width > 0 and height > 0:
123+
circle(window_x % width, window_y % height, 12)
124+
125+
126+
run()

crates/processing_pyo3/mewnala/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
"key_is_pressed",
4242
"display_width",
4343
"display_height",
44+
"window_x",
45+
"window_y",
4446
)
4547
)
4648

crates/processing_pyo3/src/lib.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,144 @@ mod mewnala {
844844
Ok(())
845845
}
846846

847+
#[pyfunction]
848+
#[pyo3(pass_module)]
849+
fn window_title(module: &Bound<'_, PyModule>, title: &str) -> PyResult<()> {
850+
let Some(graphics) = get_graphics(module)? else {
851+
return Ok(());
852+
};
853+
graphics.surface.set_title(title)
854+
}
855+
856+
#[pyfunction]
857+
#[pyo3(pass_module)]
858+
fn window_move(module: &Bound<'_, PyModule>, x: i32, y: i32) -> PyResult<()> {
859+
let Some(graphics) = get_graphics(module)? else {
860+
return Ok(());
861+
};
862+
graphics.surface.set_position(x, y)
863+
}
864+
865+
#[pyfunction]
866+
#[pyo3(pass_module)]
867+
fn window_resize(module: &Bound<'_, PyModule>, w: u32, h: u32) -> PyResult<()> {
868+
let Some(graphics) = get_graphics(module)? else {
869+
return Ok(());
870+
};
871+
::processing::prelude::surface_resize(graphics.surface.entity, w, h)
872+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
873+
}
874+
875+
#[pyfunction]
876+
#[pyo3(pass_module)]
877+
fn window_resizable(module: &Bound<'_, PyModule>, resizable: bool) -> PyResult<()> {
878+
let Some(graphics) = get_graphics(module)? else {
879+
return Ok(());
880+
};
881+
graphics.surface.set_resizable(resizable)
882+
}
883+
884+
#[pyfunction]
885+
#[pyo3(pass_module, signature = (monitor=None))]
886+
fn full_screen(
887+
module: &Bound<'_, PyModule>,
888+
monitor: Option<&crate::monitor::Monitor>,
889+
) -> PyResult<()> {
890+
let Some(graphics) = get_graphics(module)? else {
891+
return Ok(());
892+
};
893+
graphics.surface.set_fullscreen(monitor)
894+
}
895+
896+
#[pyfunction]
897+
#[pyo3(pass_module)]
898+
fn window_visible(module: &Bound<'_, PyModule>, visible: bool) -> PyResult<()> {
899+
let Some(graphics) = get_graphics(module)? else {
900+
return Ok(());
901+
};
902+
graphics.surface.set_visible(visible)
903+
}
904+
905+
#[pyfunction]
906+
#[pyo3(pass_module)]
907+
fn window_decorated(module: &Bound<'_, PyModule>, decorated: bool) -> PyResult<()> {
908+
let Some(graphics) = get_graphics(module)? else {
909+
return Ok(());
910+
};
911+
graphics.surface.set_decorated(decorated)
912+
}
913+
914+
#[pyfunction]
915+
#[pyo3(pass_module)]
916+
fn window_always_on_top(module: &Bound<'_, PyModule>, on_top: bool) -> PyResult<()> {
917+
let Some(graphics) = get_graphics(module)? else {
918+
return Ok(());
919+
};
920+
graphics.surface.set_always_on_top(on_top)
921+
}
922+
923+
#[pyfunction]
924+
#[pyo3(pass_module)]
925+
fn window_opacity(module: &Bound<'_, PyModule>, opacity: f32) -> PyResult<()> {
926+
let Some(graphics) = get_graphics(module)? else {
927+
return Ok(());
928+
};
929+
graphics.surface.set_opacity(opacity)
930+
}
931+
932+
#[pyfunction]
933+
#[pyo3(pass_module)]
934+
fn window_iconify(module: &Bound<'_, PyModule>) -> PyResult<()> {
935+
let Some(graphics) = get_graphics(module)? else {
936+
return Ok(());
937+
};
938+
graphics.surface.iconify()
939+
}
940+
941+
#[pyfunction]
942+
#[pyo3(pass_module)]
943+
fn window_restore(module: &Bound<'_, PyModule>) -> PyResult<()> {
944+
let Some(graphics) = get_graphics(module)? else {
945+
return Ok(());
946+
};
947+
graphics.surface.restore()
948+
}
949+
950+
#[pyfunction]
951+
#[pyo3(pass_module)]
952+
fn window_maximize(module: &Bound<'_, PyModule>) -> PyResult<()> {
953+
let Some(graphics) = get_graphics(module)? else {
954+
return Ok(());
955+
};
956+
graphics.surface.maximize()
957+
}
958+
959+
#[pyfunction]
960+
#[pyo3(pass_module)]
961+
fn window_center_on(
962+
module: &Bound<'_, PyModule>,
963+
monitor: &crate::monitor::Monitor,
964+
) -> PyResult<()> {
965+
let Some(graphics) = get_graphics(module)? else {
966+
return Ok(());
967+
};
968+
graphics.surface.center_on(monitor)
969+
}
970+
971+
#[pyfunction]
972+
#[pyo3(pass_module)]
973+
fn window_position_on(
974+
module: &Bound<'_, PyModule>,
975+
monitor: &crate::monitor::Monitor,
976+
x: i32,
977+
y: i32,
978+
) -> PyResult<()> {
979+
let Some(graphics) = get_graphics(module)? else {
980+
return Ok(());
981+
};
982+
graphics.surface.position_on(monitor, x, y)
983+
}
984+
847985
#[pyfunction]
848986
#[pyo3(pass_module)]
849987
fn size(module: &Bound<'_, PyModule>, width: u32, height: u32) -> PyResult<()> {

crates/processing_pyo3/src/monitor.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ impl Monitor {
3434
pub fn name(&self) -> PyResult<Option<String>> {
3535
monitor_name(self.entity).map_err(|e| PyRuntimeError::new_err(format!("{e}")))
3636
}
37+
38+
#[getter]
39+
pub fn position(&self) -> PyResult<(i32, i32)> {
40+
let p = monitor_position(self.entity)
41+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
42+
Ok((p.x, p.y))
43+
}
44+
45+
#[getter]
46+
pub fn workarea(&self) -> PyResult<(i32, i32, i32, i32)> {
47+
let r = monitor_workarea(self.entity)
48+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
49+
Ok((r.min.x, r.min.y, r.width(), r.height()))
50+
}
3751
}
3852

3953
pub fn primary() -> PyResult<Option<Monitor>> {

0 commit comments

Comments
 (0)