mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-06 06:15:04 +00:00
(svn r25652) -Fix: Improve text caret movement for complex scripts.
This commit is contained in:
parent
33f3cf3a5d
commit
e7dc14b25a
@ -595,6 +595,7 @@
|
||||
<ClInclude Include="..\src\story_base.h" />
|
||||
<ClInclude Include="..\src\story_type.h" />
|
||||
<ClInclude Include="..\src\strgen\strgen.h" />
|
||||
<ClInclude Include="..\src\string_base.h" />
|
||||
<ClInclude Include="..\src\string_func.h" />
|
||||
<ClInclude Include="..\src\string_type.h" />
|
||||
<ClInclude Include="..\src\stringfilter_type.h" />
|
||||
|
@ -1014,6 +1014,9 @@
|
||||
<ClInclude Include="..\src\strgen\strgen.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\string_base.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\string_func.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1654,6 +1654,10 @@
|
||||
RelativePath=".\..\src\strgen\strgen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\string_base.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\string_func.h"
|
||||
>
|
||||
|
@ -1651,6 +1651,10 @@
|
||||
RelativePath=".\..\src\strgen\strgen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\string_base.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\string_func.h"
|
||||
>
|
||||
|
@ -328,6 +328,7 @@ stdafx.h
|
||||
story_base.h
|
||||
story_type.h
|
||||
strgen/strgen.h
|
||||
string_base.h
|
||||
string_func.h
|
||||
string_type.h
|
||||
stringfilter_type.h
|
||||
|
121
src/string.cpp
121
src/string.cpp
@ -14,6 +14,7 @@
|
||||
#include "core/alloc_func.hpp"
|
||||
#include "core/math_func.hpp"
|
||||
#include "string_func.h"
|
||||
#include "string_base.h"
|
||||
|
||||
#include "table/control_codes.h"
|
||||
|
||||
@ -650,3 +651,123 @@ int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
|
||||
/* Do a normal comparison if ICU is missing or if we cannot create a collator. */
|
||||
return strcasecmp(s1, s2);
|
||||
}
|
||||
|
||||
#ifdef WITH_ICU
|
||||
|
||||
#include <unicode/utext.h>
|
||||
#include <unicode/brkiter.h>
|
||||
|
||||
/** String iterator using ICU as a backend. */
|
||||
class IcuStringIterator : public StringIterator
|
||||
{
|
||||
icu::BreakIterator *char_itr; ///< ICU iterator for characters.
|
||||
const char *string; ///< Iteration string in UTF-8.
|
||||
|
||||
public:
|
||||
IcuStringIterator() : char_itr(NULL)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
this->char_itr = icu::BreakIterator::createCharacterInstance(icu::Locale(_current_language != NULL ? _current_language->isocode : "en"), status);
|
||||
}
|
||||
|
||||
virtual ~IcuStringIterator()
|
||||
{
|
||||
delete this->char_itr;
|
||||
}
|
||||
|
||||
virtual void SetString(const char *s)
|
||||
{
|
||||
this->string = s;
|
||||
|
||||
UText text = UTEXT_INITIALIZER;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
utext_openUTF8(&text, s, -1, &status);
|
||||
this->char_itr->setText(&text, status);
|
||||
this->char_itr->first();
|
||||
}
|
||||
|
||||
virtual size_t SetCurPosition(size_t pos)
|
||||
{
|
||||
/* isBoundary has the documented side-effect of setting the current
|
||||
* position to the first valid boundary equal to or greater than
|
||||
* the passed value. */
|
||||
this->char_itr->isBoundary((int32_t)pos);
|
||||
return this->char_itr->current();
|
||||
}
|
||||
|
||||
virtual size_t Next()
|
||||
{
|
||||
int32_t pos = this->char_itr->next();
|
||||
return pos == icu::BreakIterator::DONE ? END : pos;
|
||||
}
|
||||
|
||||
virtual size_t Prev()
|
||||
{
|
||||
int32_t pos = this->char_itr->previous();
|
||||
return pos == icu::BreakIterator::DONE ? END : pos;
|
||||
}
|
||||
};
|
||||
|
||||
/* static */ StringIterator *StringIterator::Create()
|
||||
{
|
||||
return new IcuStringIterator();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/** Fallback simple string iterator. */
|
||||
class DefaultStringIterator : public StringIterator
|
||||
{
|
||||
const char *string; ///< Current string.
|
||||
size_t len; ///< String length.
|
||||
size_t cur_pos; ///< Current iteration position.
|
||||
|
||||
public:
|
||||
DefaultStringIterator() : string(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void SetString(const char *s)
|
||||
{
|
||||
this->string = s;
|
||||
this->len = strlen(s);
|
||||
this->cur_pos = 0;
|
||||
}
|
||||
|
||||
virtual size_t SetCurPosition(size_t pos)
|
||||
{
|
||||
assert(this->string != NULL && pos <= this->len);
|
||||
/* Sanitize in case we get a position inside an UTF-8 sequence. */
|
||||
while (pos > 0 && IsUtf8Part(this->string[pos])) pos--;
|
||||
return this->cur_pos = pos;
|
||||
}
|
||||
|
||||
virtual size_t Next()
|
||||
{
|
||||
assert(this->string != NULL);
|
||||
|
||||
/* Already at the end? */
|
||||
if (this->cur_pos >= this->len) return END;
|
||||
|
||||
WChar c;
|
||||
this->cur_pos += Utf8Decode(&c, this->string + this->cur_pos);
|
||||
return this->cur_pos;
|
||||
}
|
||||
|
||||
virtual size_t Prev()
|
||||
{
|
||||
assert(this->string != NULL);
|
||||
|
||||
/* Already at the beginning? */
|
||||
if (this->cur_pos == 0) return END;
|
||||
|
||||
return this->cur_pos = Utf8PrevChar(this->string + this->cur_pos) - this->string;
|
||||
}
|
||||
};
|
||||
|
||||
/* static */ StringIterator *StringIterator::Create()
|
||||
{
|
||||
return new DefaultStringIterator();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
60
src/string_base.h
Normal file
60
src/string_base.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef STRING_BASE_H
|
||||
#define STRING_BASE_H
|
||||
|
||||
#include "string_type.h"
|
||||
|
||||
/** Class for iterating over different kind of parts of a string. */
|
||||
class StringIterator {
|
||||
public:
|
||||
/** Sentinel to indicate end-of-iteration. */
|
||||
static const size_t END = SIZE_MAX;
|
||||
|
||||
/**
|
||||
* Create a new iterator instance.
|
||||
* @return New iterator instance.
|
||||
*/
|
||||
static StringIterator *Create();
|
||||
|
||||
virtual ~StringIterator() {}
|
||||
|
||||
/**
|
||||
* Set a new iteration string. Must also be called if the string contents
|
||||
* changed. The cursor is reset to the start of the string.
|
||||
* @param s New string.
|
||||
*/
|
||||
virtual void SetString(const char *s) = 0;
|
||||
|
||||
/**
|
||||
* Change the current string cursor.
|
||||
* @param p New cursor position.
|
||||
* @return Actual new cursor position at the next valid character boundary.
|
||||
* @pre p has to be inside the current string.
|
||||
*/
|
||||
virtual size_t SetCurPosition(size_t pos) = 0;
|
||||
|
||||
/**
|
||||
* Advance the cursor by one iteration unit.
|
||||
* @return New cursor position (in bytes) or #END if the cursor is already at the end of the string.
|
||||
*/
|
||||
virtual size_t Next() = 0;
|
||||
|
||||
/**
|
||||
* Move the cursor back by one iteration unit.
|
||||
* @return New cursor position (in bytes) or #END if the cursor is already at the start of the string.
|
||||
*/
|
||||
virtual size_t Prev() = 0;
|
||||
|
||||
protected:
|
||||
StringIterator() {}
|
||||
};
|
||||
|
||||
#endif /* STRING_BASE_H */
|
@ -147,6 +147,13 @@ static inline char *Utf8PrevChar(char *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline const char *Utf8PrevChar(const char *s)
|
||||
{
|
||||
const char *ret = s;
|
||||
while (IsUtf8Part(*--ret)) {}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t Utf8StringLength(const char *s);
|
||||
|
||||
/**
|
||||
|
@ -87,11 +87,11 @@ void Textbuf::DelChar(bool backspace)
|
||||
this->bytes -= len;
|
||||
this->chars--;
|
||||
|
||||
if (backspace) this->caretpos -= len;
|
||||
|
||||
this->UpdateStringIter();
|
||||
this->UpdateWidth();
|
||||
if (backspace) {
|
||||
this->caretpos -= len;
|
||||
this->UpdateCaretPosition();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,6 +147,7 @@ void Textbuf::DeleteAll()
|
||||
memset(this->buf, 0, this->max_bytes);
|
||||
this->bytes = this->chars = 1;
|
||||
this->pixels = this->caretpos = this->caretxoffs = 0;
|
||||
this->UpdateStringIter();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,9 +165,10 @@ bool Textbuf::InsertChar(WChar key)
|
||||
Utf8Encode(this->buf + this->caretpos, key);
|
||||
this->chars++;
|
||||
this->bytes += len;
|
||||
this->UpdateWidth();
|
||||
|
||||
this->caretpos += len;
|
||||
|
||||
this->UpdateStringIter();
|
||||
this->UpdateWidth();
|
||||
this->UpdateCaretPosition();
|
||||
return true;
|
||||
}
|
||||
@ -210,6 +212,7 @@ bool Textbuf::InsertClipboard()
|
||||
assert(this->chars <= this->max_chars);
|
||||
this->buf[this->bytes - 1] = '\0'; // terminating zero
|
||||
|
||||
this->UpdateStringIter();
|
||||
this->UpdateWidth();
|
||||
this->UpdateCaretPosition();
|
||||
|
||||
@ -234,12 +237,15 @@ WChar Textbuf::MoveCaretLeft()
|
||||
{
|
||||
assert(this->CanMoveCaretLeft());
|
||||
|
||||
WChar c;
|
||||
const char *s = Utf8PrevChar(this->buf + this->caretpos);
|
||||
Utf8Decode(&c, s);
|
||||
this->caretpos = s - this->buf;
|
||||
size_t pos = this->char_iter->Prev();
|
||||
if (pos == StringIterator::END) pos = 0;
|
||||
|
||||
this->caretpos = (uint16)pos;
|
||||
this->UpdateCaretPosition();
|
||||
|
||||
WChar c;
|
||||
Utf8Decode(&c, this->buf + this->caretpos);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@ -261,14 +267,24 @@ WChar Textbuf::MoveCaretRight()
|
||||
{
|
||||
assert(this->CanMoveCaretRight());
|
||||
|
||||
WChar c;
|
||||
this->caretpos += (uint16)Utf8Decode(&c, this->buf + this->caretpos);
|
||||
size_t pos = this->char_iter->Next();
|
||||
if (pos == StringIterator::END) pos = this->bytes - 1;
|
||||
|
||||
this->caretpos = (uint16)pos;
|
||||
this->UpdateCaretPosition();
|
||||
|
||||
WChar c;
|
||||
Utf8Decode(&c, this->buf + this->caretpos);
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Update the character iter after the text has changed. */
|
||||
void Textbuf::UpdateStringIter()
|
||||
{
|
||||
this->char_iter->SetString(this->buf);
|
||||
this->caretpos = (uint16)this->char_iter->SetCurPosition(this->caretpos);
|
||||
}
|
||||
|
||||
/** Update pixel width of the text. */
|
||||
void Textbuf::UpdateWidth()
|
||||
{
|
||||
@ -372,6 +388,8 @@ Textbuf::Textbuf(uint16 max_bytes, uint16 max_chars)
|
||||
assert(max_bytes != 0);
|
||||
assert(max_chars != 0);
|
||||
|
||||
this->char_iter = StringIterator::Create();
|
||||
|
||||
this->afilter = CS_ALPHANUMERAL;
|
||||
this->max_bytes = max_bytes;
|
||||
this->max_chars = max_chars == UINT16_MAX ? max_bytes : max_chars;
|
||||
@ -381,6 +399,7 @@ Textbuf::Textbuf(uint16 max_bytes, uint16 max_chars)
|
||||
|
||||
Textbuf::~Textbuf()
|
||||
{
|
||||
delete this->char_iter;
|
||||
free(this->buf);
|
||||
}
|
||||
|
||||
@ -437,6 +456,7 @@ void Textbuf::UpdateSize()
|
||||
assert(this->chars <= this->max_chars);
|
||||
|
||||
this->caretpos = this->bytes - 1;
|
||||
this->UpdateStringIter();
|
||||
this->UpdateWidth();
|
||||
|
||||
this->UpdateCaretPosition();
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "string_type.h"
|
||||
#include "strings_type.h"
|
||||
#include "string_base.h"
|
||||
|
||||
/**
|
||||
* Return values for Textbuf::HandleKeypress
|
||||
@ -61,6 +62,8 @@ struct Textbuf {
|
||||
void UpdateSize();
|
||||
|
||||
private:
|
||||
StringIterator *char_iter;
|
||||
|
||||
bool CanDelChar(bool backspace);
|
||||
WChar GetNextDelChar(bool backspace);
|
||||
void DelChar(bool backspace);
|
||||
@ -69,6 +72,7 @@ private:
|
||||
bool CanMoveCaretRight();
|
||||
WChar MoveCaretRight();
|
||||
|
||||
void UpdateStringIter();
|
||||
void UpdateWidth();
|
||||
void UpdateCaretPosition();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user