@@ -0,0 +1,281 @@
1+#![allow(unused)]
2+use anyhow::Error;
3+use termwiz::caps::Capabilities;
4+use termwiz::cell::AttributeChange;
5+use termwiz::color::{AnsiColor, ColorAttribute, RgbColor};
6+use termwiz::input::*;
7+use termwiz::surface::Change;
8+use termwiz::terminal::{buffered::BufferedTerminal, SystemTerminal, Terminal};
9+use termwiz::widgets::*;
10+
11+use crate::Args;
12+
13+use std::{env, path::PathBuf};
14+
15+use helix_core::Buffer;
16+
17+/// This is a widget for our application
18+pub struct MainScreen {}
19+
20+impl MainScreen {
21+ pub fn new() -> Self {
22+ Self {}
23+ }
24+}
25+
26+impl Widget for MainScreen {
27+ fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool {
28+ true // handled it all
29+ }
30+
31+ /// Draw ourselves into the surface provided by RenderArgs
32+ fn render(&mut self, args: &mut RenderArgs) {
33+ // args.surface.add_change(Change::ClearScreen(
34+ // ColorAttribute::TrueColorWithPaletteFallback(
35+ // RgbColor::new(0x31, 0x1B, 0x92),
36+ // AnsiColor::Black.into(),
37+ // ),
38+ // ));
39+ // args.surface
40+ // .add_change(Change::Attribute(AttributeChange::Foreground(
41+ // ColorAttribute::TrueColorWithPaletteFallback(
42+ // RgbColor::new(0xB3, 0x88, 0xFF),
43+ // AnsiColor::Purple.into(),
44+ // ),
45+ // )));
46+ }
47+
48+ fn get_size_constraints(&self) -> layout::Constraints {
49+ let mut constraints = layout::Constraints::default();
50+ constraints.child_orientation = layout::ChildOrientation::Vertical;
51+ constraints
52+ }
53+}
54+
55+pub struct BufferComponent<'a> {
56+ text: String,
57+ buffer: &'a mut Buffer,
58+
59+ first_line: usize,
60+}
61+
62+impl<'a> BufferComponent<'a> {
63+ /// Initialize the widget with the input text
64+ pub fn new(buffer: &'a mut Buffer) -> Self {
65+ Self {
66+ buffer,
67+ text: String::new(),
68+
69+ first_line: 0,
70+ }
71+ }
72+}
73+
74+impl<'a> Widget for BufferComponent<'a> {
75+ fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool {
76+ match event {
77+ WidgetEvent::Input(InputEvent::Key(KeyEvent {
78+ key: KeyCode::Char('k'),
79+ ..
80+ })) => {
81+ self.first_line = self.first_line.saturating_sub(1);
82+ }
83+ WidgetEvent::Input(InputEvent::Key(KeyEvent {
84+ key: KeyCode::Char('j'),
85+ ..
86+ })) => {
87+ self.first_line = self.first_line.saturating_add(1);
88+ }
89+ WidgetEvent::Input(InputEvent::Key(KeyEvent {
90+ key: KeyCode::Enter,
91+ ..
92+ })) => {
93+ self.text.push_str("\r\n");
94+ }
95+ WidgetEvent::Input(InputEvent::Paste(s)) => {
96+ self.text.push_str(&s);
97+ }
98+ _ => {}
99+ }
100+
101+ true // handled it all
102+ }
103+
104+ /// Draw ourselves into the surface provided by RenderArgs
105+ fn render(&mut self, args: &mut RenderArgs) {
106+ args.surface
107+ .add_change(Change::ClearScreen(ColorAttribute::Default));
108+
109+ // args.surface
110+ // .add_change(Change::Attribute(AttributeChange::Foreground(
111+ // ColorAttribute::TrueColorWithPaletteFallback(
112+ // RgbColor::new(0x11, 0x00, 0xFF),
113+ // AnsiColor::Purple.into(),
114+ // ),
115+ // )));
116+ let (_width, height) = args.surface.dimensions();
117+
118+ for line in self.buffer.contents.lines_at(self.first_line).take(height) {
119+ args.surface
120+ .add_change(unsafe { String::from_utf8_unchecked(line.bytes().collect()) });
121+ args.surface.add_change("\r");
122+ }
123+ // args.surface
124+ // .add_change(format!("🤷 surface size is {:?}\r\n", dims));
125+ // args.surface.add_change(self.text.clone());
126+
127+ // Place the cursor at the end of the text.
128+ // A more advanced text editing widget would manage the
129+ // cursor position differently.
130+ *args.cursor = CursorShapeAndPosition {
131+ coords: args.surface.cursor_position().into(),
132+ shape: termwiz::surface::CursorShape::SteadyBar,
133+ ..Default::default()
134+ };
135+ }
136+
137+ fn get_size_constraints(&self) -> layout::Constraints {
138+ let mut c = layout::Constraints::default();
139+ c.set_valign(layout::VerticalAlignment::Top);
140+ c
141+ }
142+}
143+
144+pub struct StatusLine {}
145+
146+impl StatusLine {
147+ pub fn new() -> Self {
148+ StatusLine {}
149+ }
150+}
151+impl Widget for StatusLine {
152+ fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool {
153+ true
154+ }
155+
156+ fn render(&mut self, args: &mut RenderArgs) {
157+ args.surface.add_change(Change::ClearScreen(
158+ ColorAttribute::TrueColorWithPaletteFallback(
159+ RgbColor::new(0xFF, 0xFF, 0xFF),
160+ AnsiColor::Black.into(),
161+ ),
162+ ));
163+ args.surface
164+ .add_change(Change::Attribute(AttributeChange::Foreground(
165+ ColorAttribute::TrueColorWithPaletteFallback(
166+ RgbColor::new(0x00, 0x00, 0x00),
167+ AnsiColor::Black.into(),
168+ ),
169+ )));
170+
171+ args.surface.add_change(" helix");
172+ }
173+
174+ fn get_size_constraints(&self) -> layout::Constraints {
175+ *layout::Constraints::default()
176+ .set_fixed_height(1)
177+ .set_valign(layout::VerticalAlignment::Bottom)
178+ }
179+}
180+
181+pub struct Editor {
182+ terminal: BufferedTerminal<SystemTerminal>,
183+
184+ buffer: Option<Buffer>,
185+}
186+
187+impl Editor {
188+ pub fn new(mut args: Args) -> Result<Self, Error> {
189+ // Create a terminal
190+ let caps = Capabilities::new_from_env()?;
191+ let mut terminal = BufferedTerminal::new(SystemTerminal::new(caps)?)?;
192+
193+ let mut editor = Editor {
194+ terminal,
195+ buffer: None,
196+ };
197+
198+ if let Some(file) = args.files.pop() {
199+ editor.open(file)?;
200+ }
201+
202+ Ok(editor)
203+ }
204+
205+ pub fn open(&mut self, path: PathBuf) -> Result<(), Error> {
206+ let buffer = Buffer::load(path)?;
207+ self.buffer = Some(buffer);
208+ Ok(())
209+ }
210+
211+ pub fn run(&mut self) -> Result<(), Error> {
212+ // Start with an empty string; typing into the app will
213+ // update this string.
214+ let mut typed_text = String::new();
215+
216+ {
217+ let buf = &mut self.terminal;
218+ // Put the terminal in raw mode + alternate screen
219+ buf.terminal().enter_alternate_screen()?;
220+ buf.terminal().set_raw_mode()?;
221+
222+ // Set up the UI
223+ let mut ui = Ui::new();
224+
225+ let root_id = ui.set_root(MainScreen::new());
226+ let buffer_id =
227+ ui.add_child(root_id, BufferComponent::new(self.buffer.as_mut().unwrap()));
228+ // let root_id = ui.set_root(Buffer::new(&mut typed_text));
229+ ui.add_child(root_id, StatusLine::new());
230+ ui.set_focus(buffer_id);
231+
232+ loop {
233+ ui.process_event_queue()?;
234+
235+ // After updating and processing all of the widgets, compose them
236+ // and render them to the screen.
237+ if ui.render_to_screen(buf)? {
238+ // We have more events to process immediately; don't block waiting
239+ // for input below, but jump to the top of the loop to re-run the
240+ // updates.
241+ continue;
242+ }
243+ // Compute an optimized delta to apply to the terminal and display it
244+ buf.flush()?;
245+
246+ // Wait for user input
247+ match buf.terminal().poll_input(None) {
248+ Ok(Some(InputEvent::Resized { rows, cols })) => {
249+ // FIXME: this is working around a bug where we don't realize
250+ // that we should redraw everything on resize in BufferedTerminal.
251+ buf.add_change(Change::ClearScreen(Default::default()));
252+ buf.resize(cols, rows);
253+ }
254+ Ok(Some(input)) => match input {
255+ InputEvent::Key(KeyEvent {
256+ key: KeyCode::Escape,
257+ ..
258+ }) => {
259+ // Quit the app when escape is pressed
260+ break;
261+ }
262+ input @ _ => {
263+ // Feed input into the Ui
264+ ui.queue_event(WidgetEvent::Input(input));
265+ }
266+ },
267+ Ok(None) => {}
268+ Err(e) => {
269+ print!("{:?}\r\n", e);
270+ break;
271+ }
272+ }
273+ }
274+ }
275+
276+ // After we've stopped the full screen raw terminal,
277+ // print out the final edited value of the input text.
278+ println!("The text you entered: {}", typed_text);
279+ Ok(())
280+ }
281+}