1use std::{fs, path::PathBuf};
2
3use anyhow::Result;
4use gpui::{
5 App, AssetSource, Bounds, BoxShadow, ClickEvent, Context, SharedString, Task, Window,
6 WindowBounds, WindowOptions, div, hsla, img, point, prelude::*, px, rgb, size, svg,
7};
8use gpui_platform::application;
9
10struct Assets {
11 base: PathBuf,
12}
13
14impl AssetSource for Assets {
15 fn load(&self, path: &str) -> Result<Option<std::borrow::Cow<'static, [u8]>>> {
16 fs::read(self.base.join(path))
17 .map(|data| Some(std::borrow::Cow::Owned(data)))
18 .map_err(|e| e.into())
19 }
20
21 fn list(&self, path: &str) -> Result<Vec<SharedString>> {
22 fs::read_dir(self.base.join(path))
23 .map(|entries| {
24 entries
25 .filter_map(|entry| {
26 entry
27 .ok()
28 .and_then(|entry| entry.file_name().into_string().ok())
29 .map(SharedString::from)
30 })
31 .collect()
32 })
33 .map_err(|e| e.into())
34 }
35}
36
37struct HelloWorld {
38 _task: Option<Task<()>>,
39 opacity: f32,
40 animating: bool,
41}
42
43impl HelloWorld {
44 fn new(_window: &mut Window, _: &mut Context<Self>) -> Self {
45 Self {
46 _task: None,
47 opacity: 0.5,
48 animating: false,
49 }
50 }
51
52 fn start_animation(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context<Self>) {
53 self.opacity = 0.0;
54 self.animating = true;
55 cx.notify();
56 }
57}
58
59impl Render for HelloWorld {
60 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
61 if self.animating {
62 self.opacity += 0.005;
63 if self.opacity >= 1.0 {
64 self.animating = false;
65 self.opacity = 1.0;
66 } else {
67 window.request_animation_frame();
68 }
69 }
70
71 div()
72 .flex()
73 .flex_row()
74 .size_full()
75 .bg(rgb(0xe0e0e0))
76 .text_xl()
77 .child(
78 div()
79 .flex()
80 .size_full()
81 .justify_center()
82 .items_center()
83 .border_1()
84 .text_color(gpui::blue())
85 .child(div().child("This is background text.")),
86 )
87 .child(
88 div()
89 .id("panel")
90 .on_click(cx.listener(Self::start_animation))
91 .absolute()
92 .top_8()
93 .left_8()
94 .right_8()
95 .bottom_8()
96 .opacity(self.opacity)
97 .flex()
98 .justify_center()
99 .items_center()
100 .bg(gpui::white())
101 .border_3()
102 .border_color(gpui::red())
103 .text_color(gpui::yellow())
104 .child(
105 div()
106 .flex()
107 .flex_col()
108 .gap_2()
109 .justify_center()
110 .items_center()
111 .size(px(300.))
112 .bg(gpui::blue())
113 .border_3()
114 .border_color(gpui::black())
115 .shadow(vec![BoxShadow {
116 color: hsla(0.0, 0.0, 0.0, 0.5),
117 blur_radius: px(1.0),
118 spread_radius: px(5.0),
119 offset: point(px(10.0), px(10.0)),
120 }])
121 .child(img("image/app-icon.png").size_8())
122 .child("Opacity Panel (Click to test)")
123 .child(
124 div()
125 .id("deep-level-text")
126 .flex()
127 .justify_center()
128 .items_center()
129 .p_4()
130 .bg(gpui::black())
131 .text_color(gpui::white())
132 .text_decoration_2()
133 .text_decoration_wavy()
134 .text_decoration_color(gpui::red())
135 .child(format!("opacity: {:.1}", self.opacity)),
136 )
137 .child(
138 svg()
139 .path("image/arrow_circle.svg")
140 .text_color(gpui::black())
141 .text_2xl()
142 .size_8(),
143 )
144 .child(
145 div()
146 .flex()
147 .children(["🎊", "✈️", "🎉", "🎈", "🎁", "🎂"].map(|emoji| {
148 div()
149 .child(emoji.to_string())
150 .hover(|style| style.opacity(0.5))
151 })),
152 )
153 .child(img("image/black-cat-typing.gif").size_12()),
154 ),
155 )
156 }
157}
158
159fn main() {
160 application()
161 .with_assets(Assets {
162 base: PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"),
163 })
164 .run(|cx: &mut App| {
165 let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
166 cx.open_window(
167 WindowOptions {
168 window_bounds: Some(WindowBounds::Windowed(bounds)),
169 ..Default::default()
170 },
171 |window, cx| cx.new(|cx| HelloWorld::new(window, cx)),
172 )
173 .unwrap();
174 cx.activate(true);
175 });
176}