Merge pull request #949 from npwoods/make_filesel_current_path_clickable

Make filesel current path clickable [Nathan Woods]
This commit is contained in:
Vas Crabb 2016-06-14 14:05:46 +10:00 committed by GitHub
commit f3a11b6fd2
9 changed files with 837 additions and 384 deletions

View File

@ -75,6 +75,8 @@ files {
MAME_DIR .. "src/frontend/mame/pluginopts.h",
MAME_DIR .. "src/frontend/mame/ui/ui.cpp",
MAME_DIR .. "src/frontend/mame/ui/ui.h",
MAME_DIR .. "src/frontend/mame/ui/text.cpp",
MAME_DIR .. "src/frontend/mame/ui/text.h",
MAME_DIR .. "src/frontend/mame/ui/devctrl.h",
MAME_DIR .. "src/frontend/mame/ui/menu.cpp",
MAME_DIR .. "src/frontend/mame/ui/menu.h",

View File

@ -319,13 +319,63 @@ menu_file_selector::~menu_file_selector()
void menu_file_selector::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
extra_text_render(top, bottom,
origx1, origy1, origx2, origy2,
m_current_directory.c_str(),
nullptr);
// lay out extra text
auto layout = ui().create_layout(container);
layout.add_text(m_current_directory.c_str());
// position this extra text
float x1, y1, x2, y2;
extra_text_position(origx1, origx2, origy1, top, layout, -1, x1, y1, x2, y2);
// draw a box
ui().draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
// take off the borders
x1 += UI_BOX_LR_BORDER;
y1 += UI_BOX_TB_BORDER;
size_t hit_start = 0, hit_span = 0;
if (mouse_hit
&& layout.hit_test(mouse_x - x1, mouse_y - y1, hit_start, hit_span)
&& m_current_directory.substr(hit_start, hit_span) != PATH_SEPARATOR)
{
// we're hovering over a directory! highlight it
auto target_dir_start = m_current_directory.rfind(PATH_SEPARATOR, hit_start) + 1;
auto target_dir_end = m_current_directory.find(PATH_SEPARATOR, hit_start + hit_span);
m_hover_directory = m_current_directory.substr(0, target_dir_end);
// highlight the text in question
rgb_t fgcolor = UI_MOUSEOVER_COLOR;
rgb_t bgcolor = UI_MOUSEOVER_BG_COLOR;
layout.restyle(target_dir_start, target_dir_end - target_dir_start, &fgcolor, &bgcolor);
}
else
{
// we are not hovering over anything
m_hover_directory.clear();
}
// draw the text within it
layout.emit(container, x1, y1);
}
//-------------------------------------------------
// custom_mouse_down - perform our special mouse down
//-------------------------------------------------
bool menu_file_selector::custom_mouse_down()
{
if (m_hover_directory.length() > 0)
{
m_current_directory = m_hover_directory;
reset(reset_options::SELECT_FIRST);
return true;
}
return false;
}
//-------------------------------------------------
// compare_file_selector_entries - sorting proc

View File

