Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
Download file

root / crates / client / src / menu / text_field.rs

use winit::keyboard::KeyCode;

pub struct TextField {
    pub text: String,
    pub focused: bool,
    pub cursor: usize,
    pub anchor: usize,
    max: usize,
    /// Only `ChatAllowedCharacters`, as a Beta server would kick for anything else.
    pub chat: bool,
}

impl TextField {
    /// `GuiTextField.setText` keeps a longer existing text; `max` only limits typing.
    pub fn new(text: &str, max: usize) -> Self {
        let text = text.to_owned();
        let cursor = text.len();
        Self {
            text,
            focused: true,
            cursor,
            anchor: cursor,
            max,
            chat: false,
        }
    }

    pub fn chat(max: usize) -> Self {
        Self {
            chat: true,
            ..Self::new("", max)
        }
    }

    pub fn selection(&self) -> std::ops::Range<usize> {
        self.cursor.min(self.anchor)..self.cursor.max(self.anchor)
    }

    pub fn insert(&mut self, text: &str) {
        let selection = self.selection();
        let available = self.max.saturating_sub(
            self.text.chars().count() - self.text[selection.clone()].chars().count(),
        );
        let insert: String = text
            .chars()
            .filter(|&ch| {
                !ch.is_control()
                    && ch != '§'
                    && (!self.chat || common::text::ALLOWED_CHARACTERS.contains(ch))
            })
            .take(available)
            .collect();
        self.text.replace_range(selection.clone(), &insert);
        self.cursor = selection.start + insert.len();
        self.anchor = self.cursor;
    }

    pub fn key(&mut self, key: KeyCode, text: Option<&str>, control: bool, shift: bool) {
        let previous = |text: &str, cursor| {
            text[..cursor]
                .char_indices()
                .next_back()
                .map_or(0, |(index, _)| index)
        };
        let next = |text: &str, cursor| {
            text[cursor..]
                .chars()
                .next()
                .map_or(cursor, |ch| cursor + ch.len_utf8())
        };
        match key {
            KeyCode::KeyA if control => {
                self.anchor = 0;
                self.cursor = self.text.len();
                return;
            }
            KeyCode::Backspace => {
                if self.selection().is_empty() {
                    self.anchor = previous(&self.text, self.cursor);
                }
                self.insert("");
                return;
            }
            KeyCode::Delete => {
                if self.selection().is_empty() {
                    self.anchor = next(&self.text, self.cursor);
                }
                self.insert("");
                return;
            }
            KeyCode::Home => self.cursor = 0,
            KeyCode::End => self.cursor = self.text.len(),
            KeyCode::ArrowLeft => self.cursor = previous(&self.text, self.cursor),
            KeyCode::ArrowRight => self.cursor = next(&self.text, self.cursor),
            _ => {
                if !control && let Some(text) = text {
                    self.insert(text);
                }
                return;
            }
        }
        if !shift {
            self.anchor = self.cursor;
        }
    }
}