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_weight(FontWeight::BOLD)
352 .font_size(75.)
353 .shadow((0., 4., 20., 4., (0, 0, 0, 80)))
354 .child(count.read().to_string());
355
356 let actions = rect()
357 .horizontal()
358 .width(Size::fill())
359 .height(Size::percent(50.))
360 .center()
361 .spacing(8.0)
362 .child(
363 Button::new()
364 .on_press(move |_| {
365 *count.write() += 1;
366 })
367 .child("Increase"),
368 )
369 .child(
370 Button::new()
371 .on_press(move |_| {
372 *count.write() -= 1;
373 })
374 .child("Decrease"),
375 );
376
377 rect().child(counter).child(actions)
378}"#;
379
380const HIGHLIGH_TAGS: [&str; 23] = [
381 "constructor",
382 "attribute",
383 "constant",
384 "constant.builtin",
385 "function.builtin",
386 "function",
387 "function.method",
388 "keyword",
389 "operator",
390 "property",
391 "punctuation",
392 "punctuation.bracket",
393 "punctuation.delimiter",
394 "string",
395 "string.special",
396 "tag",
397 "type",
398 "type.builtin",
399 "variable",
400 "variable.builtin",
401 "variable.parameter",
402 "number",
403 "comment",
404];
405
406#[derive(Clone, Debug)]
407pub enum SyntaxType {
408 Number,
409 String,
410 Keyword,
411 Operator,
412 Variable,
413 Function,
414 Comment,
415 Punctuation,
416 Unknown,
417}
418
419impl SyntaxType {
420 pub fn color(&self) -> Color {
421 match self {
422 SyntaxType::Number => Color::from_hex("#9ECBFF").unwrap(),
423 SyntaxType::String => Color::from_hex("#9ECBFF").unwrap(),
424 SyntaxType::Keyword => Color::from_hex("#F97583").unwrap(),
425 SyntaxType::Operator => Color::from_hex("#F97583").unwrap(),
426 SyntaxType::Variable => Color::WHITE,
427 SyntaxType::Function => Color::from_hex("#B392F0").unwrap(),
428 SyntaxType::Comment => Color::GREEN,
429 SyntaxType::Punctuation => Color::WHITE,
430 SyntaxType::Unknown => Color::WHITE,
431 }
432 }
433}
434
435impl From<&str> for SyntaxType {
436 fn from(s: &str) -> Self {
437 match s {
438 "keyword" => SyntaxType::Keyword,
439 "variable" => SyntaxType::Variable,
440 "operator" => SyntaxType::Operator,
441 "string" => SyntaxType::String,
442 "number" => SyntaxType::Number,
443 "function" => SyntaxType::Function,
444 "constructor" => SyntaxType::Function,
445 "comment" => SyntaxType::Comment,
446 "punctuation.bracket" => SyntaxType::Punctuation,
447 _ => SyntaxType::Unknown,
448 }
449 }
450}
451
452pub type SyntaxBlocks = Vec<Vec<(SyntaxType, String)>>;