1
0
Fork 0

(svn r26024) [1.3] -Backport: number of prerequisites for key handling fixes/improvements

release/1.3
rubidium 2013-11-17 13:35:48 +00:00
parent c13d1d6362
commit 10fdf41107
32 changed files with 1147 additions and 101 deletions

View File

@ -1498,7 +1498,7 @@ make_cflags_and_ldflags() {
LDFLAGS="$LDFLAGS -Wl,--subsystem,windows"
fi
LIBS="$LIBS -lws2_32 -lwinmm -lgdi32 -ldxguid -lole32"
LIBS="$LIBS -lws2_32 -lwinmm -lgdi32 -ldxguid -lole32 -limm32"
if [ $cc_version -ge 44 ]; then
LDFLAGS_BUILD="$LDFLAGS_BUILD -static-libgcc -static-libstdc++"

View File

@ -131,7 +131,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -176,7 +176,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -233,7 +233,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -280,7 +280,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>

View File

@ -131,7 +131,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -176,7 +176,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -233,7 +233,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -280,7 +280,7 @@
<Culture>0x0809</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>LIBCMT.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>

View File

@ -87,7 +87,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -184,7 +184,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"
@ -292,7 +292,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -392,7 +392,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"

View File

@ -87,7 +87,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -184,7 +184,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"
@ -292,7 +292,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -392,7 +392,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"

View File

@ -88,7 +88,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -183,7 +183,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"
@ -290,7 +290,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -389,7 +389,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"

View File

@ -88,7 +88,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -183,7 +183,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"
@ -290,7 +290,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames=""
@ -389,7 +389,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib ws2_32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
AdditionalDependencies="winmm.lib ws2_32.lib imm32.lib libpng.lib zlibstat.lib lzo2.lib liblzma.lib libfreetype2.lib icuuc.lib icuin.lib icudt.lib icule.lib iculx.lib"
LinkIncremental="0"
SuppressStartupBanner="true"
IgnoreDefaultLibraryNames="LIBCMT.lib"

View File

@ -21,6 +21,7 @@
#include "settings_type.h"
#include "console_func.h"
#include "rev.h"
#include "video/video_driver.hpp"
#include "widgets/console_widget.h"
@ -184,6 +185,7 @@ struct IConsoleWindow : Window
~IConsoleWindow()
{
_iconsole_mode = ICONSOLE_CLOSED;
_video_driver->EditBoxLostFocus();
}
/**
@ -215,6 +217,9 @@ struct IConsoleWindow : Window
delta = 0;
}
/* If we have a marked area, draw a background highlight. */
if (_iconsole_cmdline.marklength != 0) GfxFillRect(this->line_offset + delta + _iconsole_cmdline.markxoffs, this->height - this->line_height, this->line_offset + delta + _iconsole_cmdline.markxoffs + _iconsole_cmdline.marklength, this->height - 1, PC_DARK_RED);
DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
if (_focused_window == this && _iconsole_cmdline.caret) {
@ -307,10 +312,70 @@ struct IConsoleWindow : Window
return ES_HANDLED;
}
virtual void InsertTextString(int wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
{
if (_iconsole_cmdline.InsertString(str, marked, caret, insert_location, replacement_end)) {
IConsoleWindow::scroll = 0;
IConsoleResetHistoryPos();
this->SetDirty();
}
}
virtual const char *GetFocusedText() const
{
return _iconsole_cmdline.buf;
}
virtual const char *GetCaret() const
{
return _iconsole_cmdline.buf + _iconsole_cmdline.caretpos;
}
virtual const char *GetMarkedText(size_t *length) const
{
if (_iconsole_cmdline.markend == 0) return NULL;
*length = _iconsole_cmdline.markend - _iconsole_cmdline.markpos;
return _iconsole_cmdline.buf + _iconsole_cmdline.markpos;
}
virtual Point GetCaretPosition() const
{
int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
Point pt = {this->line_offset + delta + _iconsole_cmdline.caretxoffs, this->height - this->line_height};
return pt;
}
virtual Rect GetTextBoundingRect(const char *from, const char *to) const
{
int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
Point p1 = GetCharPosInString(_iconsole_cmdline.buf, from, FS_NORMAL);
Point p2 = from != to ? GetCharPosInString(_iconsole_cmdline.buf, from) : p1;
Rect r = {this->line_offset + delta + p1.x, this->height - this->line_height, this->line_offset + delta + p2.x, this->height};
return r;
}
virtual const char *GetTextCharacterAtPosition(const Point &pt) const
{
int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
if (!IsInsideMM(pt.y, this->height - this->line_height, this->height)) return NULL;
return GetCharAtPosition(_iconsole_cmdline.buf, pt.x - delta);
}
virtual void OnMouseWheel(int wheel)
{
this->Scroll(-wheel);
}
virtual void OnFocusLost()
{
_video_driver->EditBoxLostFocus();
}
};
int IConsoleWindow::scroll = 0;

View File

@ -365,8 +365,8 @@ static inline T ROR(const T x, const uint8 n)
* (since it will use hardware swapping if available).
* Even though they should return uint16 and uint32, we get
* warnings if we don't cast those (why?) */
#define BSWAP32(x) ((uint32)Endian32_Swap(x))
#define BSWAP16(x) ((uint16)Endian16_Swap(x))
#define BSWAP32(x) ((uint32)CFSwapInt32(x))
#define BSWAP16(x) ((uint16)CFSwapInt16(x))
#elif defined(_MSC_VER)
/* MSVC has intrinsics for swapping, resulting in faster code */
#define BSWAP32(x) (_byteswap_ulong(x))

View File

@ -653,6 +653,21 @@ Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsiz
return layout.GetCharPosition(ch);
}
/**
* Get the character from a string that is drawn at a specific position.
* @param str String to test.
* @param x Position relative to the start of the string.
* @param start_fontsize Font size to start the text with.
* @return Pointer to the character at the position or NULL if there is no character at the position.
*/
const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize)
{
if (x < 0) return NULL;
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
return layout.GetCharAtPosition(x);
}
/**
* Draw single character horizontally centered around (x,y)
* @param c Character (glyph) to draw

View File

@ -71,6 +71,7 @@ extern Dimension _cur_resolution;
extern Palette _cur_palette; ///< Current palette
void HandleKeypress(uint keycode, WChar key);
void HandleTextInput(const char *str, bool marked = false, const char *caret = NULL, const char *insert_location = NULL, const char *replacement_end = NULL);
void HandleCtrlChanged();
void HandleMouseEvents();
void CSleep(int milliseconds);
@ -128,6 +129,7 @@ Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestio
Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &suggestion);
void LoadStringWidthTable(bool monospace = false);
Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsize = FS_NORMAL);
const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize = FS_NORMAL);
void DrawDirtyBlocks();
void SetDirtyBlocks(int left, int top, int right, int bottom);

View File

@ -735,6 +735,50 @@ Point Layouter::GetCharPosition(const char *ch) const
return p;
}
/**
* Get the character that is at a position.
* @param x Position in the string.
* @return Pointer to the character at the position or NULL if no character is at the position.
*/
const char *Layouter::GetCharAtPosition(int x) const
{
const ParagraphLayouter::Line *line = *this->Begin();;
for (int run_index = 0; run_index < line->CountRuns(); run_index++) {
const ParagraphLayouter::VisualRun *run = line->GetVisualRun(run_index);
for (int i = 0; i < run->GetGlyphCount(); i++) {
/* Not a valid glyph (empty). */
if (run->GetGlyphs()[i] == 0xFFFF) continue;
int begin_x = (int)run->GetPositions()[i * 2];
int end_x = (int)run->GetPositions()[i * 2 + 2];
if (IsInsideMM(x, begin_x, end_x)) {
/* Found our glyph, now convert to UTF-8 string index. */
size_t index = run->GetGlyphToCharMap()[i];
size_t cur_idx = 0;
for (const char *str = this->string; *str != '\0'; ) {
if (cur_idx == index) return str;
WChar c;
size_t len = Utf8Decode(&c, str);
#ifdef WITH_ICU
/* ICU uses UTF-16 internally which means we need to account for surrogate pairs. */
cur_idx += len < 4 ? 1 : 2;
#else
cur_idx++;
#endif
str += len;
}
}
}
}
return NULL;
}
/**
* Get a static font instance.
*/

