1#![cfg_attr(
2 all(not(debug_assertions), target_os = "windows"),
3 windows_subsystem = "windows"
4)]
5
6use freya::{
7 prelude::*,
8 text_edit::Rope,
9};
10use tree_sitter_highlight::{
11 HighlightEvent,
12 Highlighter,
13};
14
15fn main() {
16 launch(
17 LaunchConfig::new().with_window(
18 WindowConfig::new(app)
19 .with_title("like freyaui.dev but in freya")
20 .with_size(1500.0, 900.0),
21 ),
22 )
23}
24
25fn app() -> Element {
26 use_init_theme(|| DARK_THEME);
27
28 rect()
29 .background((24, 24, 27))
30 .color((255, 255, 255))
31 .child(
32 ScrollView::new().child(
33 rect()
34 .cross_align(Alignment::Center)
35 .width(Size::fill())
36 .child(
37 rect()
38 .width(Size::percent(60.))
39 .spacing(40.0)
40 .padding((40.0, 0.0))
41 .child(Navigation)
42 .child(Home),
43 ),
44 ),
45 )
46 .into()
47}
48
49#[derive(PartialEq)]
50struct Home;
51impl Render for Home {
52 fn render(&self) -> impl IntoElement {
53 rect()
54 .cross_align(Alignment::Center)
55 .width(Size::fill())
56 .spacing(40.0)
57 .child(
58 rect()
59 .direction(Direction::Horizontal)
60 .cross_align(Alignment::Center)
61 .spacing(12.0)
62 .child(svg(Bytes::from_static(include_bytes!("./freya_icon.svg"))).width(Size::px(150.)).height(Size::px(150.)))
63 .child(
64 rect()
65 .spacing(16.0)
66 .child(
67 paragraph()
68 .width(Size::px(500.0))
69 .span(Span::new("Build native & cross-platform GUI applications using 🦀 Rust. Powered by 🧬 "))
70 .span(Span::new("Dioxus "))
71 .span(Span::new(" and 🎨 "))
72 .span(Span::new("Skia.")),
73 )
74 .child(
75 rect()
76 .direction(Direction::Horizontal)
77 .spacing(10.0)
78 .child(Link::new("https://book.freyaui.dev/getting_started.html")
79 .child(Button::new()
80 .padding((10., 24.))
81 .background((14, 165, 233))
82 .hover_background((2, 132, 199))
83 .border_fill(Color::TRANSPARENT)
84 .color(Color::BLACK)
85 .child("Get Started")
86 )
87 )
88 .child(Link::new("https://github.com/marc2332/freya")
89 .child(Button::new()
90 .padding((10., 24.))
91 .background((253, 186, 116))
92 .hover_background((251, 146, 60))
93 .border_fill(Color::TRANSPARENT)
94 .color(Color::BLACK)
95 .child("Source Code")
96 )
97 )
98 .child(Link::new("https://github.com/sponsors/marc2332")
99 .child(Button::new()
100 .padding((10., 24.))
101 .background((249, 168, 212))
102 .hover_background((244, 114, 182))
103 .border_fill(Color::TRANSPARENT)
104 .color(Color::BLACK)
105 .child("Sponsor")
106 )
107 )
108 ),
109 ),
110 )
111 .child(
112 rect()
113 .width(Size::fill())
114 .cross_align(Alignment::Center)
115 .child(
116 rect()
117 .background((19, 19, 21))
118 .border(Border::new().alignment(BorderAlignment::Inner).fill((41, 37, 36)).width(1.0))
119 .corner_radius(16.0)
120 .width(Size::px(960.0))
121 .height(Size::px(700.0))
122 .spacing(20.0)
123 .padding((32.0, 24.0))
124 .direction(Direction::Horizontal)
125 .child(
126 rect()
127 .width(Size::func(|c| Some(c.parent / 2. - 20.)))
128 .height(Size::fill())
129 .cross_align(Alignment::Center)
130 .child(Code),
131 )
132 .child(
133 rect()
134 .width(Size::func(|c| Some(c.parent / 2. - 20.)))
135 .height(Size::fill())
136 .padding((20.0, 20.0))
137 .spacing(20.0)
138 .child(rect().height(Size::percent(90.)).child(Counter))
139 .child(
140 rect()
141 .height(Size::fill())
142 .width(Size::fill())
143 .cross_align(Alignment::Center)
144 .child(
145 Link::new("https://github.com/marc2332/freya#want-to-try-it-")
146 .child(Button::new()
147 .padding((10., 24.))
148 .background((109, 78, 233))
149 .hover_background((87, 62, 186))
150 .border_fill(Color::TRANSPARENT)
151 .color(Color::WHITE)
152 .child("Run Locally")
153 )
154 ),
155 ),
156 ),
157 ),
158 )
159 }
160}
161
162#[derive(PartialEq)]
163struct Navigation;
164impl Render for Navigation {
165 fn render(&self) -> impl IntoElement {
166 rect()
167 .direction(Direction::Horizontal)
168 .spacing(24.0)
169 .cross_align(Alignment::Center)
170 .color((214, 211, 209))
171 .child(
172 svg(Bytes::from_static(include_bytes!("./freya_icon.svg")))
173 .width(Size::px(50.))
174 .height(Size::px(50.)),
175 )
176 .child(
177 svg(Bytes::from_static(include_bytes!("./freya_logo.svg")))
178 .width(Size::px(50.))
179 .height(Size::px(50.)),
180 )
181 .child(Link::new("https://freyaui.dev/blog").child("Blog"))
182 .child(Link::new("https://book.freyaui.dev/").child("Book"))
183 .child(Link::new("https://docs.rs/freya/latest/freya/").child("Docs"))
184 .child(Link::new("https://discord.gg/sYejxCdewG").child("Discord"))
185 }
186}
187
188#[derive(PartialEq)]
189struct Counter;
190impl Render for Counter {
191 fn render(&self) -> impl IntoElement {
192 use_init_theme(|| LIGHT_THEME);
193 let mut count = use_state(|| 4);
194
195 rect()
196 .corner_radius(16.)
197 .overflow(Overflow::Clip)
198 .shadow(Shadow::new().blur(10.).color((0, 0, 0, 0.3)))
199 .child(
200 rect()
201 .width(Size::fill())
202 .height(Size::percent(50.))
203 .center()
204 .color((255, 255, 255))
205 .background((15, 163, 242))
206 .font_size(75.)
207 .shadow((0., 4., 20., 4., (0, 0, 0, 80)))
208 .child(count.read().to_string()),
209 )
210 .child(
211 rect()
212 .horizontal()
213 .background((255, 255, 255))
214 .width(Size::fill())
215 .height(Size::percent(50.))
216 .center()
217 .spacing(8.0)
218 .child(
219 Button::new()
220 .on_press(move |_| {
221 *count.write() += 1;
222 })
223 .child("Increase"),
224 )
225 .child(
226 Button::new()
227 .on_press(move |_| {
228 *count.write() -= 1;
229 })
230 .child("Decrease"),
231 ),
232 )
233 }
234}
235
236#[derive(PartialEq)]
237struct Code;
238impl Render for Code {
239 fn render(&self) -> impl IntoElement {
240 let code = use_hook(move || {
241 use tree_sitter_highlight::HighlightConfiguration;
242
243 let mut rust_config = HighlightConfiguration::new(
244 tree_sitter_rust::LANGUAGE.into(),
245 "rust",
246 tree_sitter_rust::HIGHLIGHTS_QUERY,
247 tree_sitter_rust::INJECTIONS_QUERY,
248 tree_sitter_rust::TAGS_QUERY,
249 )
250 .unwrap();
251
252 rust_config.configure(&HIGHLIGH_TAGS);
253
254 let mut highlighter = Highlighter::new();
255
256 let highlights = highlighter
257 .highlight(&rust_config, CODE.as_bytes(), None, |_| None)
258 .unwrap();
259
260 let rope = Rope::from_str(CODE);
261
262 let mut syntax_blocks = SyntaxBlocks::default();
263
264 let mut prepared_block: (SyntaxType, Vec<(usize, String)>) =
265 (SyntaxType::Unknown, Vec::new());
266
267 for event in highlights {
268 match event.unwrap() {
269 HighlightEvent::Source { start, end } => {
270 let data_begining = rope.byte_slice(start..end);
272 let starting_line = rope.char_to_line(start);
273
274 let mut back = String::new();
275 let mut line = starting_line;
276
277 for (i, d) in data_begining.chars().enumerate() {
278 if d != '\n' {
279 back.push(d);
280 }
281
282 if start + i == end - 1 || d == '\n' {
283 prepared_block.1.push((line, back.clone()));
284 line += 1;
285 back.clear();
286 }
287 }
288 }
289 HighlightEvent::HighlightStart(s) => {
290 prepared_block.0 = SyntaxType::from(HIGHLIGH_TAGS[s.0]);
292 }
293 HighlightEvent::HighlightEnd => {
294 for (i, d) in prepared_block.1 {
296 if syntax_blocks.get(i).is_none() {
297 syntax_blocks.push(Vec::new());
298 }
299 let line = syntax_blocks.last_mut().unwrap();
300 line.push((prepared_block.0.clone(), d));
301 }
302 prepared_block = (SyntaxType::Unknown, Vec::new());
304 }
305 }
306 }
307
308 if !prepared_block.1.is_empty() {
310 for (i, d) in prepared_block.1 {
311 if syntax_blocks.get(i).is_none() {
312 syntax_blocks.push(Vec::new());
313 }
314 let line = syntax_blocks.last_mut().unwrap();
315 line.push((SyntaxType::Unknown, d));
316 }
317 }
318
319 syntax_blocks
320 });
321
322 let mut container = rect();
323
324 for (line_n, line) in code.iter().enumerate() {
325 let mut p = paragraph()
326 .key(line_n)
327 .font_size(12.0)
328 .font_family("Jetbrains Mono");
329 for (syntax_type, text) in line.iter() {
332 p = p.span(Span::new(text.clone()).color(syntax_type.color()));
333 }
334
335 container = container.child(p);
336 }
337
338 container
339 }
340}
341
342const CODE: &str = r#"fn app() -> impl IntoElement {
343 let mut count = use_state(|| 4);
344
345 let counter = rect()
346 .width(Size::fill())
347 .height(Size::percent(50.))
348 .center()
349 .color((255, 255, 255))
350 .background((15, 163, 242))
351 .font_size(75.)
352 .shadow((0., 4., 20., 4., (0, 0, 0, 80)))
353 .child(count.read().to_string());
354
355 let actions = rect()
356 .horizontal()
357 .width(Size::fill())
358 .height(Size::percent(50.))
359 .center()
360 .spacing(8.0)
361 .child(
362 Button::new()
363 .on_press(move |_| {
364 *count.write() += 1;
365 })
366 .child("Increase"),
367 )
368 .child(
369 Button::new()
370 .on_press(move |_| {
371 *count.write() -= 1;
372 })
373 .child("Decrease"),
374 );
375
376 rect().child(counter).child(actions)
377}"#;
378
379const HIGHLIGH_TAGS: [&str; 23] = [
380 "constructor",
381 "attribute",
382 "constant",
383 "constant.builtin",
384 "function.builtin",
385 "function",
386 "function.method",
387 "keyword",
388 "operator",
389 "property",
390 "punctuation",
391 "punctuation.bracket",
392 "punctuation.delimiter",
393 "string",
394 "string.special",
395 "tag",
396 "type",
397 "type.builtin",
398 "variable",
399 "variable.builtin",
400 "variable.parameter",
401 "number",
402 "comment",
403];
404
405#[derive(Clone, Debug)]
406pub enum SyntaxType {
407 Number,
408 String,
409 Keyword,
410 Operator,
411 Variable,
412 Function,
413 Comment,
414 Punctuation,
415 Unknown,
416}
417
418impl SyntaxType {
419 pub fn color(&self) -> Color {
420 match self {
421 SyntaxType::Number => Color::from_hex("#9ECBFF").unwrap(),
422 SyntaxType::String => Color::from_hex("#9ECBFF").unwrap(),
423 SyntaxType::Keyword => Color::from_hex("#F97583").unwrap(),
424 SyntaxType::Operator => Color::from_hex("#F97583").unwrap(),
425 SyntaxType::Variable => Color::WHITE,
426 SyntaxType::Function => Color::from_hex("#B392F0").unwrap(),
427 SyntaxType::Comment => Color::GREEN,
428 SyntaxType::Punctuation => Color::WHITE,
429 SyntaxType::Unknown => Color::WHITE,
430 }
431 }
432}
433
434impl From<&str> for SyntaxType {
435 fn from(s: &str) -> Self {
436 match s {
437 "keyword" => SyntaxType::Keyword,
438 "variable" => SyntaxType::Variable,
439 "operator" => SyntaxType::Operator,
440 "string" => SyntaxType::String,
441 "number" => SyntaxType::Number,
442 "function" => SyntaxType::Function,
443 "constructor" => SyntaxType::Function,
444 "comment" => SyntaxType::Comment,
445 "punctuation.bracket" => SyntaxType::Punctuation,
446 _ => SyntaxType::Unknown,
447 }
448 }
449}
450
451pub type SyntaxBlocks = Vec<Vec<(SyntaxType, String)>>;