DIR := Apps/AxWin/3.0
BIN := AxWinWM
-OBJ := main.o input.o video.o ipc.o image.o
+OBJ := main.o input.o video.o ipc.o image.o utf-8.o
OBJ += wm.o wm_input.o wm_render.o wm_render_text.o
OBJ += decorator.o
OBJ += renderers/passthru.o
WM_Render_GetTextDims(
NULL, // TODO: Select font
- Window->Title ? Window->Title : "jI",
+ Window->Title ? Window->Title : "jI", -1,
&text_width, &text_height
);
WM_Render_DrawText(Window,
Window->W - ciTitlebarHeight - 4, text_height,
NULL, // TODO: Select font
(bActive ? cColourActive_TitleText : cColourInactive_TitleText),
- Window->Title ? Window->Title : "--"
+ Window->Title ? Window->Title : "--", -1
);
// Maximized windows don't have any other borders
--- /dev/null
+/*
+ * Acess2 GUI (AxWin) Version 3
+ * - By John Hodge (thePowersGang)
+ *
+ * utf8.h
+ * - UTF-8 Parsing header
+ */
+#ifndef _UTF8_H_
+#define _UTF8_H_
+
+#include <stdint.h>
+
+extern int ReadUTF8(const char *Input, uint32_t *Val);
+extern int ReadUTF8Rev(const char *Base, int Offset, uint32_t *Val);
+extern int WriteUTF8(char *buf, uint32_t Val);
+
+#endif
+
// --- Rendering
extern void WM_Render_FillRect(tWindow *Window, int X, int Y, int W, int H, tColour Colour);
extern void WM_Render_DrawRect(tWindow *Window, int X, int Y, int W, int H, tColour Colour);
-extern int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Colour, const char *Text);
-extern void WM_Render_GetTextDims(tFont *Font, const char *Text, int *W, int *H);
+/**
+ * \brief Draw text to a window
+ * \param Window Destination Window
+ * \param X X coordinate (Left)
+ * \param Y Y coordinate (Top)
+ * \param W Width of destination region
+ * \param H Height of destination region
+ * \param Font Font to use
+ * \param Colour Text foreground colour
+ * \param Text UTF-8 string to render
+ * \param MaxLen Number of bytes in \a Text to read (Note: A final multi-byte sequence can exceed this count)
+ *
+ * \note As as noted in the \a MaxLen parameter, up to 3 more bytes may be read
+ * if the final character is a multi-byte UTF-8 sequence. This allows 1
+ * to be passed to only render a single character.
+ */
+extern int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Colour, const char *Text, int MaxLen);
+/**
+ * \brief Get the dimensions of a string if it was rendered
+ * \param Font Font to use
+ * \param Text UTF-8 string to be processed
+ * \param MaxLen Number of bytes in \a Text to read (same caveat as WM_Render_DrawText applies)
+ * \param W Pointer to an integer to store the width of the rendered text
+ * \param H Pointer to an integer to store the height of the rendered text
+ */
+extern void WM_Render_GetTextDims(tFont *Font, const char *Text, int MaxLen, int *W, int *H);
extern void WM_Render_DrawImage(tWindow *Window, int X, int Y, int W, int H, tImage *Image);
extern void WM_Render_SetTextCursor(tWindow *Window, int X, int Y, int W, int H, tColour Colour);
// NOTE: Should really be elsewhere
if( read(giTerminalFD, &codepoint, sizeof(codepoint)) != sizeof(codepoint) )
{
// oops, error
+ _SysDebug("Terminal read failed?");
}
+// _SysDebug("Keypress 0x%x", codepoint);
+
switch(codepoint & 0xC0000000)
{
case 0x00000000: // Key pressed
WM_Input_KeyDown(codepoint & KEY_CODEPOINT_MASK, scancode);
- case 0x40000000: // Key refire
+ case 0x80000000: // Key release
WM_Input_KeyFire(codepoint & KEY_CODEPOINT_MASK, scancode);
scancode = 0;
break;
- case 0x80000000: // Key release
+ case 0x40000000: // Key refire
WM_Input_KeyUp(codepoint & KEY_CODEPOINT_MASK, scancode);
scancode = 0;
break;
scancode = codepoint & KEY_CODEPOINT_MASK;
break;
}
-
- // TODO: pass on to message handler
- _SysDebug("Keypress 0x%x", codepoint);
}
if(FD_ISSET(giMouseFD, set))
w, ciMenu_ItemHeight,
gMenu_Font,
cMenu_LabelColour,
- item->Label
+ item->Label, -1
);
// Underline
if(item->UnderlineW)
w, ciMenu_ItemHeight,
gMenu_Font,
cMenu_ShortcutColour,
- item->Shortcut
+ item->Shortcut, -1
);
}
{
char tmp = item->Label[item->KeyOffset];
// Get width of preceding substring
- item->Label[item->KeyOffset] = '\0';
- WM_Render_GetTextDims(NULL, item->Label, &item->UnderlineX, NULL);
+ WM_Render_GetTextDims(NULL, item->Label, item->KeyOffset, &item->UnderlineX, NULL);
// Get the width of the underlined character
- // TODO: Fix for high UTF-8 characters
item->Label[item->KeyOffset] = tmp;
- tmp = item->Label[item->KeyOffset+1];
- item->Label[item->KeyOffset+1] = '\0';
+ // NOTE: 1 makes only one character be parsed, even if it is >1 byte long
WM_Render_GetTextDims(
- NULL, item->Label+item->KeyOffset,
+ NULL, item->Label+item->KeyOffset, 1,
&item->UnderlineW, NULL
);
item->Label[item->KeyOffset+1] = tmp;
}
// - Labels
- WM_Render_GetTextDims(NULL, item->Label, &item->LabelWidth, NULL);
+ WM_Render_GetTextDims(NULL, item->Label, -1, &item->LabelWidth, NULL);
if(item->Shortcut)
- WM_Render_GetTextDims(NULL, item->Shortcut, &item->ShortcutWidth, NULL);
+ WM_Render_GetTextDims(NULL, item->Shortcut, -1, &item->ShortcutWidth, NULL);
else
item->ShortcutWidth = 0;
Element->CachedX+1, Element->CachedY+1,
Element->CachedW-2, Element->CachedH-2,
NULL, TEXT_COLOUR,
- Element->Text
+ Element->Text, -1
);
}
if(Element->Text) free(Element->Text);
Element->Text = strdup(Text);
- WM_Render_GetTextDims(NULL, Element->Text, &w, &h);
+ WM_Render_GetTextDims(NULL, Element->Text, -1, &w, &h);
// Apply edge padding
w += 2; h += 2;
#include <common.h>
#include "./common.h"
#include "./colours.h"
+#include <utf8.h>
+#include <string.h>
struct sTextInputInfo
{
int DrawOfs; // Byte offset for the leftmost character
int CursorXOfs; // Pixel offset of the cursor
+
+ int CursorByteOfs;
+ int Length;
};
+// === CONSTANTS ===
+const int ciTextInput_MarginT = 3;
+const int ciTextInput_MarginB = 3;
+const int ciTextInput_MarginV = 6; // Sum of above
+const int ciTextInput_MarginL = 3;
+const int ciTextInput_MarginR = 3;
+const int ciTextInput_MarginH = 6;
+
+// === GLOBALS ===
+tFont *gpTextInput_Font = NULL;
+
+// === CODE ===
void Widget_TextInput_Render(tWindow *Window, tElement *Element)
{
struct sTextInputInfo *info = (void*)Element->Data;
struct sWidgetWin *wininfo = Window->RendererInfo;
+ // Scroll view when X offset reaches either end
+ while(info->CursorXOfs >= Element->CachedW - ciTextInput_MarginH)
+ {
+ int w;
+ uint32_t cp;
+ info->DrawOfs += ReadUTF8( &Element->Text[info->DrawOfs], &cp );
+ WM_Render_GetTextDims(
+ gpTextInput_Font,
+ &Element->Text[info->DrawOfs], info->CursorByteOfs - info->DrawOfs,
+ &w, NULL
+ );
+ info->CursorXOfs = w;
+ }
+ if(info->CursorXOfs < 0)
+ {
+ info->DrawOfs = info->CursorByteOfs;
+ info->CursorXOfs = 0;
+ }
+
+
+ // Borders
WM_Render_FillRect(Window,
Element->CachedX, Element->CachedY,
Element->CachedW, Element->CachedH,
);
// Text
+ // - Pre-cursor
WM_Render_DrawText(Window,
- Element->CachedX+2, Element->CachedY+2,
- Element->CachedW-4, Element->CachedW-4,
- NULL, TEXTINPUT_TEXT,
- &Element->Text[info->DrawOfs]
+ Element->CachedX+ciTextInput_MarginL, Element->CachedY+ciTextInput_MarginT,
+ Element->CachedW-ciTextInput_MarginH, Element->CachedH-ciTextInput_MarginV,
+ gpTextInput_Font, TEXTINPUT_TEXT,
+ &Element->Text[info->DrawOfs], -1
);
- // TODO: Determine if this element has focus
+ // Cursor
if( wininfo->FocusedElement == Element )
{
- // TODO: Multiple Cursors
WM_Render_SetTextCursor(Window,
- Element->CachedX+2+info->CursorXOfs,
- Element->CachedY+2,
- 1, Element->CachedH-4,
+ Element->CachedX+ciTextInput_MarginL+info->CursorXOfs,
+ Element->CachedY+ciTextInput_MarginR,
+ 1, Element->CachedH-ciTextInput_MarginV,
TEXTINPUT_TEXT
);
}
int h;
// TODO: Select font correctly
- WM_Render_GetTextDims(NULL, "jJ", NULL, &h);
+ WM_Render_GetTextDims(gpTextInput_Font, "jy|qJ", -1, NULL, &h);
- h += 2+2; // Border padding
+ h += ciTextInput_MarginV; // Border padding
Element->MinH = h;
- Element->MinW = 4;
+ Element->MinW = ciTextInput_MarginH;
info = Element->Data = malloc(sizeof(*info));
info->DrawOfs = 0;
// No need to explicitly update parent min dims, as the AddElement routine does that
}
-int Widget_TextInput_KeyFire(tElement *Ele, int KeySym, int Character)
+int Widget_TextInput_KeyFire(tElement *Element, int KeySym, int Character)
{
- _SysDebug("Key 0x%x fired ('%c')", Character, Character);
+ struct sTextInputInfo *info = Element->Data;
+ int len;
+ int w;
+ char *dest;
+ uint32_t cp;
+
+// _SysDebug("Key 0x%x fired ('%c')", Character, Character);
+
+ if( Character == 0 )
+ return 0;
+
+ // TODO: Don't hard code
+ if(Character > 0x30000000) return 0;
+
+ switch(Character)
+ {
+ case '\t':
+ return 0;
+
+ case '\b':
+ // Check if there is anything to delete
+ if( info->CursorByteOfs == 0 ) return 0;
+ // Get character to be deleted
+ len = ReadUTF8Rev(Element->Text, info->CursorByteOfs, &cp);
+ info->CursorByteOfs -= len;
+ dest = &Element->Text[info->CursorByteOfs];
+// _SysDebug("\\b, len = %i, removing '%.*s'", len, len, dest);
+ WM_Render_GetTextDims(gpTextInput_Font, dest, len, &w, 0);
+ // Remove from buffer
+ memmove(dest, &dest[len], info->Length - info->CursorByteOfs - len);
+ info->Length -= len;
+ Element->Text[info->Length] = '\0';
+ // Adjust cursor
+ info->CursorXOfs -= w;
+ break;
+ default:
+ if(Character >= 0x30000000) return 0;
+ if(Character < ' ') return 0;
+
+ // Get required length
+ len = WriteUTF8(NULL, Character);
+
+ // Create space (possibly in the middle)
+ Element->Text = realloc(Element->Text, info->Length + len + 1);
+ dest = &Element->Text[info->CursorByteOfs];
+ memmove(&dest[len], dest, info->Length - info->CursorByteOfs);
+ // Add the character
+ WriteUTF8(dest, Character);
+ info->CursorByteOfs += len;
+ info->Length += len;
+ Element->Text[info->Length] = '\0';
+
+ // Update the cursor position
+ // - Scrolling is implemented in render function (CachedW/CachedH are invalid atm)
+ WM_Render_GetTextDims(gpTextInput_Font, dest, len, &w, NULL);
+ info->CursorXOfs += w;
+ }
+
+ // TODO: Have a Widget_ function to do this instead
+ WM_Invalidate(Element->Window);
+
return 0;
}
--- /dev/null
+/*
+ * Acess2 GUI (AxWin) Version 3
+ * - By John Hodge (thePowersGang)
+ *
+ * utf-8.c
+ * - UTF-8 Parsing code
+ */
+#include <stdint.h>
+#include <utf8.h>
+
+/**
+ * \brief Read a UTF-8 character from a string
+ * \param Input Source UTF-8 encoded string
+ * \param Val Destination for read codepoint
+ * \return Number of bytes read/used
+ */
+int ReadUTF8(const char *Input, uint32_t *Val)
+{
+ const uint8_t *str = (const uint8_t *)Input;
+ *Val = 0xFFFD; // Assume invalid character
+
+ // ASCII
+ if( !(*str & 0x80) ) {
+ *Val = *str;
+ return 1;
+ }
+
+ // Middle of a sequence
+ if( (*str & 0xC0) == 0x80 ) {
+ return 1;
+ }
+
+ // Two Byte
+ if( (*str & 0xE0) == 0xC0 ) {
+ *Val = (*str & 0x1F) << 6; // Upper 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F); // Lower 6 Bits
+ return 2;
+ }
+
+ // Three Byte
+ if( (*str & 0xF0) == 0xE0 ) {
+ *Val = (*str & 0x0F) << 12; // Upper 4 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F) << 6; // Middle 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F); // Lower 6 Bits
+ return 3;
+ }
+
+ // Four Byte
+ if( (*str & 0xF1) == 0xF0 ) {
+ *Val = (*str & 0x07) << 18; // Upper 3 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F); // Lower 6 Bits
+ return 4;
+ }
+
+ // UTF-8 Doesn't support more than four bytes
+ return 4;
+}
+
+/**
+ * \brief Get the UTF-8 character before the
+ * \
+ */
+int ReadUTF8Rev(const char *Base, int Offset, uint32_t *Val)
+{
+ int len = 0;
+
+ // Scan backwards for the beginning of the character
+ while( Offset > 0 && (Base[Offset--] & 0xC0) == 0x80 )
+ len ++;
+ // Invalid string (no beginning)
+ if(Offset == 0 && (Base[Offset] & 0xC0) == 0x80 )
+ return len;
+
+ len ++; // First character
+ if( ReadUTF8(Base+Offset, Val) != len ) {
+ *Val = 0xFFFD;
+ }
+ return len;
+}
+
+/**
+ * \brief Write a UTF-8 character sequence to a string
+ * \param buf Destination buffer (must have at least 4 bytes available)
+ * \param Val Unicode codepoint to write
+ * \return Number of bytes written
+ * \note Does not NULL terminate the string in \a buf
+ */
+int WriteUTF8(char *buf, uint32_t Val)
+{
+ uint8_t *str = (void*)buf;
+
+ // ASCII
+ if( Val < 128 ) {
+ if(str) {
+ *str = Val;
+ }
+ return 1;
+ }
+
+ // Two Byte
+ if( Val < 0x8000 ) {
+ if(str) {
+ *str = 0xC0 | (Val >> 6);
+ str ++;
+ *str = 0x80 | (Val & 0x3F);
+ }
+ return 2;
+ }
+
+ // Three Byte
+ if( Val < 0x10000 ) {
+ if(str) {
+ *str = 0xE0 | (Val >> 12);
+ str ++;
+ *str = 0x80 | ((Val >> 6) & 0x3F);
+ str ++;
+ *str = 0x80 | (Val & 0x3F);
+ }
+ return 3;
+ }
+
+ // Four Byte
+ if( Val < 0x110000 ) {
+ if(str) {
+ *str = 0xF0 | (Val >> 18);
+ str ++;
+ *str = 0x80 | ((Val >> 12) & 0x3F);
+ str ++;
+ *str = 0x80 | ((Val >> 6) & 0x3F);
+ str ++;
+ *str = 0x80 | (Val & 0x3F);
+ }
+ return 4;
+ }
+
+ // UTF-8 Doesn't support more than four bytes
+ return 0;
+}
+
void WM_Input_KeyFire(uint32_t Character, uint32_t Scancode)
{
struct sWndMsg_KeyAction msg;
+
+ // TODO: Properly translate into KeySyms and Unicode
+
msg.KeySym = Scancode;
msg.UCS32 = Character;
WM_SendMessage(NULL, gpWM_FocusedWindow, WNDMSG_KEYFIRE, sizeof(msg), &msg);
#include <common.h>
#include <wm_internals.h>
#include <stdlib.h>
+#include <utf8.h>
// === TYPES ===
typedef struct sGlyph tGlyph;
// === PROTOTYPES ===
- int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Color, const char *Text);
-void WM_Render_GetTextDims(tFont *Font, const char *Text, int *W, int *H);
+ int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Color, const char *Text, int MaxLen);
+void WM_Render_GetTextDims(tFont *Font, const char *Text, int MaxLen, int *W, int *H);
tGlyph *_GetGlyph(tFont *Font, uint32_t Codepoint);
void _RenderGlyph(tWindow *Window, short X, short Y, tGlyph *Glyph, uint32_t Color);
tGlyph *_SystemFont_CacheGlyph(tFont *Font, uint32_t Codepoint);
- int ReadUTF8(const char *Input, uint32_t *Output);
// === GLOBALS ===
tFont gSystemFont = {
/**
* \brief Draw text to the screen
*/
-int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Colour, const char *Text)
+int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Colour, const char *Text, int MaxLen)
{
int xOfs = 0;
tGlyph *glyph;
if(!Text) return 0;
+ if(MaxLen < 0) MaxLen = INT_MAX;
X += Window->BorderL;
Y += Window->BorderT;
// Handle NULL font (system default monospace)
if( !Font ) Font = &gSystemFont;
- while( *Text )
+ while( MaxLen > 0 && *Text )
{
+ int len;
// Read character
- Text += ReadUTF8(Text, &ch);
+ len = ReadUTF8(Text, &ch);
+ Text += len;
+ MaxLen -= len;
// Find (or load) the glyph
glyph = _GetGlyph(Font, ch);
return xOfs;
}
-void WM_Render_GetTextDims(tFont *Font, const char *Text, int *W, int *H)
+void WM_Render_GetTextDims(tFont *Font, const char *Text, int MaxLen, int *W, int *H)
{
int w=0, h=0;
uint32_t ch;
tGlyph *glyph;
if( !Font ) Font = &gSystemFont;
- while( *Text )
+ if(MaxLen < 0) MaxLen = INT_MAX;
+
+ while( MaxLen > 0 && *Text )
{
- Text += ReadUTF8(Text, &ch);
+ int len;
+ len = ReadUTF8(Text, &ch);
+ Text += len;
+ MaxLen -= len;
glyph = _GetGlyph(Font, ch);
if( !glyph ) continue;
return ret;
}
-
-/**
- * \fn int ReadUTF8(char *Input, uint32_t *Val)
- * \brief Read a UTF-8 character from a string
- */
-int ReadUTF8(const char *Input, uint32_t *Val)
-{
- const uint8_t *str = (const uint8_t *)Input;
- *Val = 0xFFFD; // Assume invalid character
-
- // ASCII
- if( !(*str & 0x80) ) {
- *Val = *str;
- return 1;
- }
-
- // Middle of a sequence
- if( (*str & 0xC0) == 0x80 ) {
- return 1;
- }
-
- // Two Byte
- if( (*str & 0xE0) == 0xC0 ) {
- *Val = (*str & 0x1F) << 6; // Upper 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
- return 2;
- }
-
- // Three Byte
- if( (*str & 0xF0) == 0xE0 ) {
- *Val = (*str & 0x0F) << 12; // Upper 4 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 6; // Middle 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
- return 3;
- }
-
- // Four Byte
- if( (*str & 0xF1) == 0xF0 ) {
- *Val = (*str & 0x07) << 18; // Upper 3 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
- return 4;
- }
-
- // UTF-8 Doesn't support more than four bytes
- return 4;
-}
-
-
-
-
#ifndef _STDINT_H_
#define _STDINT_H_
+#define INT_MIN -0x80000000
+#define INT_MAX 0x7FFFFFFF
+
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;