View File

@ -179,6 +179,7 @@ public:
Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL);
Dimension GetBounds();
Point GetCharPosition(const char *ch) const;
const char *GetCharAtPosition(int x) const;
static void ResetFontCache(FontSize size);
static void ResetLineCache();

View File

@ -2588,7 +2588,7 @@ struct IndustryCargoesWindow : public Window {
delete lst;
break;
}
int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? this->ind_cargo - NUM_INDUSTRYTYPES : -1;
int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
ShowDropDownList(this, lst, selected, WID_IC_CARGO_DROPDOWN, 0, true);
break;
}
@ -2605,7 +2605,7 @@ struct IndustryCargoesWindow : public Window {
delete lst;
break;
}
int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? this->ind_cargo : -1;
int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
ShowDropDownList(this, lst, selected, WID_IC_IND_DROPDOWN, 0, true);
break;
}

View File

@ -764,6 +764,9 @@ void QueryString::DrawEditBox(const Window *w, int wid) const
if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
/* If we have a marked area, draw a background highlight. */
if (tb->marklength != 0) GfxFillRect(delta + tb->markxoffs, 0, delta + tb->markxoffs + tb->marklength - 1, bottom - top, PC_GREY);
DrawString(delta, tb->pixels, 0, tb->buf, TC_YELLOW);
bool focussed = w->IsWidgetGloballyFocused(wid) || IsOSKOpenedFor(w, wid);
if (focussed && tb->caret) {
@ -774,6 +777,105 @@ void QueryString::DrawEditBox(const Window *w, int wid) const
_cur_dpi = old_dpi;
}
/**
* Get the current caret position.
* @param w Window the edit box is in.
* @param wid Widget index.
* @return Top-left location of the caret, relative to the window.
*/
Point QueryString::GetCaretPosition(const Window *w, int wid) const
{
const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
assert((wi->type & WWT_MASK) == WWT_EDITBOX);
bool rtl = _current_text_dir == TD_RTL;
Dimension sprite_size = GetSpriteSize(rtl ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
int clearbtn_width = sprite_size.width + WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT;
int left = wi->pos_x + (rtl ? clearbtn_width : 0);
int right = wi->pos_x + (rtl ? wi->current_x : wi->current_x - clearbtn_width) - 1;
/* Clamp caret position to be inside out current width. */
const Textbuf *tb = &this->text;
int delta = min(0, (right - left) - tb->pixels - 10);
if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
Point pt = {left + WD_FRAMERECT_LEFT + tb->caretxoffs + delta, wi->pos_y + WD_FRAMERECT_TOP};
return pt;
}
/**
* Get the bounding rectangle for a range of the query string.
* @param w Window the edit box is in.
* @param wid Widget index.
* @param from Start of the string range.
* @param to End of the string range.
* @return Rectangle encompassing the string range, relative to the window.
*/
Rect QueryString::GetBoundingRect(const Window *w, int wid, const char *from, const char *to) const
{
const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
assert((wi->type & WWT_MASK) == WWT_EDITBOX);
bool rtl = _current_text_dir == TD_RTL;
Dimension sprite_size = GetSpriteSize(rtl ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
int clearbtn_width = sprite_size.width + WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT;
int left = wi->pos_x + (rtl ? clearbtn_width : 0);
int right = wi->pos_x + (rtl ? wi->current_x : wi->current_x - clearbtn_width) - 1;
int top = wi->pos_y + WD_FRAMERECT_TOP;
int bottom = wi->pos_y + wi->current_y - 1 - WD_FRAMERECT_BOTTOM;
/* Clamp caret position to be inside our current width. */
const Textbuf *tb = &this->text;
int delta = min(0, (right - left) - tb->pixels - 10);
if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
/* Get location of first and last character. */
Point p1 = GetCharPosInString(tb->buf, from, FS_NORMAL);
Point p2 = from != to ? GetCharPosInString(tb->buf, to, FS_NORMAL) : p1;
Rect r = { Clamp(left + p1.x + delta + WD_FRAMERECT_LEFT, left, right), top, Clamp(left + p2.x + delta + WD_FRAMERECT_LEFT, left, right - WD_FRAMERECT_RIGHT), bottom };
return r;
}
/**
* Get the character that is rendered at a position.
* @param w Window the edit box is in.
* @param wid Widget index.
* @param pt Position to test.
* @return Pointer to the character at the position or NULL if no character is at the position.
*/
const char *QueryString::GetCharAtPosition(const Window *w, int wid, const Point &pt) const
{
const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
assert((wi->type & WWT_MASK) == WWT_EDITBOX);
bool rtl = _current_text_dir == TD_RTL;
Dimension sprite_size = GetSpriteSize(rtl ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
int clearbtn_width = sprite_size.width + WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT;
int left = wi->pos_x + (rtl ? clearbtn_width : 0);
int right = wi->pos_x + (rtl ? wi->current_x : wi->current_x - clearbtn_width) - 1;
int top = wi->pos_y + WD_FRAMERECT_TOP;
int bottom = wi->pos_y + wi->current_y - 1 - WD_FRAMERECT_BOTTOM;
if (!IsInsideMM(pt.y, top, bottom)) return NULL;
/* Clamp caret position to be inside our current width. */
const Textbuf *tb = &this->text;
int delta = min(0, (right - left) - tb->pixels - 10);
if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
return ::GetCharAtPosition(tb->buf, pt.x - delta - left);
}
void QueryString::ClickEditBox(Window *w, Point pt, int wid, int click_count, bool focus_changed)
{
const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);

View File

@ -108,6 +108,14 @@ typedef unsigned int NSUInteger;
#endif /* __LP64__ */
#endif /* NSInteger */
#ifndef CGFLOAT_DEFINED
#if __LP64__
typedef double CGFloat;
#else
typedef float CGFloat;
#endif /* __LP64__ */
#endif /* CGFLOAT_DEFINED */
/* OS X SDK versions >= 10.5 have a non-const iconv. */
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
# define HAVE_NON_CONST_ICONV

View File

@ -16,6 +16,7 @@
#include "window_func.h"
#include "gfx_func.h"
#include "querystring_gui.h"
#include "video/video_driver.hpp"
#include "widgets/osk_widget.h"
@ -205,6 +206,7 @@ struct OskWindow : public Window {
virtual void OnFocusLost()
{
_video_driver->EditBoxLostFocus();
delete this;
}
};

View File

@ -53,6 +53,41 @@ public:
void DrawEditBox(const Window *w, int wid) const;
void ClickEditBox(Window *w, Point pt, int wid, int click_count, bool focus_changed);
void HandleEditBox(Window *w, int wid);
Point GetCaretPosition(const Window *w, int wid) const;
Rect GetBoundingRect(const Window *w, int wid, const char *from, const char *to) const;
const char *GetCharAtPosition(const Window *w, int wid, const Point &pt) const;
/**
* Get the current text.
* @return Current text.
*/
const char *GetText() const
{
return this->text.buf;
}
/**
* Get the position of the caret in the text buffer.
* @return Pointer to the caret in the text buffer.
*/
const char *GetCaret() const
{
return this->text.buf + this->text.caretpos;
}
/**
* Get the currently marked text.
* @param[out] length Length of the marked text.
* @return Begining of the marked area or NULL if no text is marked.
*/
const char *GetMarkedText(size_t *length) const
{
if (this->text.markend == 0) return NULL;
*length = this->text.markend - this->text.markpos;
return this->text.buf + this->text.markpos;
}
};
void ShowOnScreenKeyboard(Window *parent, int button);

View File

@ -67,7 +67,7 @@ const char *SoundDriver_Cocoa::Start(const char * const *parm)
requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
MxInitialize(requestedDesc.mSampleRate);
MxInitialize((uint)requestedDesc.mSampleRate);
/* Locate the default output audio unit */
desc.componentType = kAudioUnitType_Output;

View File

@ -103,6 +103,7 @@ bool Textbuf::DeleteChar(uint16 keycode)
this->UpdateStringIter();
this->UpdateWidth();
this->UpdateCaretPosition();
this->UpdateMarkedText();
return true;
}
@ -115,6 +116,7 @@ void Textbuf::DeleteAll()
memset(this->buf, 0, this->max_bytes);
this->bytes = this->chars = 1;
this->pixels = this->caretpos = this->caretxoffs = 0;
this->markpos = this->markend = this->markxoffs = this->marklength = 0;
this->UpdateStringIter();
}
@ -138,11 +140,80 @@ bool Textbuf::InsertChar(WChar key)
this->UpdateStringIter();
this->UpdateWidth();
this->UpdateCaretPosition();
this->UpdateMarkedText();
return true;
}
return false;
}
/**
* Insert a string into the text buffer. If maxwidth of the Textbuf is zero,
* we don't care about the visual-length but only about the physical
* length of the string.
* @param str String to insert.
* @param marked Replace the currently marked text with the new text.
* @param caret Move the caret to this point in the insertion string.
* @param insert_location Position at which to insert the string.
* @param replacement_end Replace all characters from #insert_location up to this location with the new string.
* @return True on successful change of Textbuf, or false otherwise.
*/
bool Textbuf::InsertString(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
{
uint16 insertpos = (marked && this->marklength != 0) ? this->markpos : this->caretpos;
if (insert_location != NULL) {
insertpos = insert_location - this->buf;
if (insertpos > this->bytes) return false;
if (replacement_end != NULL) {
this->DeleteText(insertpos, replacement_end - this->buf, str == NULL);
}
} else {
if (marked) this->DiscardMarkedText(str == NULL);
}
if (str == NULL) return false;
uint16 bytes = 0, chars = 0;
WChar c;
for (const char *ptr = str; (c = Utf8Consume(&ptr)) != '\0';) {
if (!IsValidChar(c, this->afilter)) break;
byte len = Utf8CharLen(c);
if (this->bytes + bytes + len > this->max_bytes) break;
if (this->chars + chars + 1 > this->max_chars) break;
bytes += len;
chars++;
/* Move caret if needed. */
if (ptr == caret) this->caretpos = insertpos + bytes;
}
if (bytes == 0) return false;
if (marked) {
this->markpos = insertpos;
this->markend = insertpos + bytes;
}
memmove(this->buf + insertpos + bytes, this->buf + insertpos, this->bytes - insertpos);
memcpy(this->buf + insertpos, str, bytes);
this->bytes += bytes;
this->chars += chars;
if (!marked && caret == NULL) this->caretpos += bytes;
assert(this->bytes <= this->max_bytes);
assert(this->chars <= this->max_chars);
this->buf[this->bytes - 1] = '\0'; // terminating zero
this->UpdateStringIter();
this->UpdateWidth();
this->UpdateCaretPosition();
this->UpdateMarkedText();
return true;
}
/**
* Insert a chunk of text from the clipboard onto the textbuffer. Get TEXT clipboard
* and append this up to the maximum length (either absolute or screenlength). If maxlength
@ -155,36 +226,55 @@ bool Textbuf::InsertClipboard()
if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
uint16 bytes = 0, chars = 0;
WChar c;
for (const char *ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
if (!IsValidChar(c, this->afilter)) break;
return this->InsertString(utf8_buf, false);
}
byte len = Utf8CharLen(c);
if (this->bytes + bytes + len > this->max_bytes) break;
if (this->chars + chars + 1 > this->max_chars) break;
bytes += len;
chars++;
/**
* Delete a part of the text.
* @param from Start of the text to delete.
* @param to End of the text to delete.
* @param update Set to true if the internal state should be updated.
*/
void Textbuf::DeleteText(uint16 from, uint16 to, bool update)
{
uint c = 0;
const char *s = this->buf + from;
while (s < this->buf + to) {
Utf8Consume(&s);
c++;
}
if (bytes == 0) return false;
/* Strip marked characters from buffer. */
memmove(this->buf + from, this->buf + to, this->bytes - to);
this->bytes -= to - from;
this->chars -= c;
memmove(this->buf + this->caretpos + bytes, this->buf + this->caretpos, this->bytes - this->caretpos);
memcpy(this->buf + this->caretpos, utf8_buf, bytes);
/* Fixup caret if needed. */
if (this->caretpos > from) {
if (this->caretpos <= to) {
this->caretpos = from;
} else {
this->caretpos -= to - from;
}
}
this->bytes += bytes;
this->chars += chars;
this->caretpos += bytes;
assert(this->bytes <= this->max_bytes);
assert(this->chars <= this->max_chars);
this->buf[this->bytes - 1] = '\0'; // terminating zero
if (update) {
this->UpdateStringIter();
this->UpdateCaretPosition();
this->UpdateMarkedText();
}
}
this->UpdateStringIter();
this->UpdateWidth();
this->UpdateCaretPosition();
/**
* Discard any marked text.
* @param update Set to true if the internal state should be updated.
*/
void Textbuf::DiscardMarkedText(bool update)
{
if (this->markend == 0) return;
return true;
this->DeleteText(this->markpos, this->markend, update);
this->markpos = this->markend = this->markxoffs = this->marklength = 0;
}
/** Update the character iter after the text has changed. */
@ -207,6 +297,17 @@ void Textbuf::UpdateCaretPosition()
this->caretxoffs = this->chars > 1 ? GetCharPosInString(this->buf, this->buf + this->caretpos, FS_NORMAL).x : 0;
}
/** Update pixel positions of the marked text area. */
void Textbuf::UpdateMarkedText()
{
if (this->markend != 0) {
this->markxoffs = GetCharPosInString(this->buf, this->buf + this->markpos, FS_NORMAL).x;
this->marklength = GetCharPosInString(this->buf, this->buf + this->markend, FS_NORMAL).x - this->markxoffs;
} else {
this->markxoffs = this->marklength = 0;
}
}
/**
* Handle text navigation with arrow keys left/right.
* This defines where the caret will blink and the next character interaction will occur
@ -342,6 +443,7 @@ void Textbuf::UpdateSize()
this->caretpos = this->bytes - 1;
this->UpdateStringIter();
this->UpdateWidth();
this->UpdateMarkedText();
this->UpdateCaretPosition();
}

View File

@ -40,6 +40,10 @@ struct Textbuf {
bool caret; ///< is the caret ("_") visible or not
uint16 caretpos; ///< the current position of the caret in the buffer, in bytes
uint16 caretxoffs; ///< the current position of the caret in pixels
uint16 markpos; ///< the start position of the marked area in the buffer, in bytes
uint16 markend; ///< the end position of the marked area in the buffer, in bytes
uint16 markxoffs; ///< the start position of the marked area in pixels
uint16 marklength; ///< the length of the marked area in pixels
explicit Textbuf(uint16 max_bytes, uint16 max_chars = UINT16_MAX);
~Textbuf();
@ -52,6 +56,7 @@ struct Textbuf {
bool InsertClipboard();
bool InsertChar(uint32 key);
bool InsertString(const char *str, bool marked, const char *caret = NULL, const char *insert_location = NULL, const char *replacement_end = NULL);
bool DeleteChar(uint16 keycode);
bool MovePos(uint16 keycode);
@ -61,14 +66,19 @@ struct Textbuf {
bool HandleCaret();
void UpdateSize();
void DiscardMarkedText(bool update = true);
private:
StringIterator *char_iter;
bool CanDelChar(bool backspace);
void DeleteText(uint16 from, uint16 to, bool update);
void UpdateStringIter();
void UpdateWidth();
void UpdateCaretPosition();
void UpdateMarkedText();
};
#endif /* TEXTBUF_TYPE_H */

