use qtrs::dialog;
use qtrs::prelude::*;
use qtrs::splitter::HORIZONTAL;
use std::cell::RefCell;
use std::rc::Rc;
const DEFAULT_MARKDOWN: &str = "# Markdown Editor
Welcome to the **Markdown Editor**!
Edit your markdown on the left and see a live preview on the right.
## Features
- **Bold** and *italic* text
- `inline code` and code blocks
- Ordered and unordered lists
- [Links](https://example.com) and images
### Code Block
```rust
fn main() {
println!(\"Hello, Markdown!\");
}
```
### Blockquote
> Markdown is a lightweight markup language
> with plain-text formatting syntax.
---
Happy editing!
";
fn markdown_to_html(md: &str) -> String {
let parser = pulldown_cmark::Parser::new(md);
let mut html = String::new();
pulldown_cmark::html::push_html(&mut html, parser);
html
}
fn main() {
let app = Application::new();
let window = Rc::new(
MainWindow::new()
.window_title("Markdown Editor")
.size(900, 600)
.build(),
);
let initial_html = markdown_to_html(DEFAULT_MARKDOWN);
let preview = Rc::new(RefCell::new(
TextBrowser::new()
.html(&initial_html)
.build(),
));
let text_edit = Rc::new(RefCell::new(
PlainTextEdit::new()
.text(DEFAULT_MARKDOWN)
.build(),
));
text_edit.borrow_mut().connect_text_changed({
let text_edit = Rc::clone(&text_edit);
let preview = Rc::clone(&preview);
move || {
let md = text_edit.borrow().plain_text();
let html = markdown_to_html(&md);
preview.borrow().set_html(&html);
}
});
let file_path = Rc::new(RefCell::new(None::<String>));
let file_menu = Menu::new("File")
.action("New", {
let text_edit = Rc::clone(&text_edit);
let file_path = Rc::clone(&file_path);
move || {
text_edit.borrow().set_plain_text("");
*file_path.borrow_mut() = None;
}
})
.action("Open...", {
let text_edit = Rc::clone(&text_edit);
let file_path = Rc::clone(&file_path);
move || {
if let Some(path) = FileDialog::open_file(
None,
"Open File",
"",
"Markdown Files (*.md);;Text Files (*.txt);;All Files (*.*)",
) {
if let Ok(content) = std::fs::read_to_string(&path) {
text_edit.borrow().set_plain_text(&content);
*file_path.borrow_mut() = Some(path);
}
}
}
})
.action("Save", {
let text_edit = Rc::clone(&text_edit);
let file_path = Rc::clone(&file_path);
move || {
let path = file_path.borrow().clone();
match path {
Some(p) => {
let content = text_edit.borrow().plain_text();
let _ = std::fs::write(&p, content);
}
None => {
if let Some(new_path) = FileDialog::save_file(
None,
"Save File",
"",
"Markdown Files (*.md);;Text Files (*.txt);;All Files (*.*)",
) {
let content = text_edit.borrow().plain_text();
let _ = std::fs::write(&new_path, content);
*file_path.borrow_mut() = Some(new_path);
}
}
}
}
})
.action("Save As...", {
let text_edit = Rc::clone(&text_edit);
let file_path = Rc::clone(&file_path);
move || {
if let Some(path) = FileDialog::save_file(
None,
"Save File As",
"",
"Markdown Files (*.md);;Text Files (*.txt);;All Files (*.*)",
) {
let content = text_edit.borrow().plain_text();
let _ = std::fs::write(&path, content);
*file_path.borrow_mut() = Some(path);
}
}
})
.action("About", || dialog::information(None, "About", "TextEditor v0.1.0"))
.action("Exit", || std::process::exit(0))
.parent(&*window)
.build();
let menu_bar = MenuBar::new()
.add_menu(file_menu)
.parent(&*window)
.build();
window.set_menu_bar(&menu_bar);
text_edit.borrow_mut().set_has_parent();
preview.borrow_mut().set_has_parent();
let left_scroll = ScrollArea::new()
.set_widget_resizable(true)
.build();
left_scroll.set_widget(&*text_edit.borrow());
let right_scroll = ScrollArea::new()
.set_widget_resizable(true)
.build();
right_scroll.set_widget(&*preview.borrow());
let mut splitter = Splitter::new(HORIZONTAL).build();
splitter.add(left_scroll);
splitter.add(right_scroll);
window.set_central_widget(&splitter);
window.show();
app.exec();
}