@ -69,6 +69,9 @@ public:
virtual void handle() override;
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
protected:
virtual bool custom_mouse_down() override;
private:
enum file_selector_entry_type
{
@ -98,6 +101,7 @@ private:
bool m_has_create;
int * m_result;
file_selector_entry * m_entrylist;
std::string m_hover_directory;
char m_filename_buffer[1024];
// methods

View File

@ -514,8 +514,6 @@ void menu::draw(UINT32 flags, float origx0, float origy0)
bool selected_subitem_too_big = false;
int itemnum, linenum;
bool mouse_hit, mouse_button;
float mouse_x = -1, mouse_y = -1;
if (ui().options().use_background_image() && &machine().system() == &GAME_NAME(___empty) && bgrnd_bitmap->valid() && !noimage)
container->add_quad(0.0f, 0.0f, 1.0f, 1.0f, rgb_t::white, bgrnd_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
@ -916,6 +914,9 @@ void menu::handle_events(UINT32 flags)
{
// if we are hovering over a valid item, select it with a single click
case UI_EVENT_MOUSE_DOWN:
if (custom_mouse_down())
return;
if ((flags & PROCESS_ONLYCHAR) == 0)
{
if (hover >= 0 && hover < item.size())
@ -2957,24 +2958,13 @@ void menu::set_pressed()
void menu::extra_text_draw_box(float origx1, float origx2, float origy, float yspan, const char *text, int direction)
{
float text_width, text_height;
float width, maxwidth;
float x1, y1, x2, y2;
// get the size of the text
ui().draw_text_full(container,text, 0.0f, 0.0f, 1.0f, JUSTIFY_LEFT, WRAP_WORD,
DRAW_NONE, rgb_t::white, rgb_t::black, &text_width, &text_height);
width = text_width + (2 * UI_BOX_LR_BORDER);
maxwidth = MAX(width, origx2 - origx1);
auto layout = ui().create_layout(container);
layout.add_text(text);
// compute our bounds
x1 = 0.5f - 0.5f * maxwidth;
x2 = x1 + maxwidth;
y1 = origy + (yspan * direction);
y2 = origy + (UI_BOX_TB_BORDER * direction);
if (y1 > y2)
std::swap(y1, y2);
// position this extra text
float x1, y1, x2, y2;
extra_text_position(origx1, origx2, origy, yspan, layout, direction, x1, y1, x2, y2);
// draw a box
ui().draw_outlined_box(container,x1, y1, x2, y2, UI_BACKGROUND_COLOR);
@ -2984,8 +2974,29 @@ void menu::extra_text_draw_box(float origx1, float origx2, float origy, float ys
y1 += UI_BOX_TB_BORDER;
// draw the text within it
ui().draw_text_full(container,text, x1, y1, text_width, JUSTIFY_LEFT, WRAP_WORD,
DRAW_NORMAL, rgb_t::white, rgb_t::black, nullptr, nullptr);
layout.emit(container, x1, y1);
}
//-------------------------------------------------
// extra_text_position - given extra text that has
// been put into a layout, position it
//-------------------------------------------------
void menu::extra_text_position(float origx1, float origx2, float origy, float yspan, text_layout &layout,
int direction, float &x1, float &y1, float &x2, float &y2)
{
float width = layout.actual_width() + (2 * UI_BOX_LR_BORDER);
float maxwidth = MAX(width, origx2 - origx1);
// compute our bounds
x1 = 0.5f - 0.5f * maxwidth;
x2 = x1 + maxwidth;
y1 = origy + (yspan * direction);
y2 = origy + (UI_BOX_TB_BORDER * direction);
if (y1 > y2)
std::swap(y1, y2);
}

View File

@ -284,6 +284,11 @@ protected:
// draw header and footer text
void extra_text_render(float top, float bottom, float origx1, float origy1, float origx2, float origy2, const char *header, const char *footer);
void extra_text_position(float origx1, float origx2, float origy, float yspan, text_layout &layout,
int direction, float &x1, float &y1, float &x2, float &y2);
// custom events
virtual bool custom_mouse_down() { return false; }
template <typename T>
static T *topmost_menu() { return dynamic_cast<T *>(menu_stack.get()); }

View File

@ -0,0 +1,539 @@
// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods
/*********************************************************************
text.cpp
Text functionality for MAME's crude user interface
*********************************************************************/
#include "text.h"
#include "rendfont.h"
#include "render.h"
namespace ui {
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
//-------------------------------------------------
// is_space_character
//-------------------------------------------------
inline bool is_space_character(unicode_char ch)
{
return ch == ' ';
}
//-------------------------------------------------
// is_breakable_char - is a given unicode
// character a possible line break?
//-------------------------------------------------
inline bool is_breakable_char(unicode_char ch)
{
// regular spaces and hyphens are breakable
if (is_space_character(ch) || ch == '-')
return true;
// In the following character sets, any character is breakable:
// Hiragana (3040-309F)
// Katakana (30A0-30FF)
// Bopomofo (3100-312F)
// Hangul Compatibility Jamo (3130-318F)
// Kanbun (3190-319F)
// Bopomofo Extended (31A0-31BF)
// CJK Strokes (31C0-31EF)
// Katakana Phonetic Extensions (31F0-31FF)
// Enclosed CJK Letters and Months (3200-32FF)
// CJK Compatibility (3300-33FF)
// CJK Unified Ideographs Extension A (3400-4DBF)
// Yijing Hexagram Symbols (4DC0-4DFF)
// CJK Unified Ideographs (4E00-9FFF)
if (ch >= 0x3040 && ch <= 0x9fff)
return true;
// Hangul Syllables (AC00-D7AF) are breakable
if (ch >= 0xac00 && ch <= 0xd7af)
return true;
// CJK Compatibility Ideographs (F900-FAFF) are breakable
if (ch >= 0xf900 && ch <= 0xfaff)
return true;
return false;
}
/***************************************************************************
CORE IMPLEMENTATION
***************************************************************************/
//-------------------------------------------------
// ctor
//-------------------------------------------------
text_layout::text_layout(render_font &font, float xscale, float yscale, float width, text_layout::text_justify justify, text_layout::word_wrapping wrap)
: m_font(font), m_xscale(xscale), m_yscale(yscale), m_width(width), m_justify(justify), m_wrap(wrap), m_current_line(nullptr), m_last_break(0), m_text_position(0)
{
}
//-------------------------------------------------
// ctor (move)
//-------------------------------------------------
text_layout::text_layout(text_layout &&that)
: m_font(that.m_font), m_xscale(that.m_xscale), m_yscale(that.m_yscale), m_width(that.m_width), m_justify(that.m_justify), m_wrap(that.m_wrap), m_lines(std::move(that.m_lines)),
m_current_line(that.m_current_line), m_last_break(that.m_last_break), m_text_position(that.m_text_position)
{
}
//-------------------------------------------------
// dtor
//-------------------------------------------------
text_layout::~text_layout()
{
}
//-------------------------------------------------
// add_text
//-------------------------------------------------
void text_layout::add_text(const char *text, const char_style &style)
{
int position = 0;
int text_length = strlen(text);
while(position < text_length)
{
// do we need to create a new line?
if (m_current_line == nullptr)
{
// get the current character
unicode_char schar;
int scharcount;
scharcount = uchar_from_utf8(&schar, &text[position], text_length - position);
if (scharcount == -1)
break;
// if the line starts with a tab character, center it regardless
text_justify line_justify = justify();
if (schar == '\t')
{
position += scharcount;
line_justify = text_layout::CENTER;
}
// start a new line
start_new_line(line_justify, style.size);
}
// get the current character
int scharcount;
unicode_char ch;
scharcount = uchar_from_utf8(&ch, &text[position], text_length - position);
if (scharcount < 0)
break;
position += scharcount;
// set up source information
source_info source = { 0, };
source.start = m_text_position;
source.span = scharcount;
m_text_position += scharcount;
// is this an endline?
if (ch == '\n')
{
// first, start a line if we have not already
if (m_current_line == nullptr)
start_new_line(LEFT, style.size);
// and then close up the current line
update_maximum_line_width();
m_current_line = nullptr;
}
else
{
// if we hit a space, remember the location and width *without* the space
if (is_space_character(ch))
m_last_break = m_current_line->character_count();
// append the character
m_current_line->add_character(ch, style, source);
// do we have to wrap?
if (wrap() != NEVER && m_current_line->width() > m_width)
{
switch (wrap())
{
case TRUNCATE:
truncate_wrap();
break;
case WORD:
word_wrap();
break;
default:
fatalerror("invalid word wrapping value");
break;
}
}
else
{
// we didn't wrap - if we hit any non-space breakable character, remember the location and width
// *with* the breakable character
if (ch != ' ' && is_breakable_char(ch))
m_last_break = m_current_line->character_count();
}
}
}
}
//-------------------------------------------------
// update_maximum_line_width
//-------------------------------------------------
void text_layout::update_maximum_line_width()
{
m_maximum_line_width = actual_width();
}
//-------------------------------------------------
// actual_width
//-------------------------------------------------
float text_layout::actual_width() const
{
float current_line_width = m_current_line ? m_current_line->width() : 0;
return MAX(m_maximum_line_width, current_line_width);
}
//-------------------------------------------------
// actual_height
//-------------------------------------------------
float text_layout::actual_height() const
{
line *last_line = (m_lines.size() > 0)
? m_lines[m_lines.size() - 1].get()
: nullptr;
return last_line
? last_line->yoffset() + last_line->height()
: 0;
}
//-------------------------------------------------
// start_new_line
//-------------------------------------------------
void text_layout::start_new_line(text_layout::text_justify justify, float height)
{
// create a new line
std::unique_ptr<line> new_line(global_alloc_clear<line>(*this, justify, actual_height(), height * yscale()));
// update the current line
update_maximum_line_width();
m_current_line = new_line.get();
m_last_break = 0;
// append it
m_lines.push_back(std::move(new_line));
}
//-------------------------------------------------
// get_char_width
//-------------------------------------------------
float text_layout::get_char_width(unicode_char ch, float size)
{
return font().char_width(size * yscale(), xscale() / yscale(), ch);
}
//-------------------------------------------------
// truncate_wrap
//-------------------------------------------------
void text_layout::truncate_wrap()
{
// for now, lets assume that we're only truncating the last character
size_t truncate_position = m_current_line->character_count() - 1;
const auto& truncate_char = m_current_line->character(truncate_position);
// copy style information
char_style style = truncate_char.style;
// copy source information
source_info source = { 0, };
source.start = truncate_char.source.start + truncate_char.source.span;
source.span = 0;
// figure out how wide an elipsis is
float elipsis_width = 3 * get_char_width('.', style.size);
// where should we really truncate from?
while (truncate_position > 0 && m_current_line->character(truncate_position).xoffset + elipsis_width < width())
truncate_position--;
// truncate!!!
m_current_line->truncate(truncate_position);
// and append the elipsis
m_current_line->add_character('.', style, source);
// finally start a new line
start_new_line(m_current_line->justify(), style.size);
}
//-------------------------------------------------
// word_wrap
//-------------------------------------------------
void text_layout::word_wrap()
{
// keep track of the last line and break
line *last_line = m_current_line;
size_t last_break = m_last_break;
// start a new line with the same justification
start_new_line(last_line->justify(), last_line->character(last_line->character_count() - 1).style.size);
// find the begining of the word to wrap
size_t position = last_break;
while (position + 1 < last_line->character_count() && is_space_character(last_line->character(position).character))
position++;
// transcribe the characters
for (size_t i = position; i < last_line->character_count(); i++)
{
auto &ch = last_line->character(i);
m_current_line->add_character(ch.character, ch.style, ch.source);
}
// and finally, truncate the last line
last_line->truncate(last_break);
}
//-------------------------------------------------
// hit_test
//-------------------------------------------------
bool text_layout::hit_test(float x, float y, size_t &start, size_t &span) const
{
for (const auto &line : m_lines)
{
if (y >= line->yoffset() && y < line->yoffset() + line->height())
{
float line_xoffset = line->xoffset();
if (x >= line_xoffset && x < line_xoffset + line->width())
{
for (size_t i = 0; i < line->character_count(); i++)
{
const auto &ch = line->character(i);
if (x >= ch.xoffset && x < ch.xoffset + ch.xwidth)
{
start = ch.source.start;
span = ch.source.span;
return true;
}
}
}
}
}
start = 0;
span = 0;
return false;
}
//-------------------------------------------------
// restyle
//-------------------------------------------------
void text_layout::restyle(size_t start, size_t span, rgb_t *fgcolor, rgb_t *bgcolor)
{
for (const auto &line : m_lines)
{
for (size_t i = 0; i < line->character_count(); i++)
{
auto &ch = line->character(i);
if (ch.source.start >= start && ch.source.start + ch.source.span <= start + span)
{
if (fgcolor != nullptr)
ch.style.fgcolor = *fgcolor;
if (bgcolor != nullptr)
ch.style.bgcolor = *bgcolor;
}
}
}
}
//-------------------------------------------------
// get_wrap_info
//-------------------------------------------------
int text_layout::get_wrap_info(std::vector<int> &xstart, std::vector<int> &xend) const
{
// this is a hacky method (tailored to the need to implement
// mame_ui_manager::wrap_text) but so be it
int line_count = 0;
for (const auto &line : m_lines)
{
int start_pos = 0;
int end_pos = 0;
auto line_character_count = line->character_count();
if (line_character_count > 0)
{
start_pos = line->character(0).source.start;
end_pos = line->character(line_character_count - 1).source.start
+ line->character(line_character_count - 1).source.span;
}
line_count++;
xstart.push_back(start_pos);
xend.push_back(end_pos);
}
return line_count;
}
//-------------------------------------------------
// emit
//-------------------------------------------------
void text_layout::emit(render_container *container, float x, float y)
{
for (const auto &line : m_lines)
{
float line_xoffset = line->xoffset();
// emit every single character
for (auto i = 0; i < line->character_count(); i++)
{
auto &ch = line->character(i);
// position this specific character correctly (TODO - this doesn't
// handle differently sized text (yet)
float char_x = x + line_xoffset + ch.xoffset;
float char_y = y + line->yoffset();
float char_width = ch.xwidth;
float char_height = line->height();
// render the background of the character (if present)
if (ch.style.bgcolor.a() != 0)
container->add_rect(char_x, char_y, char_x + char_width, char_y + char_height, ch.style.bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
// render the foreground
container->add_char(
char_x,
char_y,
char_height,
xscale() / yscale(),
ch.style.fgcolor,
font(),
ch.character);
}
}
}
//-------------------------------------------------
// line::ctor
//-------------------------------------------------
text_layout::line::line(text_layout &layout, text_justify justify, float yoffset, float height)
: m_layout(layout), m_justify(justify), m_yoffset(yoffset), m_width(0.0), m_height(height)
{
}
//-------------------------------------------------
// line::add_character
//-------------------------------------------------
void text_layout::line::add_character(unicode_char ch, const char_style &style, const source_info &source)
{
// get the width of this character
float chwidth = m_layout.get_char_width(ch, style.size);
// create the positioned character
positioned_char positioned_char = { 0, };
positioned_char.character = ch;
positioned_char.xoffset = m_width;
positioned_char.xwidth = chwidth;
positioned_char.style = style;
positioned_char.source = source;
// append the character
m_characters.push_back(positioned_char);
m_width += chwidth;
// we might be bigger
m_height = MAX(m_height, style.size * m_layout.yscale());
}
//-------------------------------------------------
// line::xoffset
//-------------------------------------------------
float text_layout::line::xoffset() const
{
float result;
switch (justify())
{
case LEFT:
default:
result = 0;
break;
case CENTER:
result = (m_layout.width() - width()) / 2;
break;
case RIGHT:
result = m_layout.width() - width();
break;
}
return result;
}
//-------------------------------------------------
// line::truncate
//-------------------------------------------------
void text_layout::line::truncate(size_t position)
{
assert(position <= m_characters.size());
// are we actually truncating?
if (position < m_characters.size())
{
// set the width as appropriate
m_width = m_characters[position].xoffset;
// and resize the array
m_characters.resize(position);
}
}
} // namespace ui

160
src/frontend/mame/ui/text.h Normal file
View File

@ -0,0 +1,160 @@
// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods
/***************************************************************************
text.h
Text functionality for MAME's crude user interface
***************************************************************************/
#pragma once
#ifndef MAME_FRONTEND_UI_TEXT_H
#define MAME_FRONTEND_UI_TEXT_H
#include "palette.h"
#include "unicode.h"
class render_font;
class render_container;
namespace ui {
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
class text_layout
{
public:
// justification options for text
enum text_justify
{
LEFT = 0,
CENTER,
RIGHT
};
// word wrapping options
enum word_wrapping
{
NEVER,
TRUNCATE,
WORD
};
// ctor/dtor
text_layout(render_font &font, float xscale, float yscale, float width, text_justify justify, word_wrapping wrap);
text_layout(text_layout &&that);
~text_layout();
// accessors
render_font &font() const { return m_font; }
float xscale() const { return m_xscale; }
float yscale() const { return m_yscale; }
float width() const { return m_width; }
text_justify justify() const { return m_justify; }
word_wrapping wrap() const { return m_wrap; }
// methods
float actual_width() const;
float actual_height() const;
bool hit_test(float x, float y, size_t &start, size_t &span) const;
void restyle(size_t start, size_t span, rgb_t *fgcolor, rgb_t *bgcolor);
int get_wrap_info(std::vector<int> &xstart, std::vector<int> &xend) const;
void emit(render_container *container, float x, float y);
void add_text(const char *text, rgb_t fgcolor = rgb_t::white, rgb_t bgcolor = rgb_t(0,0,0,0), float size = 1.0)
{
// create the style
char_style style = { 0, };
style.fgcolor = fgcolor;
style.bgcolor = bgcolor;
style.size = size;
// and add the text
add_text(text, style);
}
private:
// text style information - in a struct to facilitate copying
struct char_style
{
rgb_t fgcolor;
rgb_t bgcolor;
float size;
};
// information about the "source" of a chracter - also in a struct
// to facilitate copying
struct source_info
{
size_t start;
size_t span;
};
// this should really be "positioned glyph" as glyphs != characters, but
// we'll get there eventually
struct positioned_char
{
unicode_char character;
char_style style;
source_info source;
float xoffset;
float xwidth;
};
// class to represent a line
class line
{
public:
line(text_layout &layout, text_justify justify, float yoffset, float height);
// methods
void add_character(unicode_char ch, const char_style &style, const source_info &source);
void truncate(size_t position);
// accessors
float xoffset() const;
float yoffset() const { return m_yoffset; }
float width() const { return m_width; }
float height() const { return m_height; }
text_justify justify() const { return m_justify; }
size_t character_count() const { return m_characters.size(); }
const positioned_char &character(size_t index) const { return m_characters[index]; }
positioned_char &character(size_t index) { return m_characters[index]; }
private:
std::vector<positioned_char> m_characters;
text_layout &m_layout;
text_justify m_justify;
float m_yoffset;
float m_width;
float m_height;
};
// instance variables
render_font &m_font;
float m_xscale;
float m_yscale;
float m_width;
float m_maximum_line_width;
text_justify m_justify;
word_wrapping m_wrap;
std::vector<std::unique_ptr<line>> m_lines;
line *m_current_line;
size_t m_last_break;
size_t m_text_position;
// methods
void add_text(const char *text, const char_style &style);
void start_new_line(text_justify justify, float height);
float get_char_width(unicode_char ch, float size);
void truncate_wrap();
void word_wrap();
void update_maximum_line_width();
};
} // namespace ui
#endif // MAME_FRONTEND_UI_TEXT_H

View File

@ -125,51 +125,6 @@ std::vector<ui::menu_item> mame_ui_manager::slider_list;
slider_state *mame_ui_manager::slider_current;
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
//-------------------------------------------------
// is_breakable_char - is a given unicode
// character a possible line break?
//-------------------------------------------------
static inline int is_breakable_char(unicode_char ch)
{
// regular spaces and hyphens are breakable
if (ch == ' ' || ch == '-')
return TRUE;
// In the following character sets, any character is breakable:
// Hiragana (3040-309F)
// Katakana (30A0-30FF)
// Bopomofo (3100-312F)
// Hangul Compatibility Jamo (3130-318F)
// Kanbun (3190-319F)
// Bopomofo Extended (31A0-31BF)
// CJK Strokes (31C0-31EF)
// Katakana Phonetic Extensions (31F0-31FF)
// Enclosed CJK Letters and Months (3200-32FF)
// CJK Compatibility (3300-33FF)
// CJK Unified Ideographs Extension A (3400-4DBF)
// Yijing Hexagram Symbols (4DC0-4DFF)
// CJK Unified Ideographs (4E00-9FFF)
if (ch >= 0x3040 && ch <= 0x9fff)
return TRUE;
// Hangul Syllables (AC00-D7AF) are breakable
if (ch >= 0xac00 && ch <= 0xd7af)
return TRUE;
// CJK Compatibility Ideographs (F900-FAFF) are breakable
if (ch >= 0xf900 && ch <= 0xfaff)
return TRUE;
return FALSE;
}
/***************************************************************************
CORE IMPLEMENTATION
***************************************************************************/
@ -600,204 +555,25 @@ void mame_ui_manager::draw_text(render_container *container, const char *buf, fl
void mame_ui_manager::draw_text_full(render_container *container, const char *origs, float x, float y, float origwrapwidth, int justify, int wrap, int draw, rgb_t fgcolor, rgb_t bgcolor, float *totalwidth, float *totalheight, float text_size)
{
float lineheight = get_line_height() * text_size;
const char *ends = origs + strlen(origs);
float wrapwidth = origwrapwidth;
const char *s = origs;
const char *linestart;
float cury = y;
float maxwidth = 0;
float aspect = machine().render().ui_aspect(container);
// create the layout
auto layout = create_layout(container, origwrapwidth, (ui::text_layout::text_justify)justify, (ui::text_layout::word_wrapping)wrap);
// if we don't want wrapping, guarantee a huge wrapwidth
if (wrap == WRAP_NEVER)
wrapwidth = 1000000.0f;
if (wrapwidth <= 0)
return;
// append text to it
layout.add_text(
origs,
fgcolor,
draw == DRAW_OPAQUE ? bgcolor : rgb_t(0, 0, 0, 0),
text_size);
// loop over lines
while (*s != 0)
{
const char *lastbreak = nullptr;
int line_justify = justify;
unicode_char schar;
int scharcount;
float lastbreak_width = 0;
float curwidth = 0;
float curx = x;
// and emit it (if we are asked to do so)
if (draw != DRAW_NONE)
layout.emit(container, x, y);
// get the current character
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
// if the line starts with a tab character, center it regardless
if (schar == '\t')
{
s += scharcount;
line_justify = JUSTIFY_CENTER;
}
// remember the starting position of the line
linestart = s;
// loop while we have characters and are less than the wrapwidth
while (*s != 0 && curwidth <= wrapwidth)
{
float chwidth;
// get the current chcaracter
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
// if we hit a newline, stop immediately
if (schar == '\n')
break;
// get the width of this character
chwidth = get_font()->char_width(lineheight, aspect, schar);
// if we hit a space, remember the location and width *without* the space
if (schar == ' ')
{
lastbreak = s;
lastbreak_width = curwidth;
}
// add the width of this character and advance
curwidth += chwidth;
s += scharcount;
// if we hit any non-space breakable character, remember the location and width
// *with* the breakable character
if (schar != ' ' && is_breakable_char(schar) && curwidth <= wrapwidth)
{
lastbreak = s;
lastbreak_width = curwidth;
}
}
// if we accumulated too much for the current width, we need to back off
if (curwidth > wrapwidth)
{
// if we're word wrapping, back up to the last break if we can
if (wrap == WRAP_WORD)
{
// if we hit a break, back up to there with the appropriate width
if (lastbreak != nullptr)
{
s = lastbreak;
curwidth = lastbreak_width;
}
// if we didn't hit a break, back up one character
else if (s > linestart)
{
// get the previous character
s = (const char *)utf8_previous_char(s);
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
curwidth -= get_font()->char_width(lineheight, aspect, schar);
// if back to 0, there is no space to draw even a single char
if (curwidth <= 0)
break;
}
}
// if we're truncating, make sure we have enough space for the ...
else if (wrap == WRAP_TRUNCATE)
{
// add in the width of the ...
curwidth += 3.0f * get_font()->char_width(lineheight, aspect, '.');
// while we are above the wrap width, back up one character
while (curwidth > wrapwidth && s > linestart)
{
// get the previous character
s = (const char *)utf8_previous_char(s);
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
curwidth -= get_font()->char_width(lineheight, aspect, schar);
}
}
}
// align according to the justfication
if (line_justify == JUSTIFY_CENTER)
curx += (origwrapwidth - curwidth) * 0.5f;
else if (line_justify == JUSTIFY_RIGHT)
curx += origwrapwidth - curwidth;
// track the maximum width of any given line
if (curwidth > maxwidth)
maxwidth = curwidth;
// if opaque, add a black box
if (draw == DRAW_OPAQUE)
container->add_rect(curx, cury, curx + curwidth, cury + lineheight, bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
// loop from the line start and add the characters
while (linestart < s)
{
// get the current character
unicode_char linechar;
int linecharcount = uchar_from_utf8(&linechar, linestart, ends - linestart);
if (linecharcount == -1)
break;
if (draw != DRAW_NONE)
{
container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), linechar);
curx += get_font()->char_width(lineheight, aspect, linechar);
}
linestart += linecharcount;
}
// append ellipses if needed
if (wrap == WRAP_TRUNCATE && *s != 0 && draw != DRAW_NONE)
{
container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), '.');
curx += get_font()->char_width(lineheight, aspect, '.');
container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), '.');
curx += get_font()->char_width(lineheight, aspect, '.');
container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), '.');
curx += get_font()->char_width(lineheight, aspect, '.');
}
// if we're not word-wrapping, we're done
if (wrap != WRAP_WORD)
break;
// advance by a row
cury += lineheight;
// skip past any spaces at the beginning of the next line
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
if (schar == '\n')
s += scharcount;
else
while (*s && isspace(schar))
{
s += scharcount;
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
}
}
// report the width and height of the resulting space
// return width/height
if (totalwidth)
*totalwidth = maxwidth;
*totalwidth = layout.actual_width();
if (totalheight)
*totalheight = cury - y;
*totalheight = layout.actual_height();
}
@ -2536,136 +2312,40 @@ void mame_ui_manager::set_use_natural_keyboard(bool use_natural_keyboard)
assert(error.empty());
}
//-------------------------------------------------
// wrap_text
//-------------------------------------------------
ui::text_layout mame_ui_manager::create_layout(render_container *container, float width, ui::text_layout::text_justify justify, ui::text_layout::word_wrapping wrap)
{
// determine scale factors
float yscale = get_line_height();
float xscale = yscale * machine().render().ui_aspect(container);
// create the layout
return ui::text_layout(*get_font(), xscale, yscale, width, justify, wrap);
}
//-------------------------------------------------
// wrap_text
//-------------------------------------------------
int mame_ui_manager::wrap_text(render_container *container, const char *origs, float x, float y, float origwrapwidth, std::vector<int> &xstart, std::vector<int> &xend, float text_size)
{
float lineheight = get_line_height() * text_size;
const char *ends = origs + strlen(origs);
float wrapwidth = origwrapwidth;
const char *s = origs;
const char *linestart;
float maxwidth = 0;
float aspect = machine().render().ui_aspect(container);
int count = 0;
// create the layout
auto layout = create_layout(container, origwrapwidth, ui::text_layout::LEFT, ui::text_layout::WORD);
// loop over lines
while (*s != 0)
{
const char *lastbreak = nullptr;
unicode_char schar;
int scharcount;
float lastbreak_width = 0;
float curwidth = 0;
// add the text
layout.add_text(
origs,
rgb_t::black,
rgb_t::black,
text_size);
// get the current character
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
// remember the starting position of the line
linestart = s;
// loop while we have characters and are less than the wrapwidth
while (*s != 0 && curwidth <= wrapwidth)
{
float chwidth;
// get the current chcaracter
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
// if we hit a newline, stop immediately
if (schar == '\n')
break;
// get the width of this character
chwidth = get_font()->char_width(lineheight, aspect, schar);
// if we hit a space, remember the location and width *without* the space
if (schar == ' ')
{
lastbreak = s;
lastbreak_width = curwidth;
}
// add the width of this character and advance
curwidth += chwidth;
s += scharcount;
// if we hit any non-space breakable character, remember the location and width
// *with* the breakable character
if (schar != ' ' && is_breakable_char(schar) && curwidth <= wrapwidth)
{
lastbreak = s;
lastbreak_width = curwidth;
}
}
// if we accumulated too much for the current width, we need to back off
if (curwidth > wrapwidth)
{
// if we hit a break, back up to there with the appropriate width
if (lastbreak != nullptr)
{
s = lastbreak;
curwidth = lastbreak_width;
}
// if we didn't hit a break, back up one character
else if (s > linestart)
{
// get the previous character
s = (const char *)utf8_previous_char(s);
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
curwidth -= get_font()->char_width(lineheight, aspect, schar);
}
}
// track the maximum width of any given line
if (curwidth > maxwidth)
maxwidth = curwidth;
xstart.push_back(linestart - origs);
xend.push_back(s - origs);
// loop from the line start and add the characters
while (linestart < s)
{
// get the current character
unicode_char linechar;
int linecharcount = uchar_from_utf8(&linechar, linestart, ends - linestart);
if (linecharcount == -1)
break;
linestart += linecharcount;
}
// advance by a row
count++;
// skip past any spaces at the beginning of the next line
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
if (schar == '\n')
s += scharcount;
else
while (*s && isspace(schar))
{
s += scharcount;
scharcount = uchar_from_utf8(&schar, s, ends - s);
if (scharcount == -1)
break;
}
}
return count;
// and get the wrapping info
return layout.get_wrap_info(xstart, xend);
}
//-------------------------------------------------

View File

@ -21,6 +21,7 @@
#include "ui/uimain.h"
#include "ui/menuitem.h"
#include "ui/slider.h"
#include "ui/text.h"
namespace ui {
@ -256,6 +257,7 @@ public:
// other
void process_natural_keyboard();
ui::text_layout create_layout(render_container *container, float width = 1.0, ui::text_layout::text_justify justify = ui::text_layout::LEFT, ui::text_layout::word_wrapping wrap = ui::text_layout::WORD);
// word wrap
int wrap_text(render_container *container, const char *origs, float x, float y, float origwrapwidth, std::vector<int> &xstart, std::vector<int> &xend, float text_size = 1.0f);