cursive_hexview/
lib.rs

1#![deny(
2    missing_copy_implementations,
3    trivial_casts,
4    trivial_numeric_casts,
5    unsafe_code,
6    unused_import_braces,
7    unused_qualifications,
8    missing_docs,
9    rustdoc::missing_crate_level_docs,
10    rustdoc::invalid_html_tags
11)]
12
13//! A simple `HexView` for [cursive](https://crates.io/crates/cursive).
14//!
15//! It is meant to display a data of u8 and format them like e.g. hexdump does.
16//! You can interact with the view with your keyboard.
17//! Currently the following keys are implemented:
18//!
19//! | Key                                 | Action                                                                                                                                                                                                                                                 |
20//! |-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
21//! | <kbd>&leftarrow;</kbd>              | Move the cursor to the next, left nibble. If already on the left edge of the view, the event will be "ignored", which means the outer view will handle the event (e.g. focus the view next to this one)                                                |
22//! | <kbd>&rightarrow;</kbd>             | Move the cursor to the next, right nibble. If already on the right edge of the view, the event will be "ignored", which means the outer view will handle the event (e.g. focus the view next to this one)                                              |
23//! | <kbd>&uparrow;</kbd>                | Move the cursor to the previous line. If already on the top edge of the view, the event will be "ignored", which means the outer view will handle the event (e.g. focus the view next to this one)                                                     |
24//! | <kbd>&downarrow;</kbd>              | Move the cursor to the next line. If already on the bottom edge of the,view, the event will be "ignored", which means the outer view will handle the event (e.g. focus the view next to this one)                                                      |
25//! | <kbd>Home</kbd>                     | Move the cursor to the beginning of the current line.                                                                                                                                                                                                  |
26//! | <kbd>End</kbd>                      | Move the cursor to the end of the current line.                                                                                                                                                                                                        |
27//! | <kbd>Shift</kbd> + <kbd>Home</kbd>  | Move the cursor to position (0 ,0) which means to the beginning of the view.                                                                                                                                                                           |
28//! | <kbd>Shift</kbd> + <kbd>End</kbd>   | Move the cursor to the last nibble in the view.                                                                                                                                                                                                        |
29//! | <kbd>+</kbd>                        | Increase the amount of data by one byte. It will be filled up with `0`.                                                                                                                                                                                |
30//! | <kbd>-</kbd>                        | Decrease the amount of data by one. Any data that will leave the viewable area, will be permanantly lost.                                                                                                                                              |
31//! | <kbd>0-9</kbd>, <kbd>a-f</kbd>      | Set the nibble under the cursor to the corresponding hex value. Note, that this is only available in the editable state, see [`DisplayState`](enum.DisplayState.html#Editable) and [`set_display_state`](struct.HexView.html#method.set_display_state) |
32
33extern crate cursive;
34extern crate itertools;
35
36use std::borrow::Borrow;
37use std::cmp::min;
38
39use cursive::direction::Direction;
40use cursive::event::{Event, EventResult, Key, MouseEvent};
41use cursive::theme::{ColorStyle, Effect};
42use cursive::vec::Vec2;
43use cursive::view::{CannotFocus, View};
44use cursive::{Printer, With};
45use itertools::Itertools;
46use std::fmt::Write;
47
48/// Controls the possible interactions with a [`HexView`].
49///
50/// This enum is used for the [`set_display_state`] method
51/// and controls the interaction inside of the cursive environment.
52///
53/// [HexView]: struct.HexView.html
54/// [`set_display_state`]: struct.HexView.html#method.set_display_state
55#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub enum DisplayState {
57    /// The view can neither be focused, nor edited
58    Disabled,
59    /// The view can be focused but not edited
60    Enabled,
61    /// The view can be focused and edited
62    Editable,
63}
64
65/// Controls the visual output of the `HexView` struct.
66///
67/// There are various options which can be altered. For a detailed description of them, please see the fields below.
68/// For the changes to apply, you need to use [`set_config`].
69///
70/// [`set_config`]: struct.HexView.html#method.set_config
71#[derive(Debug, Clone, Copy)]
72pub struct HexViewConfig {
73    /// Controls the number of bytes per line.
74    ///
75    /// It needs to be greater than 0 and equal or higher than the `bytes_per_group` value.
76    /// Default is `16`
77    pub bytes_per_line: usize,
78    /// Controls the number of bytes per group.
79    ///
80    /// It needs to be greater than 0 equal or lower than `bytes_per_line`.
81    /// Default is `1`
82    pub bytes_per_group: usize,
83    /// Controls the separator between the hex groups in the data output.
84    ///
85    /// Default is ` ` (0x20)
86    pub byte_group_separator: &'static str,
87    /// Controls the separator between the address label and the hex output of the data.
88    ///
89    /// Default is ` : `
90    pub addr_hex_separator: &'static str,
91    /// Controls the separator between the hex output and the ASCII representation of the data.
92    ///
93    /// Default is ` | `
94    pub hex_ascii_separator: &'static str,
95    /// Controls if the ASCII representation of the data should be shown.
96    ///
97    /// Default is `true`
98    pub show_ascii: bool,
99}
100
101impl Default for HexViewConfig {
102    fn default() -> Self {
103        Self {
104            bytes_per_line: 16,
105            bytes_per_group: 1,
106            byte_group_separator: " ",
107            addr_hex_separator: ": ",
108            hex_ascii_separator: " | ",
109            show_ascii: true,
110        }
111    }
112}
113
114/// Hexadecimal viewer.
115///
116/// This is a classic hexview which can be used to view and manipulate data which resides inside
117/// this struct. There are severeal states in which the view can be operatered, see [`DisplayState`].
118/// You should consider the corresponding method docs for each state.
119///
120/// [`DisplayState`]: enum.DisplayState.html
121///
122/// # Examples
123///
124/// ```
125/// extern crate cursive;
126/// extern crate cursive_hexview;
127///
128/// use cursive_hexview::{DisplayState,HexView};
129///
130/// fn main() {
131///     let view = HexView::new().display_state(DisplayState::Editable);
132///     let mut cur = cursive::dummy();
133///
134///     cur.add_layer(cursive::views::Dialog::around(view).title("HexView"));
135///
136///     // cur.run();
137/// }
138/// ```
139pub struct HexView {
140    cursor: Vec2,
141    data: Vec<u8>,
142    state: DisplayState,
143    config: HexViewConfig,
144}
145
146impl Default for HexView {
147    /// Creates a new, default `HexView` with an empty databuffer and disabled state.
148    fn default() -> Self {
149        Self::new()
150    }
151}
152
153impl HexView {
154    /// Creates a new, default `HexView` with an empty databuffer and disabled state.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// # use cursive_hexview::HexView;
160    /// let view = HexView::new();
161    /// ```
162    #[must_use]
163    pub fn new() -> Self {
164        Self::new_from_iter(Vec::<u8>::new())
165    }
166
167    /// Crates a new `HexView` with the given data and disabled state.
168    ///
169    /// # Examples
170    ///
171    /// With data from a `Vec`:
172    ///
173    /// ```
174    /// # use cursive_hexview::HexView;
175    /// let view = HexView::new_from_iter(vec![3, 6, 1, 8, 250]);
176    /// ```
177    ///
178    /// With data from a byte string literal:
179    ///
180    /// ```
181    /// # use cursive_hexview::HexView;
182    /// let view = HexView::new_from_iter(b"Hello, World!");
183    /// ```
184    ///
185    /// Or with a slice
186    ///
187    /// ```
188    /// # use cursive_hexview::HexView;
189    /// let view = HexView::new_from_iter(&[5, 6, 2, 89]);
190    /// ```
191    pub fn new_from_iter<B: Borrow<u8>, I: IntoIterator<Item = B>>(data: I) -> Self {
192        Self {
193            cursor: Vec2::zero(),
194            data: data.into_iter().map(|u| *u.borrow()).collect(),
195            state: DisplayState::Disabled,
196            config: HexViewConfig::default(),
197        }
198    }
199
200    /// This function allows the customization of the `HexView` output.
201    ///
202    /// For options and explanation of every possible option, see the `HexViewConfig` struct.
203    ///
204    /// #Examples
205    ///
206    /// ```
207    /// # use cursive_hexview::{HexView,HexViewConfig};
208    /// let mut view = HexView::new();
209    /// view.set_config(HexViewConfig {
210    ///     bytes_per_line: 8,
211    ///     ..Default::default()
212    /// });
213    /// ```
214    pub fn set_config(&mut self, config: HexViewConfig) {
215        self.config = config;
216    }
217
218    /// [`set_config`](#method.set_config)
219    #[must_use]
220    pub fn config(self, config: HexViewConfig) -> Self {
221        self.with(|s| s.set_config(config))
222    }
223
224    /// Returns a reference to the internal data.
225    ///
226    /// # Examples
227    ///
228    /// ```
229    /// # use cursive_hexview::HexView;
230    /// let data = vec![3, 4, 9, 1];
231    /// let view = HexView::new_from_iter(&data);
232    /// assert_eq!(view.data(), &data);
233    /// ```
234    #[must_use]
235    pub fn data(&self) -> &[u8] {
236        self.data.as_slice()
237    }
238
239    /// Sets the data during the lifetime of this instance.
240    ///
241    /// For insance to update the data due to an external event.
242    ///
243    /// ```
244    /// # use cursive_hexview::HexView;
245    /// let mut view = HexView::new();
246    /// view.set_data(b"Hello, World!".to_owned().iter());
247    /// ```
248    pub fn set_data<B: Borrow<u8>, I: IntoIterator<Item = B>>(&mut self, data: I) {
249        self.data = data.into_iter().map(|u| *u.borrow()).collect();
250    }
251
252    /// [`set_display_state`](#method.set_display_state)
253    #[must_use]
254    pub fn display_state(self, state: DisplayState) -> Self {
255        self.with(|s| s.set_display_state(state))
256    }
257
258    /// Sets the state of the view to one of the variants from `DisplayState`.
259    ///
260    /// This will alter the behavior of the view accrodingly to the set state.
261    ///
262    /// If the state is set to `Disabled` this view can neither be focused nor edited. If the state
263    /// is set to `Enabled` it can be focused and the cursor can be moved around, but no data can
264    /// be altered. If set to `Editable` this view behaves like `Enabled` but the data *can* be altered.
265    ///
266    /// # Note
267    ///
268    /// This has nothing to do with rusts type system, which means even when this instance is set to
269    /// `Disabled` you still can alter the data through [`set_data`](#method.set_data) but you cannot
270    /// alter it with the keyboard commands (<kbd>+</kbd>, <kbd>-</kbd>, <kbd>#hexvalue</kbd>).
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// # use cursive_hexview::{DisplayState,HexView};
276    /// let view = HexView::new().display_state(DisplayState::Editable);
277    /// ```
278    pub fn set_display_state(&mut self, state: DisplayState) {
279        self.state = state;
280    }
281
282    /// Returns the length of the data.
283    ///
284    /// # Examples
285    ///
286    /// ```
287    /// # use cursive_hexview::HexView;
288    /// let view = HexView::new_from_iter(vec![0, 1, 2, 3]);
289    /// assert_eq!(4, view.len());
290    /// ```
291    #[must_use]
292    pub fn len(&self) -> usize {
293        self.data.len()
294    }
295
296    /// Checks whether the data is empty.
297    ///
298    /// # Examples
299    ///
300    /// ```
301    /// # use cursive_hexview::HexView;
302    /// let view = HexView::new();
303    /// assert!(view.is_empty());
304    /// ```
305    ///
306    /// ```
307    /// # use cursive_hexview::HexView;
308    /// let view = HexView::new_from_iter(b"ABC");
309    /// assert!(!view.is_empty());
310    /// ```
311    #[must_use]
312    pub fn is_empty(&self) -> bool {
313        self.data.is_empty()
314    }
315
316    /// Sets the length of the data which this view displays.
317    ///
318    /// If the new length is greater than the current one, 0's will be appended to the data.
319    /// If the new length is less than the current one, the data will be truncated and is lost.
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// # use cursive_hexview::HexView;
325    /// let mut view = HexView::new();
326    /// view.set_len(3);
327    ///
328    /// assert_eq!(view.len(), 3);
329    /// assert_eq!(view.data(), &vec![0u8, 0u8, 0u8]);
330    /// ```
331    pub fn set_len(&mut self, length: usize) {
332        if self.data.len() != length {
333            let oldlen = self.data.len();
334            self.data.resize(length, 0);
335            if oldlen > length {
336                self.cursor.y = min(self.cursor.y, self.get_widget_height());
337                self.cursor.x = min(self.cursor.x, (self.data.len() * 2).saturating_sub(1));
338            }
339        }
340    }
341}
342
343enum Field {
344    Addr,
345    AddrSep,
346    Hex,
347    AsciiSep,
348    Ascii,
349}
350
351/// calcs the position in a line with spacing
352fn get_cursor_offset(vec: Vec2, config: &HexViewConfig) -> Vec2 {
353    (
354        ((vec.x as f32 / (2 * config.bytes_per_group) as f32).floor() as usize) * config.byte_group_separator.len(),
355        0,
356    )
357        .into()
358}
359
360/// returns the number of elements in `row` for a given `datalen` with `elements_per_line`
361fn get_elements_in_row(datalen: usize, row: usize, elements_per_line: usize) -> usize {
362    min(datalen.saturating_sub(elements_per_line * row), elements_per_line)
363}
364
365/// returns the maximal cursor position in a `row` for a `datalen` and `elements_per_line`
366fn get_max_x_in_row(datalen: usize, row: usize, elements_per_line: usize) -> usize {
367    (get_elements_in_row(datalen, row, elements_per_line) * 2).saturating_sub(1)
368}
369
370/// converts the character either to itself if it `is_ascii_graphic`
371fn make_printable<T: Borrow<u8>>(c: T) -> char {
372    let c = *c.borrow();
373    if c.is_ascii_graphic() {
374        c as char
375    } else {
376        '.'
377    }
378}
379
380// implements helper functions for this struct
381impl HexView {
382    /// Counts how many digits we need to align the addresses evenly.
383    ///
384    /// E.g. we need 2 digits for 20 elements (0x14), but only 1 for 10 elements (0xA)
385    fn get_addr_digit_length(&self) -> usize {
386        match self.data.len() {
387            0..=1 => 1,
388            e => (e as f64).log(16.0).ceil() as usize,
389        }
390    }
391
392    /// Counts how many rows we need to display the complete data
393    fn get_widget_height(&self) -> usize {
394        match self.data.len() {
395            0 => 1,
396            e => (e as f64 / self.config.bytes_per_line as f64).ceil() as usize,
397        }
398    }
399
400    /// calcs the offset to the current position to match the spacing we insert to group the hex chars.
401    ///
402    /// e.g. cursor (5, 0) will result in (6, 0) because of the 1 space spacing after the fourth char
403    /// and cursor (9, 0) will result in (11, 0) because of the 1+1 spacing after the fourth and eighth char
404    fn get_cursor_offset(&self) -> Vec2 {
405        self.cursor + get_cursor_offset(self.cursor, &self.config)
406    }
407
408    /// gets the amount of nibbles in the current row
409    fn get_elements_in_current_row(&self) -> usize {
410        get_elements_in_row(self.data.len(), self.cursor.y, self.config.bytes_per_line)
411    }
412
413    /// gets the max cursor-x position in the current row
414    fn get_max_x_in_current_row(&self) -> usize {
415        get_max_x_in_row(self.data.len(), self.cursor.y, self.config.bytes_per_line)
416    }
417
418    /// advances the x position by one
419    ///
420    /// Returns either an `EventResult::Ignored` if the end of
421    /// the line is reached or `EventResult::Consumed(None)` if it was successful.
422    fn cursor_x_advance(&mut self) -> EventResult {
423        let max_pos = self.get_max_x_in_current_row();
424        if self.cursor.x == max_pos {
425            return EventResult::Ignored;
426        }
427
428        self.cursor.x = min(self.cursor.x + 1, max_pos);
429        EventResult::Consumed(None)
430    }
431
432    /// Gets the element under the cursor
433    ///
434    /// (which points to a nibble, but we are interested in the
435    /// whole u8)
436    ///
437    /// Returns none if the cursor is out of range.
438    fn get_element_under_cursor(&self) -> Option<u8> {
439        let elem = self.cursor.y * self.config.bytes_per_line + self.cursor.x / 2;
440        self.data.get(elem).copied()
441    }
442
443    /// Converts the visual position to a non spaced one.
444    ///
445    /// This function is used to convert the
446    /// point where the mouse clicked to the real cursor position without padding
447    fn convert_visual_to_real_cursor(&self, pos: Vec2) -> Vec2 {
448        let mut res = pos;
449        let hex_offset = self.get_field_length(Field::Addr) + self.get_field_length(Field::AddrSep);
450
451        res.y = min(self.get_widget_height() - 1, pos.y);
452        res.x = res.x.saturating_sub(hex_offset);
453        res.x = res.x.saturating_sub(get_cursor_offset(res, &self.config).x);
454        res.x = min(
455            get_max_x_in_row(self.data.len(), res.y, self.config.bytes_per_line),
456            res.x,
457        );
458
459        res
460    }
461
462    /// returns the displayed characters per field
463    #[allow(unknown_lints, clippy::needless_pass_by_value)]
464    fn get_field_length(&self, field: Field) -> usize {
465        match field {
466            Field::Addr => self.get_addr_digit_length(),
467            Field::AddrSep => self.config.addr_hex_separator.len(),
468            Field::Hex => {
469                (((2 * self.config.bytes_per_group) + self.config.byte_group_separator.len())
470                    * (self.config.bytes_per_line / self.config.bytes_per_group))
471                    - self.config.byte_group_separator.len()
472            }
473            Field::AsciiSep => self.config.hex_ascii_separator.len(),
474            Field::Ascii => self.config.bytes_per_line * 2,
475        }
476    }
477}
478
479// implements draw-helper functions
480// it will look as follows
481// addr: hexehex hexhex hexhex ... | asciiiiiii
482// the addr field will be padded, so that all addresses are equal in length
483// the hex field will be grouped by 4 character (nibble) and seperated by 1 space
484// the seperator is a special pipe, which is longer and connects with the lower and bottom "pipe" (BOX DRAWINGS LIGHT VERTICAL \u{2502})
485// the ascii part is just the ascii char of the coressponding hex value if it is [graphical](https://doc.rust-lang.org/std/primitive.u8.html#method.is_ascii_graphic), if not it will be displayed as a dot (.)
486impl HexView {
487    /// draws the addr field into the printer
488    fn draw_addr(&self, printer: &Printer) {
489        let digits_len = self.get_addr_digit_length();
490        for lines in 0..self.get_widget_height() {
491            printer.print(
492                (0, lines),
493                &format!("{:0len$X}", lines * self.config.bytes_per_line, len = digits_len),
494            );
495        }
496    }
497
498    fn draw_addr_hex_sep(&self, printer: &Printer) {
499        printer.print_vline((0, 0), self.get_widget_height(), self.config.addr_hex_separator);
500    }
501
502    /// draws the hex fields between the addr and ascii representation
503    fn draw_hex(&self, printer: &Printer) {
504        for (i, c) in self.data.chunks(self.config.bytes_per_line).enumerate() {
505            let hex = c
506                .chunks(self.config.bytes_per_group)
507                .map(|c| {
508                    let mut s = String::new();
509                    for &b in c {
510                        write!(&mut s, "{:02X}", b).expect("Unable to write hex values");
511                    }
512                    s
513                })
514                .format(self.config.byte_group_separator);
515            printer.print((0, i), &format!("{}", hex));
516        }
517    }
518
519    /// draws the ascii seperator between the hex and ascii representation
520    fn draw_ascii_sep(&self, printer: &Printer) {
521        printer.print_vline((0, 0), self.get_widget_height(), self.config.hex_ascii_separator);
522    }
523
524    /// draws the ascii chars
525    fn draw_ascii(&self, printer: &Printer) {
526        for (i, c) in self.data.chunks(self.config.bytes_per_line).enumerate() {
527            let ascii: String = c.iter().map(make_printable).collect();
528            printer.print((0, i), &ascii);
529        }
530    }
531
532    /// this highlights the complete hex byte under the cursor
533    #[allow(clippy::similar_names)]
534    fn highlight_current_hex(&self, printer: &Printer) {
535        if let Some(elem) = self.get_element_under_cursor() {
536            let high = self.cursor.x % 2 == 0;
537            let hpos = self.get_cursor_offset();
538            let dpos = hpos.map_x(|x| if high { x + 1 } else { x - 1 });
539
540            let fem = format!("{:02X}", elem);
541            let s = fem.split_at(1);
542            let ext = |hl| if hl { s.0 } else { s.1 };
543
544            printer.with_color(ColorStyle::highlight(), |p| p.print(hpos, ext(high)));
545            printer.with_color(ColorStyle::secondary(), |p| {
546                p.with_effect(Effect::Reverse, |p| p.print(dpos, ext(!high)));
547            });
548        }
549    }
550
551    /// this highlights the corresponding ascii value of the hex which is under the cursor
552    fn highlight_current_ascii(&self, printer: &Printer) {
553        if let Some(elem) = self.get_element_under_cursor() {
554            let pos = self.cursor.map_x(|x| x / 2);
555            let ascii = make_printable(&elem);
556            printer.with_color(ColorStyle::highlight(), |p| p.print(pos, &ascii.to_string()));
557        }
558    }
559}
560
561impl View for HexView {
562    fn on_event(&mut self, event: Event) -> EventResult {
563        if self.state == DisplayState::Disabled {
564            return EventResult::Ignored;
565        }
566
567        match event {
568            //view keys
569            Event::Key(k) => match k {
570                Key::Left => {
571                    if self.cursor.x == 0 {
572                        return EventResult::Ignored;
573                    }
574
575                    self.cursor.x = self.cursor.x.saturating_sub(1);
576                }
577                Key::Right => {
578                    return self.cursor_x_advance();
579                }
580                Key::Up => {
581                    if self.cursor.y == 0 {
582                        return EventResult::Ignored;
583                    }
584
585                    self.cursor.y = self.cursor.y.saturating_sub(1);
586                }
587                Key::Down => {
588                    if self.cursor.y == self.get_widget_height().saturating_sub(1) {
589                        return EventResult::Ignored;
590                    }
591
592                    let max_pos = min(self.data.len(), self.cursor.y / 2 + 16).saturating_sub(1);
593                    self.cursor.y = min(self.cursor.y + 1, max_pos);
594                    self.cursor.x = min(self.cursor.x, self.get_elements_in_current_row().saturating_sub(1) * 2);
595                }
596                Key::Home => self.cursor.x = 0,
597                Key::End => self.cursor.x = self.get_max_x_in_current_row(),
598                _ => {
599                    return EventResult::Ignored;
600                }
601            },
602            Event::Shift(Key::Home) => self.cursor = (0, 0).into(),
603            Event::Shift(Key::End) => {
604                self.cursor = (
605                    get_max_x_in_row(self.data.len(), self.get_widget_height() - 1, 16),
606                    self.get_widget_height() - 1,
607                )
608                    .into();
609            }
610
611            //edit keys
612            Event::Char(c) => {
613                if self.state != DisplayState::Editable {
614                    return EventResult::Ignored;
615                }
616
617                match c {
618                    '+' => {
619                        let datalen = self.data.len();
620                        self.set_len(datalen + 1);
621                    }
622                    '-' => {
623                        let datalen = self.data.len();
624                        self.set_len(datalen.saturating_sub(1));
625                    }
626                    _ => {
627                        if let Some(val) = c.to_digit(16) {
628                            if let Some(dat) = self.get_element_under_cursor() {
629                                let realpos = self.cursor;
630                                let elem = realpos.y * self.config.bytes_per_line + realpos.x / 2;
631                                let high = self.cursor.x % 2 == 0;
632                                let mask = 0xF << if high { 4 } else { 0 };
633
634                                self.data[elem] = (dat & !mask) | ((val as u8) << if high { 4 } else { 0 });
635                                self.cursor_x_advance();
636                            }
637                        } else {
638                            return EventResult::Ignored;
639                        }
640                    }
641                }
642            }
643            Event::Mouse {
644                offset,
645                position,
646                event: MouseEvent::Press(_),
647            } => {
648                if let Some(position) = position.checked_sub(offset) {
649                    self.cursor = self.convert_visual_to_real_cursor(position);
650                } else {
651                    return EventResult::Ignored;
652                }
653            }
654            _ => {
655                return EventResult::Ignored;
656            }
657        };
658
659        EventResult::Consumed(None)
660    }
661
662    fn required_size(&mut self, _: Vec2) -> Vec2 {
663        let length = self.get_field_length(Field::Addr)
664            + self.get_field_length(Field::AddrSep)
665            + self.get_field_length(Field::Hex)
666            + self.get_field_length(Field::AsciiSep)
667            + self.get_field_length(Field::Ascii);
668
669        (length, self.get_widget_height()).into()
670    }
671
672    fn draw(&self, printer: &Printer) {
673        let height = self.get_widget_height();
674        //they are a tuple of (offset, len)
675        let addr = (0usize, self.get_field_length(Field::Addr));
676        let addr_sep = (addr.0 + addr.1, self.get_field_length(Field::AddrSep));
677        let hex = (addr_sep.0 + addr_sep.1, self.get_field_length(Field::Hex));
678        let ascii_sep = (hex.0 + hex.1, self.get_field_length(Field::AsciiSep));
679        let ascii = (ascii_sep.0 + ascii_sep.1, self.get_field_length(Field::Ascii));
680
681        self.draw_addr(&printer.offset((addr.0, 0)).cropped((addr.1, height)));
682        self.draw_addr_hex_sep(&printer.offset((addr_sep.0, 0)).cropped((addr_sep.1, height)));
683        self.draw_hex(&printer.offset((hex.0, 0)).cropped((hex.1, height)));
684        if self.config.show_ascii {
685            self.draw_ascii_sep(&printer.offset((ascii_sep.0, 0)).cropped((ascii_sep.1, height)));
686            self.draw_ascii(&printer.offset((ascii.0, 0)).cropped((ascii.1, height)));
687        }
688
689        if self.state != DisplayState::Disabled {
690            self.highlight_current_hex(&printer.offset((hex.0, 0)).cropped((hex.1, height)).focused(true));
691            if self.config.show_ascii {
692                self.highlight_current_ascii(&printer.offset((ascii.0, 0)).cropped((ascii.1, height)).focused(true));
693            }
694        }
695    }
696
697    fn take_focus(&mut self, _: Direction) -> Result<EventResult, CannotFocus> {
698        (self.state != DisplayState::Disabled)
699            .then(EventResult::consumed)
700            .ok_or(CannotFocus)
701    }
702}
703
704//TODO: needs_relayout: only when cursor moved or data has been updated (either internally or externally)
705//      required_size:  support different views (e.g. wihtout ascii, without addr, hex only)