View File

@ -50,6 +50,11 @@ public:
*/
/* virtual */ bool AfterBlitterChange();
/**
* An edit box lost the input focus. Abort character compositing if necessary.
*/
/* virtual */ void EditBoxLostFocus();
/** Return driver name
* @return driver name
*/
@ -227,7 +232,17 @@ uint QZ_ListModes(OTTD_Point *modes, uint max_modes, CGDirectDisplayID display_i
@end
/** Subclass of NSView to fix Quartz rendering and mouse awareness */
@interface OTTD_CocoaView : NSView {
@interface OTTD_CocoaView : NSView
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
<NSTextInputClient, NSTextInput>
# else
<NSTextInputClient>
# endif /* MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 */
#else
<NSTextInput>
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 */
{
CocoaSubdriver *driver;
NSTrackingRectTag trackingtag;
}

View File

@ -34,6 +34,8 @@
#include "../../blitter/factory.hpp"
#include "../../fileio_func.h"
#include "../../gfx_func.h"
#include "../../window_func.h"
#include "../../window_gui.h"
#import <sys/param.h> /* for MAXPATHLEN */
@ -57,7 +59,7 @@ static bool _cocoa_video_dialog = false;
CocoaSubdriver *_cocoa_subdriver = NULL;
static const NSString *OTTDMainLaunchGameEngine = @"ottdmain_launch_game_engine";
static NSString *OTTDMainLaunchGameEngine = @"ottdmain_launch_game_engine";
/**
@ -394,21 +396,12 @@ static CocoaSubdriver *QZ_CreateSubdriver(int width, int height, int bpp, bool f
/* OSX 10.7 allows to toggle fullscreen mode differently */
if (MacOSVersionIsAtLeast(10, 7, 0)) {
ret = QZ_CreateWindowSubdriver(width, height, bpp);
}
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9)
else {
if (ret != NULL && fullscreen) ret->ToggleFullscreen();
} else {
ret = fullscreen ? QZ_CreateFullscreenSubdriver(width, height, bpp) : QZ_CreateWindowSubdriver(width, height, bpp);
}
#endif /* (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9) */
if (ret != NULL) {
/* We cannot set any fullscreen mode on OSX 10.7 when not compiled against SDK 10.7 */
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (fullscreen) { ret->ToggleFullscreen(); }
#endif
return ret;
}
if (ret != NULL) return ret;
if (!fallback) return NULL;
/* Try again in 640x480 windowed */
@ -572,6 +565,22 @@ bool VideoDriver_Cocoa::AfterBlitterChange()
return this->ChangeResolution(_screen.width, _screen.height);
}
/**
* An edit box lost the input focus. Abort character compositing if necessary.
*/
void VideoDriver_Cocoa::EditBoxLostFocus()
{
if (_cocoa_subdriver != NULL) {
if ([ _cocoa_subdriver->cocoaview respondsToSelector:@selector(inputContext) ] && [ [ _cocoa_subdriver->cocoaview performSelector:@selector(inputContext) ] respondsToSelector:@selector(discardMarkedText) ]) {
[ [ _cocoa_subdriver->cocoaview performSelector:@selector(inputContext) ] performSelector:@selector(discardMarkedText) ];
} else {
[ [ NSInputManager currentInputManager ] markedTextAbandoned:_cocoa_subdriver->cocoaview ];
}
}
/* Clear any marked string from the current edit box. */
HandleTextInput(NULL, true);
}
/**
* Catch asserts prior to initialization of the videodriver.
*
@ -757,6 +766,43 @@ void cocoaReleaseAutoreleasePool()
/**
* Count the number of UTF-16 code points in a range of an UTF-8 string.
* @param from Start of the range.
* @param to End of the range.
* @return Number of UTF-16 code points in the range.
*/
static NSUInteger CountUtf16Units(const char *from, const char *to)
{
NSUInteger i = 0;
while (from < to) {
WChar c;
size_t len = Utf8Decode(&c, from);
i += len < 4 ? 1 : 2; // Watch for surrogate pairs.
from += len;
}
return i;
}
/**
* Advance an UTF-8 string by a number of equivalent UTF-16 code points.
* @param str UTF-8 string.
* @param count Number of UTF-16 code points to advance the string by.
* @return Advanced string pointer.
*/
static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count)
{
for (NSUInteger i = 0; i < count && *str != '\0'; ) {
WChar c;
size_t len = Utf8Decode(&c, str);
i += len < 4 ? 1 : 2; // Watch for surrogates.
str += len;
}
return str;
}
@implementation OTTD_CocoaView
/**
@ -852,6 +898,200 @@ void cocoaReleaseAutoreleasePool()
if (_cocoa_subdriver != NULL) UndrawMouseCursor();
_cursor.in_window = false;
}
/** Insert the given text at the given range. */
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
{
if (!EditBoxInGlobalFocus()) return;
NSString *s = [ aString isKindOfClass:[ NSAttributedString class ] ] ? [ aString string ] : (NSString *)aString;
const char *insert_point = NULL;
const char *replace_range = NULL;
if (replacementRange.location != NSNotFound) {
/* Calculate the part to be replaced. */
insert_point = Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), replacementRange.location);
replace_range = Utf8AdvanceByUtf16Units(insert_point, replacementRange.length);
}
HandleTextInput(NULL, true);
HandleTextInput([ s UTF8String ], false, NULL, insert_point, replace_range);
}
/** Insert the given text at the caret. */
- (void)insertText:(id)aString
{
[ self insertText:aString replacementRange:NSMakeRange(NSNotFound, 0) ];
}
/** Set a new marked text and reposition the caret. */
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange replacementRange:(NSRange)replacementRange
{
if (!EditBoxInGlobalFocus()) return;
NSString *s = [ aString isKindOfClass:[ NSAttributedString class ] ] ? [ aString string ] : (NSString *)aString;
const char *utf8 = [ s UTF8String ];
if (utf8 != NULL) {
const char *insert_point = NULL;
const char *replace_range = NULL;
if (replacementRange.location != NSNotFound) {
/* Calculate the part to be replaced. */
NSRange marked = [ self markedRange ];
insert_point = Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), replacementRange.location + (marked.location != NSNotFound ? marked.location : 0u));
replace_range = Utf8AdvanceByUtf16Units(insert_point, replacementRange.length);
}
/* Convert caret index into a pointer in the UTF-8 string. */
const char *selection = Utf8AdvanceByUtf16Units(utf8, selRange.location);
HandleTextInput(utf8, true, selection, insert_point, replace_range);
}
}
/** Set a new marked text and reposition the caret. */
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange
{
[ self setMarkedText:aString selectedRange:selRange replacementRange:NSMakeRange(NSNotFound, 0) ];
}
/** Unmark the current marked text. */
- (void)unmarkText
{
HandleTextInput(NULL, true);
}
/** Get the caret position. */
- (NSRange)selectedRange
{
if (!EditBoxInGlobalFocus()) return NSMakeRange(NSNotFound, 0);
NSUInteger start = CountUtf16Units(_focused_window->GetFocusedText(), _focused_window->GetCaret());
return NSMakeRange(start, 0);
}
/** Get the currently marked range. */
- (NSRange)markedRange
{
if (!EditBoxInGlobalFocus()) return NSMakeRange(NSNotFound, 0);
size_t mark_len;
const char *mark = _focused_window->GetMarkedText(&mark_len);
if (mark != NULL) {
NSUInteger start = CountUtf16Units(_focused_window->GetFocusedText(), mark);
NSUInteger len = CountUtf16Units(mark, mark + mark_len);
return NSMakeRange(start, len);
}
return NSMakeRange(NSNotFound, 0);
}
/** Is any text marked? */
- (BOOL)hasMarkedText
{
if (!EditBoxInGlobalFocus()) return NO;
size_t len;
return _focused_window->GetMarkedText(&len) != NULL;
}
/** Get a string corresponding to the given range. */
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
{
if (!EditBoxInGlobalFocus()) return nil;
NSString *s = [ NSString stringWithUTF8String:_focused_window->GetFocusedText() ];
NSRange valid_range = NSIntersectionRange(NSMakeRange(0, [ s length ]), theRange);
if (actualRange != NULL) *actualRange = valid_range;
if (valid_range.length == 0) return nil;
return [ [ [ NSAttributedString alloc ] initWithString:[ s substringWithRange:valid_range ] ] autorelease ];
}
/** Get a string corresponding to the given range. */
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
{
return [ self attributedSubstringForProposedRange:theRange actualRange:NULL ];
}
/** Get the current edit box string. */
- (NSAttributedString *)attributedString
{
if (!EditBoxInGlobalFocus()) return [ [ [ NSAttributedString alloc ] initWithString:@"" ] autorelease ];
return [ [ [ NSAttributedString alloc ] initWithString:[ NSString stringWithUTF8String:_focused_window->GetFocusedText() ] ] autorelease ];
}
/** Get the character that is rendered at the given point. */
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
{
if (!EditBoxInGlobalFocus()) return NSNotFound;
NSPoint view_pt = [ self convertPoint:[ [ self window ] convertScreenToBase:thePoint ] fromView:nil ];
Point pt = { (int)view_pt.x, (int)[ self frame ].size.height - (int)view_pt.y };
const char *ch = _focused_window->GetTextCharacterAtPosition(pt);
if (ch == NULL) return NSNotFound;
return CountUtf16Units(_focused_window->GetFocusedText(), ch);
}
/** Get the bounding rect for the given range. */
- (NSRect)firstRectForCharacterRange:(NSRange)aRange
{
if (!EditBoxInGlobalFocus()) return NSMakeRect(0, 0, 0, 0);
/* Convert range to UTF-8 string pointers. */
const char *start = Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), aRange.location);
const char *end = aRange.length != 0 ? Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), aRange.location + aRange.length) : start;
/* Get the bounding rect for the text range.*/
Rect r = _focused_window->GetTextBoundingRect(start, end);
NSRect view_rect = NSMakeRect(_focused_window->left + r.left, [ self frame ].size.height - _focused_window->top - r.bottom, r.right - r.left, r.bottom - r.top);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if ([ [ self window ] respondsToSelector:@selector(convertRectToScreen:) ]) {
return [ [ self window ] convertRectToScreen:[ self convertRect:view_rect toView:nil ] ];
}
#endif
NSRect window_rect = [ self convertRect:view_rect toView:nil ];
NSPoint origin = [ [ self window ] convertBaseToScreen:window_rect.origin ];
return NSMakeRect(origin.x, origin.y, window_rect.size.width, window_rect.size.height);
}
/** Get the bounding rect for the given range. */
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
return [ self firstRectForCharacterRange:aRange ];
}
/** Get all string attributes that we can process for marked text. */
- (NSArray*)validAttributesForMarkedText
{
return [ NSArray array ];
}
/** Identifier for this text input instance. */
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
- (long)conversationIdentifier
#else
- (NSInteger)conversationIdentifier
#endif
{
return 0;
}
/** Invoke the selector if we implement it. */
- (void)doCommandBySelector:(SEL)aSelector
{
if ([ self respondsToSelector:aSelector ]) [ self performSelector:aSelector ];
}
@end

