mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Better text wrapping and proper out of bounds checks
This commit is contained in:
parent
5b78e4eee4
commit
4d638540e0
@ -56,10 +56,9 @@ const CHAT_ICON_WIDTH: f64 = 16.0;
|
||||
const CHAT_MARGIN_THICKNESS: f64 = 2.0;
|
||||
const CHAT_ICON_HEIGHT: f64 = 16.0;
|
||||
pub const DEFAULT_CHAT_BOX_WIDTH: f64 = 470.0;
|
||||
const CHAT_BOX_INPUT_WIDTH: f64 = 460.0 - CHAT_ICON_WIDTH - 1.0;
|
||||
pub const DEFAULT_CHAT_BOX_HEIGHT: f64 = 154.0;
|
||||
const MIN_DIMENSION: Vec2<f64> = Vec2::new(300., 100.);
|
||||
const MAX_DIMENSION: Vec2<f64> = Vec2::new(650., 500.);
|
||||
pub const DEFAULT_CHAT_BOX_HEIGHT: f64 = 150.0;
|
||||
const MIN_DIMENSION: Vec2<f64> = Vec2::new(400.0, 150.0);
|
||||
const MAX_DIMENSION: Vec2<f64> = Vec2::new(650.0, 500.0);
|
||||
|
||||
const CHAT_TAB_HEIGHT: f64 = 20.0;
|
||||
const CHAT_TAB_ALL_WIDTH: f64 = 40.0;
|
||||
@ -237,6 +236,8 @@ impl<'a> Widget for Chat<'a> {
|
||||
let chat_tabs = &chat_settings.chat_tabs;
|
||||
let current_chat_tab = chat_settings.chat_tab_index.and_then(|i| chat_tabs.get(i));
|
||||
|
||||
let chat_box_input_width = self.chat_size.x - CHAT_ICON_WIDTH - 7.0;
|
||||
|
||||
// Empty old messages
|
||||
state.update(|s| {
|
||||
while s.messages.len() > MAX_MESSAGES {
|
||||
@ -244,8 +245,8 @@ impl<'a> Widget for Chat<'a> {
|
||||
}
|
||||
});
|
||||
|
||||
let maximum_chat_size = self.chat_size
|
||||
+ Vec2::unit_y() * (CHAT_TAB_HEIGHT + CHAT_ICON_HEIGHT + CHAT_MARGIN_THICKNESS * 2.0);
|
||||
let chat_in_screen_upper =
|
||||
self.chat_pos.y > self.global_state.window.logical_size().y / 2.0;
|
||||
let handle_chat_mouse_events = |chat_widget, ui: &mut UiCell, events: &mut Vec<Event>| {
|
||||
let pos_delta: Vec2<f64> = ui
|
||||
.widget_input(chat_widget)
|
||||
@ -254,11 +255,12 @@ impl<'a> Widget for Chat<'a> {
|
||||
.map(|drag| Vec2::<f64>::from(drag.delta_xy))
|
||||
.sum();
|
||||
if !pos_delta.is_approx_zero() {
|
||||
let pos = (self.chat_pos + pos_delta).map(|e| e.max(0.)).map3(
|
||||
self.global_state.window.logical_size(),
|
||||
maximum_chat_size,
|
||||
|e, wsz, csz| e.min(wsz - csz),
|
||||
let pos = (self.chat_pos + pos_delta).map(|e| e.max(0.)).map2(
|
||||
self.global_state.window.logical_size() - self.chat_size
|
||||
+ Vec2::unit_y() * CHAT_TAB_HEIGHT,
|
||||
|e, bounds| e.min(bounds),
|
||||
);
|
||||
|
||||
events.push(Event::MoveChat(pos));
|
||||
}
|
||||
let size_delta: Vec2<f64> = ui
|
||||
@ -268,15 +270,22 @@ impl<'a> Widget for Chat<'a> {
|
||||
.map(|drag| Vec2::<f64>::from(drag.delta_xy))
|
||||
.sum();
|
||||
if !size_delta.is_approx_zero() {
|
||||
let size = (self.chat_size + size_delta).map3(
|
||||
MIN_DIMENSION,
|
||||
MAX_DIMENSION,
|
||||
|sz, min, max| sz.clamp(min, max),
|
||||
let size = (self.chat_size + size_delta)
|
||||
.map3(MIN_DIMENSION, MAX_DIMENSION, |sz, min, max| {
|
||||
sz.clamp(min, max).trunc()
|
||||
})
|
||||
.map2(
|
||||
self.global_state.window.logical_size()
|
||||
- Vec2::unit_y() * CHAT_TAB_HEIGHT
|
||||
- self.chat_pos,
|
||||
|e, bounds| e.min(bounds),
|
||||
);
|
||||
events.push(Event::ResizeChat(size));
|
||||
}
|
||||
};
|
||||
|
||||
handle_chat_mouse_events(state.ids.draggable_area, ui, &mut events);
|
||||
|
||||
// Maintain scrolling //
|
||||
if !self.new_messages.is_empty() {
|
||||
for message in self.new_messages.iter() {
|
||||
@ -352,7 +361,13 @@ impl<'a> Widget for Chat<'a> {
|
||||
if let Some(replacement) = &s.completions.get(s.completions_index.unwrap()) {
|
||||
let (completed, offset) =
|
||||
do_tab_completion(cursor, &s.input.message, replacement);
|
||||
force_cursor = cursor_offset_to_index(offset, &completed, ui, self.fonts);
|
||||
force_cursor = cursor_offset_to_index(
|
||||
offset,
|
||||
&completed,
|
||||
ui,
|
||||
self.fonts,
|
||||
chat_box_input_width,
|
||||
);
|
||||
s.input.message = completed;
|
||||
}
|
||||
});
|
||||
@ -384,6 +399,7 @@ impl<'a> Widget for Chat<'a> {
|
||||
&s.input.message,
|
||||
ui,
|
||||
self.fonts,
|
||||
chat_box_input_width,
|
||||
);
|
||||
} else {
|
||||
s.input.message.clear();
|
||||
@ -420,7 +436,7 @@ impl<'a> Widget for Chat<'a> {
|
||||
// Any changes to this TextEdit's width and font size must be reflected in
|
||||
// `cursor_offset_to_index` below.
|
||||
let mut text_edit = TextEdit::new(&state.input.message)
|
||||
.w(CHAT_BOX_INPUT_WIDTH)
|
||||
.w(chat_box_input_width)
|
||||
.restrict_to_height(false)
|
||||
.color(color)
|
||||
.line_spacing(2.0)
|
||||
@ -437,8 +453,14 @@ impl<'a> Widget for Chat<'a> {
|
||||
};
|
||||
Rectangle::fill([self.chat_size.x, y])
|
||||
.rgba(0.0, 0.0, 0.0, chat_settings.chat_opacity + 0.1)
|
||||
.bottom_left_with_margins_on(ui.window, self.chat_pos.y, self.chat_pos.x)
|
||||
.w(self.chat_size.x)
|
||||
.and(|r| {
|
||||
if chat_in_screen_upper {
|
||||
r.down_from(state.ids.message_box_bg, CHAT_MARGIN_THICKNESS / 2.0)
|
||||
} else {
|
||||
r.bottom_left_with_margins_on(ui.window, self.chat_pos.y, self.chat_pos.x)
|
||||
}
|
||||
})
|
||||
.set(state.ids.chat_input_bg, ui);
|
||||
|
||||
//border around focused chat window
|
||||
@ -481,7 +503,7 @@ impl<'a> Widget for Chat<'a> {
|
||||
Rectangle::fill([self.chat_size.x, self.chat_size.y])
|
||||
.rgba(0.0, 0.0, 0.0, chat_settings.chat_opacity)
|
||||
.and(|r| {
|
||||
if input_focused {
|
||||
if input_focused && !chat_in_screen_upper {
|
||||
r.up_from(
|
||||
state.ids.chat_input_border_up,
|
||||
0.0 + CHAT_MARGIN_THICKNESS / 2.0,
|
||||
@ -572,7 +594,8 @@ impl<'a> Widget for Chat<'a> {
|
||||
let text = Text::new(text)
|
||||
.font_size(self.fonts.opensans.scale(15))
|
||||
.font_id(self.fonts.opensans.conrod_id)
|
||||
.w(self.chat_size.x - 17.0)
|
||||
.w(self.chat_size.x - CHAT_ICON_WIDTH - 1.0)
|
||||
.wrap_by_word()
|
||||
.color(color)
|
||||
.line_spacing(2.0);
|
||||
|
||||
@ -783,20 +806,16 @@ impl<'a> Widget for Chat<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle::fill([self.chat_size.x, self.chat_size.y])
|
||||
.rgba(0.0, 0.0, 0.0, 0.0)
|
||||
Rectangle::fill_with([self.chat_size.x, self.chat_size.y], color::TRANSPARENT)
|
||||
.and(|r| {
|
||||
if input_focused {
|
||||
r.up_from(
|
||||
state.ids.chat_input_border_up,
|
||||
0.0 + CHAT_MARGIN_THICKNESS / 2.0,
|
||||
)
|
||||
r.up_from(state.ids.chat_input_border_up, CHAT_MARGIN_THICKNESS / 2.0)
|
||||
} else {
|
||||
r.bottom_left_with_margins_on(ui.window, self.chat_pos.y, self.chat_pos.x)
|
||||
}
|
||||
})
|
||||
.set(state.ids.draggable_area, ui);
|
||||
handle_chat_mouse_events(state.ids.draggable_area, ui, &mut events);
|
||||
|
||||
events
|
||||
}
|
||||
}
|
||||
@ -844,13 +863,19 @@ fn do_tab_completion(cursor: usize, input: &str, word: &str) -> (String, usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_offset_to_index(offset: usize, text: &str, ui: &Ui, fonts: &Fonts) -> Option<Index> {
|
||||
fn cursor_offset_to_index(
|
||||
offset: usize,
|
||||
text: &str,
|
||||
ui: &Ui,
|
||||
fonts: &Fonts,
|
||||
input_width: f64,
|
||||
) -> Option<Index> {
|
||||
// This moves the cursor to the given offset. Conrod is a pain.
|
||||
//
|
||||
// Width and font must match that of the chat TextEdit
|
||||
let font = ui.fonts.get(fonts.opensans.conrod_id)?;
|
||||
let font_size = fonts.opensans.scale(15);
|
||||
let infos = text::line::infos(text, font, font_size).wrap_by_whitespace(CHAT_BOX_INPUT_WIDTH);
|
||||
let infos = text::line::infos(text, font, font_size).wrap_by_whitespace(input_width);
|
||||
|
||||
cursor::index_before_char(infos, offset)
|
||||
}
|
||||
|
@ -3451,11 +3451,46 @@ impl Hud {
|
||||
}
|
||||
}
|
||||
|
||||
if global_state.settings.audio.subtitles {
|
||||
Subtitles::new(
|
||||
client,
|
||||
&global_state.settings,
|
||||
&global_state.audio.get_listener().clone(),
|
||||
&mut global_state.audio.subtitles,
|
||||
&self.fonts,
|
||||
i18n,
|
||||
)
|
||||
.set(self.ids.subtitles, ui_widgets);
|
||||
}
|
||||
|
||||
//Loot
|
||||
LootScroller::new(
|
||||
&mut self.new_loot_messages,
|
||||
client,
|
||||
&info,
|
||||
&self.show,
|
||||
&self.imgs,
|
||||
&self.item_imgs,
|
||||
&self.rot_imgs,
|
||||
&self.fonts,
|
||||
i18n,
|
||||
&self.item_i18n,
|
||||
&msm,
|
||||
item_tooltip_manager,
|
||||
self.pulse,
|
||||
)
|
||||
.set(self.ids.loot_scroller, ui_widgets);
|
||||
|
||||
self.new_loot_messages = VecDeque::new();
|
||||
|
||||
// Don't put NPC messages in chat box.
|
||||
self.new_messages
|
||||
.retain(|m| !matches!(m.chat_type, comp::ChatType::Npc(_)));
|
||||
|
||||
// Chat box
|
||||
// Draw this after loot scroller and subtitles so it can be dragged
|
||||
// even when hovering over them
|
||||
// TODO look into parenting and then settings movable widgets to floating
|
||||
if global_state.settings.interface.toggle_chat {
|
||||
for event in Chat::new(
|
||||
&mut self.new_messages,
|
||||
@ -3506,41 +3541,9 @@ impl Hud {
|
||||
}
|
||||
}
|
||||
|
||||
if global_state.settings.audio.subtitles {
|
||||
Subtitles::new(
|
||||
client,
|
||||
&global_state.settings,
|
||||
&global_state.audio.get_listener().clone(),
|
||||
&mut global_state.audio.subtitles,
|
||||
&self.fonts,
|
||||
i18n,
|
||||
)
|
||||
.set(self.ids.subtitles, ui_widgets);
|
||||
}
|
||||
|
||||
self.new_messages = VecDeque::new();
|
||||
self.new_notifications = VecDeque::new();
|
||||
|
||||
//Loot
|
||||
LootScroller::new(
|
||||
&mut self.new_loot_messages,
|
||||
client,
|
||||
&info,
|
||||
&self.show,
|
||||
&self.imgs,
|
||||
&self.item_imgs,
|
||||
&self.rot_imgs,
|
||||
&self.fonts,
|
||||
i18n,
|
||||
&self.item_i18n,
|
||||
&msm,
|
||||
item_tooltip_manager,
|
||||
self.pulse,
|
||||
)
|
||||
.set(self.ids.loot_scroller, ui_widgets);
|
||||
|
||||
self.new_loot_messages = VecDeque::new();
|
||||
|
||||
// Windows
|
||||
|
||||
// Char Window will always appear at the left side. Other Windows default to the
|
||||
|
Loading…
Reference in New Issue
Block a user