diff --git a/demo/src/common.c b/demo/src/common.c index fa50e2e..b29c50a 100644 --- a/demo/src/common.c +++ b/demo/src/common.c @@ -69,6 +69,9 @@ void commonInit() { void commonDraw() { pushDrawingState(); + int shift = keyDown("left shift") || keyDown("right shift"); + int ctrl = keyDown("left ctrl") || keyDown("right ctrl"); + noFill(); textFontDefault(); textSize(16); @@ -82,7 +85,7 @@ void commonDraw() { soundPlay(beep, FALSE); if (mouseWentDown("middle")) - askTextEx("Test\ntext input", buffer, sizeof(buffer), TRUE, FALSE); + askTextEx("Test\ntext input", buffer, sizeof(buffer), shift, ctrl); if (mouseWentDown("right")) messageBox("Test message box\nwith test message."); diff --git a/src/private.h b/src/private.h index f98842d..4892eb8 100644 --- a/src/private.h +++ b/src/private.h @@ -195,6 +195,7 @@ typedef struct _HeliDialog { int shown; const char *question; char *answer; + char *passwordText; int maxAnswerSize; int multiline; int password; diff --git a/src/world.c b/src/world.c index 351faff..7a9d50a 100644 --- a/src/world.c +++ b/src/world.c @@ -420,9 +420,10 @@ void askTextEx(const char *question, char *answer, int maxAnswerSize, int multil dialog.shown = TRUE; dialog.question = question ? question : ""; dialog.answer = calloc(1, maxAnswerSize + 1); + dialog.passwordText = calloc(1, maxAnswerSize*4 + 1); dialog.maxAnswerSize = maxAnswerSize - 1; if (maxAnswerSize > 0) memcpy(dialog.answer, answer, maxAnswerSize); - dialog.multiline = multiline != 0; + dialog.multiline = multiline != 0 && password == 0; dialog.password = password != 0; dialog.pos = dialog.selPos = strlen(dialog.answer); dialog.success = FALSE; @@ -438,6 +439,7 @@ void askTextEx(const char *question, char *answer, int maxAnswerSize, int multil if (dialog.success && maxAnswerSize > 0) strcpy(answer, dialog.answer); free(dialog.answer); + free(dialog.passwordText); memset(&dialog, 0, sizeof(dialog)); resetEvents(); diff --git a/src/worldui.c b/src/worldui.c index ddd91e5..5d45acb 100644 --- a/src/worldui.c +++ b/src/worldui.c @@ -8,6 +8,9 @@ #include "world.h" +static const char passwordPattern[] = "\u25CF"; + + static int utf8charlen(const char *c) { if (!*c) return 0; if ( (c[0] & 0x80) == 0x00 ) @@ -28,6 +31,29 @@ static int utf8charlen(const char *c) { } +static int utf8len(const char *c) { + int len = 0; + while(*c) { + int l = utf8charlen(c); + c += l > 0 ? l : 1; + ++len; + } + return len; +} + + +static int utf8pos(const char *c, int pos) { + int p = 0; + const char *end = c + pos; + while(*c && c < end) { + int l = utf8charlen(c); + c += l > 0 ? l : 1; + ++p; + } + return p; +} + + static int utf8shift(const char *s, int pos, int offset) { int len = strlen(s); if (pos > len) { offset += pos - len; pos = len; } @@ -93,7 +119,7 @@ static void insert(HeliDialog *dialog, const char *text) { static void copy(HeliDialog *dialog) { - if (dialog->pos != dialog->selPos) { + if (!dialog->password && dialog->pos != dialog->selPos) { int pos0 = dialog->pos < dialog->selPos ? dialog->pos : dialog->selPos; int pos1 = dialog->pos < dialog->selPos ? dialog->selPos : dialog->pos; char c = dialog->answer[pos1]; @@ -118,13 +144,21 @@ static void draw(HeliDialog *dialog) { const double w = worldGetWidth(); const double h = worldGetHeight(); const double border = 16; - const double scroll = 16; - const double title = 64; - const double buttons = 32; - const double l = border; - const double t = border + title; - const double r = w - border - scroll; - const double b = h - border - scroll - buttons; + + double title = 64; + double buttons = 32; + double l = border; + double t = border + title; + double r = w - border; + double b = h - border - buttons; + if (!dialog->multiline && b - t > buttons) { + t = round(0.5*(b + t - buttons)); + b = t + buttons; + title = t - border; + } + + const double cursorSrcollWidth = 64; + const double cursorSrcollHeight = 16; const double bt = h - buttons - border + 8; const double bb = h - border; @@ -148,21 +182,58 @@ static void draw(HeliDialog *dialog) { fill(fillColor); rect(0, 0, w, h); + noFill(); stroke(strokeColor); textAlign(HALIGN_LEFT, VALIGN_TOP); - TextLayout layout = createTextLayout(dialog->answer); - double tx = l + dialog->scrollX; - double ty = t + dialog->scrollY; - double cx = textLayoutCursorGetX(layout, dialog->pos) + tx; - double cy = textLayoutCursorGetY(layout, dialog->pos) + ty; - double ch = textLayoutCursorGetHeight(layout, dialog->pos); + TextLayout layout = createTextLayout(dialog->password ? dialog->passwordText : dialog->answer); + + int shift = keyDown("left shift") || keyDown("right shift"); + if (keyWentDown("up")) { + dialog->pos = textLayoutCursorUp(layout, dialog->pos); + if (!shift) dialog->selPos = dialog->pos; + } + if (keyWentDown("down")) { + dialog->pos = textLayoutCursorDown(layout, dialog->pos); + if (!shift) dialog->selPos = dialog->pos; + } + + int pos = dialog->pos; + int selPos = dialog->selPos; + if (dialog->password) { + pos = utf8pos(dialog->answer, pos)*((int)sizeof(passwordPattern) - 1); + selPos = utf8pos(dialog->answer, selPos)*((int)sizeof(passwordPattern) - 1); + } + cliprect(l-1, t-1, r-l+2, b-t+2); - if (dialog->pos == dialog->selPos) { + double tx = l; + double ty = t; + double cx = textLayoutCursorGetX(layout, pos) + tx; + double cy = textLayoutCursorGetY(layout, pos) + ty; + double ch = textLayoutCursorGetHeight(layout, pos); + + double minScrollY = t + ch + cursorSrcollHeight - cy; + double maxScrollY = b - cursorSrcollHeight - cy; + if (maxScrollY > 0) maxScrollY = 0; + if (dialog->scrollY < minScrollY) dialog->scrollY = minScrollY; + if (dialog->scrollY > maxScrollY) dialog->scrollY = maxScrollY; + + double minScrollX = l + cursorSrcollWidth - cx; + double maxScrollX = r - cursorSrcollWidth - cx; + if (maxScrollX > 0) maxScrollX = 0; + if (dialog->scrollX < minScrollX) dialog->scrollX = minScrollX; + if (dialog->scrollX > maxScrollX) dialog->scrollX = maxScrollX; + + tx += dialog->scrollX; + ty += dialog->scrollY; + cx += dialog->scrollX; + cy += dialog->scrollY; + + if (pos == selPos) { textLayoutDraw(layout, tx, ty); } else { - int p0 = dialog->pos < dialog->selPos ? dialog->pos : dialog->selPos; - int p1 = dialog->pos < dialog->selPos ? dialog->selPos : dialog->pos; + int p0 = pos < selPos ? pos : selPos; + int p1 = pos < selPos ? selPos : pos; fill(selFillColor); stroke(selTextColor); textLayoutDrawSubstr(layout, tx, ty, p0, p1 - p0); @@ -171,26 +242,20 @@ static void draw(HeliDialog *dialog) { textLayoutDrawSubstr(layout, tx, ty, 0, p0); textLayoutDrawFrom(layout, tx, ty, p1); } + stroke(rgba(1, 1, 1, 0.5 + 0.5*sin(time/0.5*2*PI))); line(cx, cy, cx, cy - ch); - int shift = keyDown("left shift") || keyDown("right shift"); - if (keyWentDown("up")) { - dialog->pos = textLayoutCursorUp(layout, dialog->pos); - if (!shift) dialog->selPos = dialog->pos; - } - if (keyWentDown("down")) { - dialog->pos = textLayoutCursorDown(layout, dialog->pos); - if (!shift) dialog->selPos = dialog->pos; - } + textLayoutDestroy(layout); noClip(); + noFill(); stroke(strokeColor); rect(l - 2, t - 2, r - l + 4, b - t + 4); textAlign(HALIGN_CENTER, VALIGN_CENTER); - text(dialog->question, w/2, title/2); + text(dialog->question, w/2, border + title/2); rect(bl0, bt, bw, bh); rect(bl1, bt, bw, bh); @@ -229,7 +294,7 @@ void heliDialogDraw(HeliDialog *dialog) { dialog->selPos = dialog->pos; dialog->pos = utf8shift(dialog->answer, dialog->pos, 1); } else - if (shift) { + if (shift && !dialog->password) { copy(dialog); } insert(dialog, ""); @@ -278,15 +343,15 @@ void heliDialogDraw(HeliDialog *dialog) { if (ctrl && keyWentDown("a")) { dialog->selPos = 0; dialog->pos = strlen(dialog->answer); } - if (ctrl && keyWentDown("c")) + if (!dialog->password && ctrl && keyWentDown("c")) copy(dialog); - if (ctrl && keyWentDown("insert")) + if (!dialog->password && ctrl && keyWentDown("insert")) copy(dialog); if (ctrl && keyWentDown("v")) paste(dialog); if (shift && keyWentDown("insert")) paste(dialog); - if (ctrl && keyWentDown("x")) + if (!dialog->password && ctrl && keyWentDown("x")) { copy(dialog); insert(dialog, ""); } if (keyWentDown("escape")) dialog->shown = FALSE; @@ -297,5 +362,14 @@ void heliDialogDraw(HeliDialog *dialog) { fixPos(dialog); } + if (dialog->password) { + int len = utf8len(dialog->answer); + char *c = dialog->passwordText; + for(int i = 0; i < len; ++i) + for(const char *p = passwordPattern; *p; ++p, ++c) + *c = *p; + *c = 0; + } + draw(dialog); }