View File

@ -60,11 +60,28 @@ enum RightMouseButtonEmulationState {
static unsigned int _current_mods;
static bool _tab_is_down;
static bool _emulating_right_button;
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
static float _current_magnification;
#endif
#ifdef _DEBUG
static uint32 _tEvent;
#endif
/* Support for touch gestures is only available starting with the
* 10.6 SDK, even if it says that support starts in fact with 10.5.2.
* Replicate the needed stuff for older SDKs. */
#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5
static const NSUInteger NSEventTypeMagnify = 30;
static const NSUInteger NSEventTypeEndGesture = 20;
@interface NSEvent ()
/* This message is valid for events of type NSEventTypeMagnify, on 10.5.2 or later */
- (CGFloat)magnification WEAK_IMPORT_ATTRIBUTE;
@end
#endif
static uint32 GetTick()
{
struct timeval tim;
@ -255,8 +272,10 @@ static uint32 QZ_MapKey(unsigned short sym)
return key;
}
static void QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL down)
static bool QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL down)
{
bool interpret_keys = true;
switch (keycode) {
case QZ_UP: SB(_dirkeys, 1, 1, down); break;
case QZ_DOWN: SB(_dirkeys, 3, 1, down); break;
@ -275,6 +294,21 @@ static void QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL dow
if (down) {
uint32 pressed_key = QZ_MapKey(keycode);
static bool console = false;
if (pressed_key == WKC_BACKQUOTE && unicode == 0) {
if (!console) {
/* Backquote is a dead key, require a double press for hotkey behaviour (i.e. console). */
console = true;
return true;
} else {
/* Second backquote, don't interpret as text input. */
interpret_keys = false;
}
}
console = false;
/* Don't handle normal characters if an edit box has the focus. */
if (!EditBoxInGlobalFocus() || ((pressed_key & ~WKC_SPECIAL_KEYS) <= WKC_TAB) || IsInsideMM(pressed_key & ~WKC_SPECIAL_KEYS, WKC_F1, WKC_PAUSE + 1)) {
HandleKeypress(pressed_key, unicode);
@ -283,6 +317,8 @@ static void QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL dow
} else {
DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode);
}
return interpret_keys;
}
static void QZ_DoUnsidedModifiers(unsigned int newMods)
@ -387,7 +423,6 @@ static bool QZ_PollEvent()
NSString *chars;
NSPoint pt;
NSText *fieldEditor;
switch ([ event type ]) {
case NSMouseMoved:
case NSOtherMouseDragged:
@ -504,17 +539,19 @@ static bool QZ_PollEvent()
break;
}
fieldEditor = [[ event window ] fieldEditor:YES forObject:nil ];
[ fieldEditor setString:@"" ];
[ fieldEditor interpretKeyEvents: [ NSArray arrayWithObject:event ] ];
chars = [ event characters ];
if ([ chars length ] == 0) {
QZ_KeyEvent([ event keyCode ], 0, YES);
if (EditBoxInGlobalFocus()) {
if (QZ_KeyEvent([ event keyCode ], 0, YES)) {
[ _cocoa_subdriver->cocoaview interpretKeyEvents:[ NSArray arrayWithObject:event ] ];
}
} else {
QZ_KeyEvent([ event keyCode ], [ chars characterAtIndex:0 ], YES);
for (uint i = 1; i < [ chars length ]; i++) {
QZ_KeyEvent(0, [ chars characterAtIndex:i ], YES);
chars = [ event characters ];
if ([ chars length ] == 0) {
QZ_KeyEvent([ event keyCode ], 0, YES);
} else {
QZ_KeyEvent([ event keyCode ], [ chars characterAtIndex:0 ], YES);
for (uint i = 1; i < [ chars length ]; i++) {
QZ_KeyEvent(0, [ chars characterAtIndex:i ], YES);
}
}
}
break;
@ -547,6 +584,29 @@ static bool QZ_PollEvent()
_cursor.v_wheel -= (int)([ event deltaY ] * 5 * _settings_client.gui.scrollwheel_multiplier);
break;
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
case NSEventTypeMagnify:
/* Pinch open or close gesture. */
_current_magnification += [ event magnification ] * 5.0f;
while (_current_magnification >= 1.0f) {
_current_magnification -= 1.0f;
_cursor.wheel++;
HandleMouseEvents();
}
while (_current_magnification <= -1.0f) {
_current_magnification += 1.0f;
_cursor.wheel--;
HandleMouseEvents();
}
break;
case NSEventTypeEndGesture:
/* Gesture ended. */
_current_magnification = 0.0f;
break;
#endif
case NSCursorUpdate:
case NSMouseEntered:
case NSMouseExited:

View File

@ -241,7 +241,7 @@ class FullscreenSubdriver: public CocoaSubdriver {
* disable until a replacement can be found. */
if (MacOSVersionIsAtLeast(10, 7, 0)) {
this->window_buffer = NULL;
this->window_pitch = NULL;
this->window_pitch = 0;
} else {
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
this->window_buffer = CGDisplayBaseAddress(this->display_id);

View File

@ -34,6 +34,13 @@
#include "../../core/math_func.hpp"
#include "../../gfx_func.h"
/* On some old versions of MAC OS this may not be defined.
* Those versions generally only produce code for PPC. So it should be safe to
* set this to 0. */
#ifndef kCGBitmapByteOrder32Host
#define kCGBitmapByteOrder32Host 0
#endif
/**
* Important notice regarding all modifications!!!!!!!
* There are certain limitations because the file is objective C++.
@ -144,7 +151,7 @@ static CGColorSpaceRef QZ_GetCorrectColorSpace()
/* Calculate total area we are blitting */
uint32 blitArea = 0;
for (int n = 0; n < dirtyRectCount; n++) {
blitArea += dirtyRects[n].size.width * dirtyRects[n].size.height;
blitArea += (uint32)(dirtyRects[n].size.width * dirtyRects[n].size.height);
}
/*
@ -240,12 +247,12 @@ void WindowQuartzSubdriver::GetDeviceInfo()
*/
bool WindowQuartzSubdriver::ToggleFullscreen()
{
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
[this->window toggleFullScreen:this->window];
return true;
#else
if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) {
[ this->window performSelector:@selector(toggleFullScreen:) withObject:this->window ];
return true;
}
return false;
#endif
}
bool WindowQuartzSubdriver::SetVideoMode(int width, int height, int bpp)
@ -280,15 +287,17 @@ bool WindowQuartzSubdriver::SetVideoMode(int width, int height, int bpp)
return false;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
/* Add built in full-screen support when available (OS X 10.7 and higher)
* This code actually compiles for 10.5 and later, but only makes sense in conjunction
* with the quartz fullscreen support as found only in 10.7 and later
*/
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
if ([this->window respondsToSelector:@selector(toggleFullScreen:)]) {
/* Constants needed to build on pre-10.7 systems. Source: NSWindow documentation. */
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
/* Constants needed to build on pre-10.7 SDKs. Source: NSWindow documentation. */
const int NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7;
const int NSWindowFullScreenButton = 7;
#endif
NSWindowCollectionBehavior behavior = [ this->window collectionBehavior ];
behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
@ -326,10 +335,10 @@ bool WindowQuartzSubdriver::SetVideoMode(int width, int height, int bpp)
[ this->window setContentSize:contentRect.size ];
/* Ensure frame height - title bar height >= view height */
contentRect.size.height = Clamp(height, 0, [ this->window frame ].size.height - 22 /* 22 is the height of title bar of window*/);
contentRect.size.height = Clamp(height, 0, (int)[ this->window frame ].size.height - 22 /* 22 is the height of title bar of window*/);
if (this->cocoaview != nil) {
height = contentRect.size.height;
height = (int)contentRect.size.height;
[ this->cocoaview setFrameSize:contentRect.size ];
}
}
@ -514,7 +523,7 @@ NSPoint WindowQuartzSubdriver::GetMouseLocation(NSEvent *event)
{
NSPoint pt;
if (event.window == nil) {
if ( [ event window ] == nil) {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if ([ this->cocoaview respondsToSelector:@selector(convertRectFromScreen:) ]) {
pt = [ this->cocoaview convertPoint:[ [ this->cocoaview window ] convertRectFromScreen:NSMakeRect([ event locationInWindow ].x, [ event locationInWindow ].y, 0, 0) ].origin fromView:nil ];
@ -560,8 +569,8 @@ bool WindowQuartzSubdriver::WindowResized()
NSRect newframe = [ this->cocoaview frame ];
this->window_width = newframe.size.width;
this->window_height = newframe.size.height;
this->window_width = (int)newframe.size.width;
this->window_height = (int)newframe.size.height;
/* Create Core Graphics Context */
free(this->window_buffer);

View File

@ -73,6 +73,11 @@ public:
{
return true;
}
/**
* An edit box lost the input focus. Abort character compositing if necessary.
*/
virtual void EditBoxLostFocus() {}
};
/** Base of the factory for the video drivers. */

View File

@ -21,9 +21,11 @@
#include "../texteff.hpp"
#include "../thread/thread.h"
#include "../progress.h"
#include "../window_gui.h"
#include "../window_func.h"
#include "win32_v.h"
#include <windows.h>
#include <imm.h>
/* Missing define in MinGW headers. */
#ifndef MAPVK_VK_TO_CHAR
@ -50,6 +52,9 @@ bool _window_maximize;
uint _display_hz;
uint _fullscreen_bpp;
static Dimension _bck_resolution;
#if !defined(WINCE) || _WIN32_WCE >= 0x400
DWORD _imm_props;
#endif
/** Whether the drawing is/may be done in a separate thread. */
static bool _draw_threaded;
@ -498,6 +503,152 @@ static LRESULT HandleCharMsg(uint keycode, WChar charcode)
return 0;
}
#if !defined(WINCE) || _WIN32_WCE >= 0x400
/** Should we draw the composition string ourself, i.e is this a normal IME? */
static bool DrawIMECompositionString()
{
return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
}
/** Set position of the composition window to the caret position. */
static void SetCompositionPos(HWND hwnd)
{
HIMC hIMC = ImmGetContext(hwnd);
if (hIMC != NULL) {
COMPOSITIONFORM cf;
cf.dwStyle = CFS_POINT;
if (EditBoxInGlobalFocus()) {
/* Get caret position. */
Point pt = _focused_window->GetCaretPosition();
cf.ptCurrentPos.x = _focused_window->left + pt.x;
cf.ptCurrentPos.y = _focused_window->top + pt.y;
} else {
cf.ptCurrentPos.x = 0;
cf.ptCurrentPos.y = 0;
}
ImmSetCompositionWindow(hIMC, &cf);
}
ImmReleaseContext(hwnd, hIMC);
}
/** Set the position of the candidate window. */
static void SetCandidatePos(HWND hwnd)
{
HIMC hIMC = ImmGetContext(hwnd);
if (hIMC != NULL) {
CANDIDATEFORM cf;
cf.dwIndex = 0;
cf.dwStyle = CFS_EXCLUDE;
if (EditBoxInGlobalFocus()) {
Point pt = _focused_window->GetCaretPosition();
cf.ptCurrentPos.x = _focused_window->left + pt.x;
cf.ptCurrentPos.y = _focused_window->top + pt.y;
if (_focused_window->window_class == WC_CONSOLE) {
cf.rcArea.left = _focused_window->left;
cf.rcArea.top = _focused_window->top;
cf.rcArea.right = _focused_window->left + _focused_window->width;
cf.rcArea.bottom = _focused_window->top + _focused_window->height;
} else {
cf.rcArea.left = _focused_window->left + _focused_window->nested_focus->pos_x;
cf.rcArea.top = _focused_window->top + _focused_window->nested_focus->pos_y;
cf.rcArea.right = cf.rcArea.left + _focused_window->nested_focus->current_x;
cf.rcArea.bottom = cf.rcArea.top + _focused_window->nested_focus->current_y;
}
} else {
cf.ptCurrentPos.x = 0;
cf.ptCurrentPos.y = 0;
SetRectEmpty(&cf.rcArea);
}
ImmSetCandidateWindow(hIMC, &cf);
}
ImmReleaseContext(hwnd, hIMC);
}
/** Handle WM_IME_COMPOSITION messages. */
static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
HIMC hIMC = ImmGetContext(hwnd);
if (hIMC != NULL) {
if (lParam & GCS_RESULTSTR) {
/* Read result string from the IME. */
LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); // Length is always in bytes, even in UNICODE build.
TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR));
len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str, len);
str[len / sizeof(TCHAR)] = '\0';
/* Transmit text to windowing system. */
if (len > 0) {
HandleTextInput(NULL, true); // Clear marked string.
HandleTextInput(FS2OTTD(str));
}
SetCompositionPos(hwnd);
/* Don't pass the result string on to the default window proc. */
lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
}
if ((lParam & GCS_COMPSTR) && DrawIMECompositionString()) {
/* Read composition string from the IME. */
LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0); // Length is always in bytes, even in UNICODE build.
TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR));
len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str, len);
str[len / sizeof(TCHAR)] = '\0';
if (len > 0) {
static char utf8_buf[1024];
convert_from_fs(str, utf8_buf, lengthof(utf8_buf));
/* Convert caret position from bytes in the input string to a position in the UTF-8 encoded string. */
LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0);
const char *caret = utf8_buf;
for (const TCHAR *c = str; *c != '\0' && *caret != '\0' && caret_bytes > 0; c++, caret_bytes--) {
/* Skip DBCS lead bytes or leading surrogates. */
#ifdef UNICODE
if (Utf16IsLeadSurrogate(*c)) {
#else
if (IsDBCSLeadByte(*c)) {
#endif
c++;
caret_bytes--;
}
Utf8Consume(&caret);
}
HandleTextInput(utf8_buf, true, caret);
} else {
HandleTextInput(NULL, true);
}
lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
}
}
ImmReleaseContext(hwnd, hIMC);
return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
}
/** Clear the current composition string. */
static void CancelIMEComposition(HWND hwnd)
{
HIMC hIMC = ImmGetContext(hwnd);
if (hIMC != NULL) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
ImmReleaseContext(hwnd, hIMC);
/* Clear any marked string from the current edit box. */
HandleTextInput(NULL, true);
}
#else
static bool DrawIMECompositionString() { return false; }
static void SetCompositionPos(HWND hwnd) {}
static void SetCandidatePos(HWND hwnd) {}
static void CancelIMEComposition(HWND hwnd) {}
#endif /* !defined(WINCE) || _WIN32_WCE >= 0x400 */
static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static uint32 keycode = 0;
@ -507,6 +658,10 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
switch (msg) {
case WM_CREATE:
SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
SetCompositionPos(hwnd);
#if !defined(WINCE) || _WIN32_WCE >= 0x400
_imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
#endif
break;
case WM_ENTERSIZEMOVE:
@ -633,6 +788,33 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
}
#if !defined(WINCE) || _WIN32_WCE >= 0x400
case WM_INPUTLANGCHANGE:
_imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
break;
case WM_IME_SETCONTEXT:
/* Don't show the composition window if we draw the string ourself. */
if (DrawIMECompositionString()) lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
break;
case WM_IME_STARTCOMPOSITION:
SetCompositionPos(hwnd);
if (DrawIMECompositionString()) return 0;
break;
case WM_IME_COMPOSITION:
return HandleIMEComposition(hwnd, wParam, lParam);
case WM_IME_ENDCOMPOSITION:
/* Clear any pending composition string. */
HandleTextInput(NULL, true);
if (DrawIMECompositionString()) return 0;
break;
case WM_IME_NOTIFY:
if (wParam == IMN_OPENCANDIDATE) SetCandidatePos(hwnd);
break;
#if !defined(UNICODE)
case WM_IME_CHAR:
if (GB(wParam, 8, 8) != 0) {
@ -817,6 +999,7 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
case WM_SETFOCUS:
_wnd.has_focus = true;
SetCompositionPos(hwnd);
break;
case WM_KILLFOCUS:
@ -1172,3 +1355,10 @@ bool VideoDriver_Win32::AfterBlitterChange()
{
return AllocateDibSection(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen);
}
void VideoDriver_Win32::EditBoxLostFocus()
{
CancelIMEComposition(_wnd.main_wnd);
SetCompositionPos(_wnd.main_wnd);
SetCandidatePos(_wnd.main_wnd);
}

View File

@ -33,6 +33,8 @@ public:
/* virtual */ bool ClaimMousePointer();
/* virtual */ void EditBoxLostFocus();
/* virtual */ const char *GetName() const { return "win32"; }
bool MakeWindow(bool full_screen);

View File

@ -33,6 +33,7 @@
#include "statusbar_gui.h"
#include "error.h"
#include "game/game.hpp"
#include "video/video_driver.hpp"
/** Values for _settings_client.gui.auto_scrolling */
enum ViewportAutoscrolling {
@ -249,6 +250,89 @@ QueryString *Window::GetQueryString(uint widnum)
return query != this->querystrings.End() ? query->second : NULL;
}
/**
* Get the current input text if an edit box has the focus.
* @return The currently focused input text or NULL if no input focused.
*/
/* virtual */ const char *Window::GetFocusedText() const
{
if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
return this->GetQueryString(this->nested_focus->index)->GetText();
}
return NULL;
}
/**
* Get the string at the caret if an edit box has the focus.
* @return The text at the caret or NULL if no edit box is focused.
*/
/* virtual */ const char *Window::GetCaret() const
{
if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
return this->GetQueryString(this->nested_focus->index)->GetCaret();
}
return NULL;
}
/**
* Get the range of the currently marked input text.
* @param[out] length Length of the marked text.
* @return Pointer to the start of the marked text or NULL if no text is marked.
*/
/* virtual */ const char *Window::GetMarkedText(size_t *length) const
{
if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
return this->GetQueryString(this->nested_focus->index)->GetMarkedText(length);
}
return NULL;
}
/**
* Get the current caret position if an edit box has the focus.
* @return Top-left location of the caret, relative to the window.
*/
/* virtual */ Point Window::GetCaretPosition() const
{
if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
return this->GetQueryString(this->nested_focus->index)->GetCaretPosition(this, this->nested_focus->index);
}
Point pt = {0, 0};
return pt;
}
/**
* Get the bounding rectangle for a text range if an edit box has the focus.
* @param from Start of the string range.
* @param to End of the string range.
* @return Rectangle encompassing the string range, relative to the window.
*/
/* virtual */ Rect Window::GetTextBoundingRect(const char *from, const char *to) const
{
if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
return this->GetQueryString(this->nested_focus->index)->GetBoundingRect(this, this->nested_focus->index, from, to);
}
Rect r = {0, 0, 0, 0};
return r;
}
/**
* Get the character that is rendered at a position by the focused edit box.
* @param pt The position to test.
* @return Pointer to the character at the position or NULL if no character is at the position.
*/
/* virtual */ const char *Window::GetTextCharacterAtPosition(const Point &pt) const
{
if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
return this->GetQueryString(this->nested_focus->index)->GetCharAtPosition(this, this->nested_focus->index, pt);
}
return NULL;
}
/**
* Set the window that has the focus
@ -293,6 +377,8 @@ bool EditBoxInGlobalFocus()
void Window::UnfocusFocusedWidget()
{
if (this->nested_focus != NULL) {
if (this->nested_focus->type == WWT_EDITBOX) _video_driver->EditBoxLostFocus();
/* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
this->nested_focus->SetDirty(this);
this->nested_focus = NULL;
@ -315,11 +401,20 @@ bool Window::SetFocusedWidget(int widget_index)
/* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
this->nested_focus->SetDirty(this);
if (this->nested_focus->type == WWT_EDITBOX) _video_driver->EditBoxLostFocus();
}
this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
return true;
}
/**
* Called when window looses focus
*/
void Window::OnFocusLost()
{
if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) _video_driver->EditBoxLostFocus();
}
/**
* Sets the enabled/disabled status of a list of widgets.
* By default, widgets are enabled.
@ -823,7 +918,10 @@ Window::~Window()
if (_last_scroll_window == this) _last_scroll_window = NULL;
/* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
if (_focused_window == this) _focused_window = NULL;
if (_focused_window == this) {
this->OnFocusLost();
_focused_window = NULL;
}
this->DeleteChildWindows();
@ -2299,9 +2397,14 @@ EventState Window::HandleEditBoxKey(int wid, WChar key, uint16 keycode)
break;
case QueryString::ACTION_CLEAR:
query->text.DeleteAll();
this->SetWidgetDirty(wid);
this->OnEditboxChanged(wid);
if (query->text.bytes <= 1) {
/* If already empty, unfocus instead */
this->UnfocusFocusedWidget();
} else {
query->text.DeleteAll();
this->SetWidgetDirty(wid);
this->OnEditboxChanged(wid);
}
break;
default:
@ -2372,6 +2475,35 @@ void HandleCtrlChanged()
}
}
/**
* Insert a text string at the cursor position into the edit box widget.
* @param wid Edit box widget.
* @param str Text string to insert.
*/
/* virtual */ void Window::InsertTextString(int wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
{
QueryString *query = this->GetQueryString(wid);
if (query == NULL) return;
if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
this->SetWidgetDirty(wid);
this->OnEditboxChanged(wid);
}
}
/**
* Handle text input.
* @param str Text string to input.
* @param marked Is the input a marked composition string from an IME?
* @param caret Move the caret to this point in the insertion string.
*/
void HandleTextInput(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
{
if (!EditBoxInGlobalFocus()) return;
_focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->index, str, marked, caret, insert_location, replacement_end);
}
/**
* Local counter that is incremented each time an mouse input event is detected.
* The counter is used to stop auto-scrolling.

View File

@ -14,6 +14,7 @@
#include "window_type.h"
#include "company_type.h"
#include "core/geometry_type.hpp"
Window *FindWindowById(WindowClass cls, WindowNumber number);
Window *FindWindowByClass(WindowClass cls);
@ -53,5 +54,6 @@ void DeleteWindowById(WindowClass cls, WindowNumber number, bool force = true);
void DeleteWindowByClass(WindowClass cls);
bool EditBoxInGlobalFocus();
Point GetCaretPosition();
#endif /* WINDOW_FUNC_H */

