Implemented a value hint, changed colors.
* When you press on a value, that value will be shown with a highlighted background across the board until another value is set. * Changed the colors to those from the Haiku logo rather than the BeOS logo. This makes it a bit more colorful which one might need to get used to -- comments welcome.
This commit is contained in:
+106
-36
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2007-2010, Axel Dörfler, [email protected].
|
* Copyright 2007-2012, Axel Dörfler, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -28,9 +28,17 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
const uint32 kMsgCheckSolved = 'chks';
|
static const uint32 kMsgCheckSolved = 'chks';
|
||||||
|
|
||||||
const uint32 kStrongLineSize = 2;
|
static const uint32 kStrongLineSize = 2;
|
||||||
|
|
||||||
|
static const rgb_color kBackgroundColor = {255, 255, 240};
|
||||||
|
static const rgb_color kHintColor = {255, 115, 0};
|
||||||
|
static const rgb_color kValueColor = {0, 91, 162};
|
||||||
|
static const rgb_color kValueCompletedColor = {55, 140, 35};
|
||||||
|
static const rgb_color kInvalidValueColor = {200, 0, 0};
|
||||||
|
static const rgb_color kValueHintBackgroundColor = {255, 215, 127};
|
||||||
|
static const rgb_color kHintValueHintBackgroundColor = {255, 235, 185};
|
||||||
|
|
||||||
extern const char* kSignature;
|
extern const char* kSignature;
|
||||||
|
|
||||||
@@ -103,6 +111,7 @@ SudokuView::InitObject(const BMessage* archive)
|
|||||||
{
|
{
|
||||||
fField = NULL;
|
fField = NULL;
|
||||||
fShowHintX = ~0UL;
|
fShowHintX = ~0UL;
|
||||||
|
fValueHintValue = ~0UL;
|
||||||
fLastHintValue = ~0UL;
|
fLastHintValue = ~0UL;
|
||||||
fLastField = ~0UL;
|
fLastField = ~0UL;
|
||||||
fKeyboardX = 0;
|
fKeyboardX = 0;
|
||||||
@@ -131,9 +140,8 @@ SudokuView::InitObject(const BMessage* archive)
|
|||||||
|
|
||||||
SetViewColor(B_TRANSPARENT_COLOR);
|
SetViewColor(B_TRANSPARENT_COLOR);
|
||||||
// to avoid flickering
|
// to avoid flickering
|
||||||
rgb_color color = { 255, 255, 240 };
|
fBackgroundColor = kBackgroundColor;
|
||||||
fBackgroundColor = color;
|
SetLowColor(fBackgroundColor);
|
||||||
SetLowColor(color);
|
|
||||||
FrameResized(0, 0);
|
FrameResized(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +220,7 @@ SudokuView::SetTo(entry_ref& ref)
|
|||||||
|
|
||||||
_PushUndo();
|
_PushUndo();
|
||||||
status = fField->SetTo(_BaseCharacter(), buffer);
|
status = fField->SetTo(_BaseCharacter(), buffer);
|
||||||
|
fValueHintValue = ~0UL;
|
||||||
Invalidate();
|
Invalidate();
|
||||||
fclose(file);
|
fclose(file);
|
||||||
return status;
|
return status;
|
||||||
@@ -234,6 +243,7 @@ SudokuView::SetTo(const char* data)
|
|||||||
|
|
||||||
_PushUndo();
|
_PushUndo();
|
||||||
status = fField->SetTo(_BaseCharacter(), buffer);
|
status = fField->SetTo(_BaseCharacter(), buffer);
|
||||||
|
fValueHintValue = ~0UL;
|
||||||
Invalidate();
|
Invalidate();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -250,6 +260,7 @@ SudokuView::SetTo(SudokuField* field)
|
|||||||
fField = field;
|
fField = field;
|
||||||
|
|
||||||
fBlockSize = fField->BlockSize();
|
fBlockSize = fField->BlockSize();
|
||||||
|
fValueHintValue = ~0UL;
|
||||||
FrameResized(0, 0);
|
FrameResized(0, 0);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -377,7 +388,7 @@ SudokuView::SaveTo(BDataIO& stream, uint32 exportAs)
|
|||||||
text << "<td width=\"" << divider << "\" ";
|
text << "<td width=\"" << divider << "\" ";
|
||||||
text << "class=\"sudoku sudoku_empty " << style;
|
text << "class=\"sudoku sudoku_empty " << style;
|
||||||
text << "\">\n ";
|
text << "\">\n ";
|
||||||
} else if (fField->FlagsAt(x, y) & kInitialValue) {
|
} else if (fField->IsInitialValue(x, y)) {
|
||||||
text << "<td width=\"" << divider << "\" ";
|
text << "<td width=\"" << divider << "\" ";
|
||||||
text << "class=\"sudoku sudoku_initial " << style
|
text << "class=\"sudoku sudoku_initial " << style
|
||||||
<< "\">\n";
|
<< "\">\n";
|
||||||
@@ -543,7 +554,7 @@ SudokuView::ClearChanged()
|
|||||||
|
|
||||||
for (uint32 y = 0; y < fField->Size(); y++) {
|
for (uint32 y = 0; y < fField->Size(); y++) {
|
||||||
for (uint32 x = 0; x < fField->Size(); x++) {
|
for (uint32 x = 0; x < fField->Size(); x++) {
|
||||||
if ((fField->FlagsAt(x, y) & kInitialValue) == 0)
|
if (!fField->IsInitialValue(x, y))
|
||||||
fField->SetValueAt(x, y, 0);
|
fField->SetValueAt(x, y, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -612,8 +623,9 @@ SudokuView::FrameResized(float /*width*/, float /*height*/)
|
|||||||
// font for numbers
|
// font for numbers
|
||||||
|
|
||||||
uint32 size = fField->Size();
|
uint32 size = fField->Size();
|
||||||
fWidth = (Bounds().Width() - kStrongLineSize * (fBlockSize - 1)) / size;
|
fWidth = (Bounds().Width() + 2 - kStrongLineSize * (fBlockSize - 1)) / size;
|
||||||
fHeight = (Bounds().Height() - kStrongLineSize * (fBlockSize - 1)) / size;
|
fHeight = (Bounds().Height() + 2 - kStrongLineSize * (fBlockSize - 1))
|
||||||
|
/ size;
|
||||||
_FitFont(fFieldFont, fWidth - 2, fHeight - 2);
|
_FitFont(fFieldFont, fWidth - 2, fHeight - 2);
|
||||||
|
|
||||||
font_height fontHeight;
|
font_height fontHeight;
|
||||||
@@ -641,8 +653,8 @@ SudokuView::FrameResized(float /*width*/, float /*height*/)
|
|||||||
BPoint
|
BPoint
|
||||||
SudokuView::_LeftTop(uint32 x, uint32 y)
|
SudokuView::_LeftTop(uint32 x, uint32 y)
|
||||||
{
|
{
|
||||||
return BPoint(x * fWidth + x / fBlockSize * kStrongLineSize + 1,
|
return BPoint(x * fWidth - 1 + x / fBlockSize * kStrongLineSize + 1,
|
||||||
y * fHeight + y / fBlockSize * kStrongLineSize + 1);
|
y * fHeight - 1 + y / fBlockSize * kStrongLineSize + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -678,6 +690,22 @@ SudokuView::_InvalidateField(uint32 x, uint32 y)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SudokuView::_InvalidateValue(uint32 value, bool invalidateHint,
|
||||||
|
uint32 fieldX, uint32 fieldY)
|
||||||
|
{
|
||||||
|
for (uint32 y = 0; y < fField->Size(); y++) {
|
||||||
|
for (uint32 x = 0; x < fField->Size(); x++) {
|
||||||
|
if (fField->ValueAt(x, y) == value || (x == fieldX && y == fieldY))
|
||||||
|
Invalidate(_Frame(x, y));
|
||||||
|
else if (invalidateHint && fField->ValueAt(x, y) == 0
|
||||||
|
&& fField->HasHint(x, y, value))
|
||||||
|
Invalidate(_Frame(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SudokuView::_InvalidateKeyboardFocus(uint32 x, uint32 y)
|
SudokuView::_InvalidateKeyboardFocus(uint32 x, uint32 y)
|
||||||
{
|
{
|
||||||
@@ -723,6 +751,22 @@ SudokuView::_GetFieldFor(BPoint where, uint32& x, uint32& y)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SudokuView::_SetValueHintValue(uint32 value)
|
||||||
|
{
|
||||||
|
if (value == fValueHintValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (fValueHintValue != ~0UL)
|
||||||
|
_InvalidateValue(fValueHintValue, true);
|
||||||
|
|
||||||
|
fValueHintValue = value;
|
||||||
|
|
||||||
|
if (fValueHintValue != ~0UL)
|
||||||
|
_InvalidateValue(fValueHintValue, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SudokuView::_RemoveHint()
|
SudokuView::_RemoveHint()
|
||||||
{
|
{
|
||||||
@@ -801,6 +845,15 @@ SudokuView::MouseDown(BPoint where)
|
|||||||
Looper()->CurrentMessage()->FindInt32("clicks", &clicks);
|
Looper()->CurrentMessage()->FindInt32("clicks", &clicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buttons == B_PRIMARY_MOUSE_BUTTON && clicks == 1) {
|
||||||
|
uint32 value = fField->ValueAt(x, y);
|
||||||
|
if (value != 0) {
|
||||||
|
// Toggle value hint
|
||||||
|
_SetValueHintValue(fValueHintValue == value ? ~0UL : value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32 hintX, hintY;
|
uint32 hintX, hintY;
|
||||||
if (!_GetHintFieldFor(where, x, y, hintX, hintY))
|
if (!_GetHintFieldFor(where, x, y, hintX, hintY))
|
||||||
return;
|
return;
|
||||||
@@ -813,9 +866,10 @@ SudokuView::MouseDown(BPoint where)
|
|||||||
|| (buttons & (B_SECONDARY_MOUSE_BUTTON
|
|| (buttons & (B_SECONDARY_MOUSE_BUTTON
|
||||||
| B_TERTIARY_MOUSE_BUTTON)) != 0) {
|
| B_TERTIARY_MOUSE_BUTTON)) != 0) {
|
||||||
// double click or other buttons set a value
|
// double click or other buttons set a value
|
||||||
if ((fField->FlagsAt(x, y) & kInitialValue) == 0) {
|
if (!fField->IsInitialValue(x, y)) {
|
||||||
bool wasCompleted;
|
bool wasCompleted;
|
||||||
if (fField->ValueAt(x, y) > 0) {
|
if (fField->ValueAt(x, y) > 0) {
|
||||||
|
// Remove value
|
||||||
value = fField->ValueAt(x, y) - 1;
|
value = fField->ValueAt(x, y) - 1;
|
||||||
wasCompleted = fField->IsValueCompleted(value + 1);
|
wasCompleted = fField->IsValueCompleted(value + 1);
|
||||||
|
|
||||||
@@ -823,6 +877,7 @@ SudokuView::MouseDown(BPoint where)
|
|||||||
fShowHintX = x;
|
fShowHintX = x;
|
||||||
fShowHintY = y;
|
fShowHintY = y;
|
||||||
} else {
|
} else {
|
||||||
|
// Set value
|
||||||
wasCompleted = fField->IsValueCompleted(value + 1);
|
wasCompleted = fField->IsValueCompleted(value + 1);
|
||||||
|
|
||||||
fField->SetValueAt(x, y, value + 1);
|
fField->SetValueAt(x, y, value + 1);
|
||||||
@@ -834,8 +889,11 @@ SudokuView::MouseDown(BPoint where)
|
|||||||
fLastField = field;
|
fLastField = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value + 1 != fValueHintValue)
|
||||||
|
_SetValueHintValue(~0UL);
|
||||||
|
|
||||||
if (wasCompleted != fField->IsValueCompleted(value + 1))
|
if (wasCompleted != fField->IsValueCompleted(value + 1))
|
||||||
Invalidate();
|
_InvalidateValue(value + 1, false, x, y);
|
||||||
else
|
else
|
||||||
_InvalidateField(x, y);
|
_InvalidateField(x, y);
|
||||||
}
|
}
|
||||||
@@ -852,7 +910,12 @@ SudokuView::MouseDown(BPoint where)
|
|||||||
hintMask &= ~valueMask;
|
hintMask &= ~valueMask;
|
||||||
|
|
||||||
fField->SetHintMaskAt(x, y, hintMask);
|
fField->SetHintMaskAt(x, y, hintMask);
|
||||||
_InvalidateHintField(x, y, hintX, hintY);
|
|
||||||
|
if (value + 1 != fValueHintValue) {
|
||||||
|
_SetValueHintValue(~0UL);
|
||||||
|
_InvalidateHintField(x, y, hintX, hintY);
|
||||||
|
} else
|
||||||
|
_InvalidateField(x, y);
|
||||||
|
|
||||||
fLastHintValue = value;
|
fLastHintValue = value;
|
||||||
fLastField = field;
|
fLastField = field;
|
||||||
@@ -879,8 +942,9 @@ SudokuView::MouseMoved(BPoint where, uint32 transit,
|
|||||||
fKeyboardX = x;
|
fKeyboardX = x;
|
||||||
fKeyboardY = y;
|
fKeyboardY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isField
|
if (!isField
|
||||||
|| (fField->FlagsAt(x, y) & kInitialValue) != 0
|
|| fField->IsInitialValue(x, y)
|
||||||
|| (!fShowCursor && fField->ValueAt(x, y) != 0)) {
|
|| (!fShowCursor && fField->ValueAt(x, y) != 0)) {
|
||||||
_RemoveHint();
|
_RemoveHint();
|
||||||
return;
|
return;
|
||||||
@@ -919,7 +983,7 @@ void
|
|||||||
SudokuView::_InsertKey(char rawKey, int32 modifiers)
|
SudokuView::_InsertKey(char rawKey, int32 modifiers)
|
||||||
{
|
{
|
||||||
if (!fEditable || !_ValidCharacter(rawKey)
|
if (!fEditable || !_ValidCharacter(rawKey)
|
||||||
|| (fField->FlagsAt(fKeyboardX, fKeyboardY) & kInitialValue) != 0)
|
|| fField->IsInitialValue(fKeyboardX, fKeyboardY))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32 value = rawKey - _BaseCharacter();
|
uint32 value = rawKey - _BaseCharacter();
|
||||||
@@ -1144,9 +1208,12 @@ SudokuView::_DrawHints(uint32 x, uint32 y)
|
|||||||
for (uint32 j = 0; j < fBlockSize; j++) {
|
for (uint32 j = 0; j < fBlockSize; j++) {
|
||||||
for (uint32 i = 0; i < fBlockSize; i++) {
|
for (uint32 i = 0; i < fBlockSize; i++) {
|
||||||
uint32 value = j * fBlockSize + i;
|
uint32 value = j * fBlockSize + i;
|
||||||
if (hintMask & (1UL << value))
|
if (hintMask & (1UL << value)) {
|
||||||
SetHighColor(200, 0, 0);
|
// if (value + 1 == fValueHintValue)
|
||||||
else {
|
// SetHighColor(kValueHintBackgroundColor);
|
||||||
|
// else
|
||||||
|
SetHighColor(kHintColor);
|
||||||
|
}else {
|
||||||
if (!showAll)
|
if (!showAll)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -1170,18 +1237,14 @@ SudokuView::_DrawHints(uint32 x, uint32 y)
|
|||||||
void
|
void
|
||||||
SudokuView::Draw(BRect /*updateRect*/)
|
SudokuView::Draw(BRect /*updateRect*/)
|
||||||
{
|
{
|
||||||
// draw one pixel border otherwise not covered
|
|
||||||
// by lines and fields
|
|
||||||
SetLowColor(fBackgroundColor);
|
|
||||||
StrokeRect(Bounds(), B_SOLID_LOW);
|
|
||||||
|
|
||||||
// draw lines
|
// draw lines
|
||||||
|
|
||||||
uint32 size = fField->Size();
|
uint32 size = fField->Size();
|
||||||
|
|
||||||
|
SetLowColor(fBackgroundColor);
|
||||||
SetHighColor(0, 0, 0);
|
SetHighColor(0, 0, 0);
|
||||||
|
|
||||||
float width = fWidth;
|
float width = fWidth - 1;
|
||||||
for (uint32 x = 1; x < size; x++) {
|
for (uint32 x = 1; x < size; x++) {
|
||||||
if (x % fBlockSize == 0) {
|
if (x % fBlockSize == 0) {
|
||||||
FillRect(BRect(width, 0, width + kStrongLineSize,
|
FillRect(BRect(width, 0, width + kStrongLineSize,
|
||||||
@@ -1193,7 +1256,7 @@ SudokuView::Draw(BRect /*updateRect*/)
|
|||||||
width += fWidth;
|
width += fWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
float height = fHeight;
|
float height = fHeight - 1;
|
||||||
for (uint32 y = 1; y < size; y++) {
|
for (uint32 y = 1; y < size; y++) {
|
||||||
if (y % fBlockSize == 0) {
|
if (y % fBlockSize == 0) {
|
||||||
FillRect(BRect(0, height, Bounds().Width(),
|
FillRect(BRect(0, height, Bounds().Width(),
|
||||||
@@ -1209,39 +1272,46 @@ SudokuView::Draw(BRect /*updateRect*/)
|
|||||||
|
|
||||||
for (uint32 y = 0; y < size; y++) {
|
for (uint32 y = 0; y < size; y++) {
|
||||||
for (uint32 x = 0; x < size; x++) {
|
for (uint32 x = 0; x < size; x++) {
|
||||||
|
uint32 value = fField->ValueAt(x, y);
|
||||||
|
|
||||||
|
rgb_color backgroundColor = fBackgroundColor;
|
||||||
|
if (value == fValueHintValue)
|
||||||
|
backgroundColor = kValueHintBackgroundColor;
|
||||||
|
else if (value == 0 && fField->HasHint(x, y, fValueHintValue))
|
||||||
|
backgroundColor = kHintValueHintBackgroundColor;
|
||||||
|
|
||||||
if (((fShowCursor && x == fShowHintX && y == fShowHintY)
|
if (((fShowCursor && x == fShowHintX && y == fShowHintY)
|
||||||
|| (fShowKeyboardFocus && x == fKeyboardX
|
|| (fShowKeyboardFocus && x == fKeyboardX
|
||||||
&& y == fKeyboardY))
|
&& y == fKeyboardY))
|
||||||
&& (fField->FlagsAt(x, y) & kInitialValue) == 0) {
|
&& !fField->IsInitialValue(x, y)) {
|
||||||
// TODO: make color more intense
|
// TODO: make color more intense
|
||||||
SetLowColor(tint_color(fBackgroundColor, B_DARKEN_2_TINT));
|
SetLowColor(tint_color(backgroundColor, B_DARKEN_2_TINT));
|
||||||
FillRect(_Frame(x, y), B_SOLID_LOW);
|
FillRect(_Frame(x, y), B_SOLID_LOW);
|
||||||
} else {
|
} else {
|
||||||
SetLowColor(fBackgroundColor);
|
SetLowColor(backgroundColor);
|
||||||
FillRect(_Frame(x, y), B_SOLID_LOW);
|
FillRect(_Frame(x, y), B_SOLID_LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fShowKeyboardFocus && x == fKeyboardX && y == fKeyboardY)
|
if (fShowKeyboardFocus && x == fKeyboardX && y == fKeyboardY)
|
||||||
_DrawKeyboardFocus();
|
_DrawKeyboardFocus();
|
||||||
|
|
||||||
uint32 value = fField->ValueAt(x, y);
|
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
_DrawHints(x, y);
|
_DrawHints(x, y);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFont(&fFieldFont);
|
SetFont(&fFieldFont);
|
||||||
if ((fField->FlagsAt(x, y) & kInitialValue) != 0)
|
if (fField->IsInitialValue(x, y))
|
||||||
SetHighColor(0, 0, 0);
|
SetHighColor(0, 0, 0);
|
||||||
else {
|
else {
|
||||||
if ((fHintFlags & kMarkInvalid) == 0
|
if ((fHintFlags & kMarkInvalid) == 0
|
||||||
|| fField->ValidMaskAt(x, y) & (1UL << (value - 1))) {
|
|| fField->IsValid(x, y, value)) {
|
||||||
if (fField->IsValueCompleted(value))
|
if (fField->IsValueCompleted(value))
|
||||||
SetHighColor(60, 60, 150);
|
SetHighColor(kValueCompletedColor);
|
||||||
else
|
else
|
||||||
SetHighColor(0, 0, 220);
|
SetHighColor(kValueColor);
|
||||||
} else
|
} else
|
||||||
SetHighColor(200, 0, 0);
|
SetHighColor(kInvalidValueColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
char text[2];
|
char text[2];
|
||||||
|
|||||||
@@ -94,8 +94,12 @@ private:
|
|||||||
void _InvalidateHintField(uint32 x, uint32 y,
|
void _InvalidateHintField(uint32 x, uint32 y,
|
||||||
uint32 hintX, uint32 hintY);
|
uint32 hintX, uint32 hintY);
|
||||||
void _InvalidateField(uint32 x, uint32 y);
|
void _InvalidateField(uint32 x, uint32 y);
|
||||||
|
void _InvalidateValue(uint32 value,
|
||||||
|
bool invalidateHint = false,
|
||||||
|
uint32 x = ~0UL, uint32 y = ~0UL);
|
||||||
void _InvalidateKeyboardFocus(uint32 x, uint32 y);
|
void _InvalidateKeyboardFocus(uint32 x, uint32 y);
|
||||||
void _InsertKey(char rawKey, int32 modifiers);
|
void _InsertKey(char rawKey, int32 modifiers);
|
||||||
|
void _SetValueHintValue(uint32 value);
|
||||||
void _RemoveHint();
|
void _RemoveHint();
|
||||||
bool _GetHintFieldFor(BPoint where, uint32 x,
|
bool _GetHintFieldFor(BPoint where, uint32 x,
|
||||||
uint32 y, uint32& hintX, uint32& hintY);
|
uint32 y, uint32& hintX, uint32& hintY);
|
||||||
@@ -127,6 +131,7 @@ private:
|
|||||||
uint32 fShowHintY;
|
uint32 fShowHintY;
|
||||||
uint32 fLastHintValue;
|
uint32 fLastHintValue;
|
||||||
bool fLastHintValueSet;
|
bool fLastHintValueSet;
|
||||||
|
uint32 fValueHintValue;
|
||||||
uint32 fLastField;
|
uint32 fLastField;
|
||||||
uint32 fKeyboardX;
|
uint32 fKeyboardX;
|
||||||
uint32 fKeyboardY;
|
uint32 fKeyboardY;
|
||||||
|
|||||||
Reference in New Issue
Block a user