View File

@ -323,6 +323,13 @@ public:
const QueryString *GetQueryString(uint widnum) const;
QueryString *GetQueryString(uint widnum);
virtual const char *GetFocusedText() const;
virtual const char *GetCaret() const;
virtual const char *GetMarkedText(size_t *length) const;
virtual Point GetCaretPosition() const;
virtual Rect GetTextBoundingRect(const char *from, const char *to) const;
virtual const char *GetTextCharacterAtPosition(const Point &pt) const;
void InitNested(const WindowDesc *desc, WindowNumber number = 0);
void CreateNestedTree(const WindowDesc *desc, bool fill_nested = true);
void FinishInitNested(const WindowDesc *desc, WindowNumber window_number = 0);
@ -467,6 +474,7 @@ public:
bool SetFocusedWidget(int widget_index);
EventState HandleEditBoxKey(int wid, WChar key, uint16 keycode);
virtual void InsertTextString(int wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end);
void HandleButtonClick(byte widget);
int GetRowFromWidget(int clickpos, int widget, int padding, int line_height = -1) const;
@ -559,10 +567,7 @@ public:
*/
virtual void OnFocus() {}
/**
* Called when window looses focus
*/
virtual void OnFocusLost() {}
virtual void OnFocusLost();
/**
* A key has been pressed.