diff --git a/Aerofoil/GpMain_Win32.cpp b/Aerofoil/GpMain_Win32.cpp index faee3d8..7766c83 100644 --- a/Aerofoil/GpMain_Win32.cpp +++ b/Aerofoil/GpMain_Win32.cpp @@ -341,7 +341,7 @@ static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue GpKeyboardInputEvent::KeyUnion key; bool isRepeat = ((lParam & 0x40000000) != 0); const GpKeyboardInputEventType_t keyEventType = isRepeat ? GpKeyboardInputEventTypes::kAuto : GpKeyboardInputEventTypes::kDown; - if (!isRepeat && IdentifyVKey(wParam, lParam, subset, key)) + if (IdentifyVKey(wParam, lParam, subset, key)) PostKeyboardEvent(eventQueue, keyEventType, subset, key, static_cast(lParam & 0xffff)); (void)TranslateMessage(msg); @@ -357,21 +357,20 @@ static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue } break; case WM_CHAR: - { - bool isRepeat = ((lParam & 0x4000000) != 0); - const GpKeyboardInputEventType_t keyEventType = isRepeat ? GpKeyboardInputEventTypes::kAutoChar : GpKeyboardInputEventTypes::kDownChar; - GpKeyboardInputEvent::KeyUnion key; - key.m_asciiChar = static_cast(wParam); - PostKeyboardEvent(eventQueue, keyEventType, GpKeyIDSubsets::kASCII, key, (lParam & 0xffff)); - } - break; case WM_UNICHAR: { bool isRepeat = ((lParam & 0x4000000) != 0); const GpKeyboardInputEventType_t keyEventType = isRepeat ? GpKeyboardInputEventTypes::kAutoChar : GpKeyboardInputEventTypes::kDownChar; GpKeyboardInputEvent::KeyUnion key; - key.m_unicodeChar = static_cast(wParam); - PostKeyboardEvent(eventQueue, keyEventType, GpKeyIDSubsets::kUnicode, key, (lParam & 0xffff)); + GpKeyIDSubset_t subset = GpKeyIDSubsets::kASCII; + if (wParam <= 128) + key.m_asciiChar = static_cast(wParam); + else + { + subset = GpKeyIDSubsets::kUnicode; + key.m_unicodeChar = static_cast(wParam); + } + PostKeyboardEvent(eventQueue, keyEventType, subset, key, (lParam & 0xffff)); } break; default: diff --git a/GpApp/About.cpp b/GpApp/About.cpp index 938c700..3f447a4 100644 --- a/GpApp/About.cpp +++ b/GpApp/About.cpp @@ -27,7 +27,7 @@ static void HiLiteOkayButton (DrawSurface *surface); static void UnHiLiteOkayButton (DrawSurface *surface); static void UpdateMainPict (Dialog *); -static int16_t AboutFilter(Dialog *, const TimeTaggedVOSEvent &evt); +static int16_t AboutFilter(Dialog *, const TimeTaggedVOSEvent *evt); static Point okayButtLowerV, okayButtUpperV; @@ -176,17 +176,20 @@ static bool PointIsInDiagonalOkayButton(const Point &pt) //-------------------------------------------------------------- AboutFilter // Dialog filter for the About dialog. -static int16_t AboutFilter(Dialog *dialog, const TimeTaggedVOSEvent &evt) +static int16_t AboutFilter(Dialog *dialog, const TimeTaggedVOSEvent *evt) { bool handledIt = false; int16_t hit = -1; + if (!evt) + return -1; + Window *window = dialog->GetWindow(); DrawSurface *surface = window->GetDrawSurface(); - if (evt.IsKeyDownEvent()) + if (evt->IsKeyDownEvent()) { - switch (PackVOSKeyCode(evt.m_vosEvent.m_event.m_keyboardInputEvent)) + switch (PackVOSKeyCode(evt->m_vosEvent.m_event.m_keyboardInputEvent)) { case PL_KEY_SPECIAL(kEnter): case PL_KEY_NUMPAD_SPECIAL(kEnter): @@ -202,9 +205,9 @@ static int16_t AboutFilter(Dialog *dialog, const TimeTaggedVOSEvent &evt) break; } } - else if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kMouseInput) + else if (evt->m_vosEvent.m_eventType == GpVOSEventTypes::kMouseInput) { - const GpMouseInputEvent &mouseEvt = evt.m_vosEvent.m_event.m_mouseInputEvent; + const GpMouseInputEvent &mouseEvt = evt->m_vosEvent.m_event.m_mouseInputEvent; const Point mousePt = window->MouseToLocal(mouseEvt); if (mouseEvt.m_eventType == GpMouseEventTypes::kDown) diff --git a/GpApp/Events.cpp b/GpApp/Events.cpp index 3b4b838..760d7c2 100644 --- a/GpApp/Events.cpp +++ b/GpApp/Events.cpp @@ -169,7 +169,7 @@ void HandleKeyEvent (const KeyDownStates &keyStates, const GpKeyboardInputEvent const bool optionDown = keyStates.IsSet(PL_KEY_EITHER_SPECIAL(kAlt)); if ((commandDown) && (!optionDown)) - DoMenuChoice(MenuKey(static_cast(theChar))); + DoMenuChoice(MenuKey(theChar)); else { switch (theChar) diff --git a/GpApp/HighScores.cpp b/GpApp/HighScores.cpp index a9a2eb2..97297ef 100644 --- a/GpApp/HighScores.cpp +++ b/GpApp/HighScores.cpp @@ -43,10 +43,10 @@ namespace PortabilityLayer void DrawHighScores (DrawSurface *); void UpdateNameDialog (Dialog *); -Boolean NameFilter (Dialog *, EventRecord *, short *); +int16_t NameFilter (Dialog *dial, const TimeTaggedVOSEvent *evt); void GetHighScoreName (short); void UpdateBannerDialog (Dialog *); -int16_t BannerFilter(Dialog *dialog, const TimeTaggedVOSEvent &evt); +int16_t BannerFilter(Dialog *dialog, const TimeTaggedVOSEvent *evt); void GetHighScoreBanner (void); Boolean OpenHighScoresFile (const VFileSpec &spec, PortabilityLayer::IOStream *&outStream); @@ -446,7 +446,7 @@ void UpdateNameDialog (Dialog *theDialog) //-------------------------------------------------------------- NameFilter // Dialog filter for the "Enter High Score Name" dialog. -int16_t NameFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t NameFilter (Dialog *dial, const TimeTaggedVOSEvent *evt) { short nChars; @@ -457,13 +457,17 @@ int16_t NameFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) keyStroke = false; } - if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kKeyboardInput) + if (!evt) + return -1; + + if (evt->m_vosEvent.m_eventType == GpVOSEventTypes::kKeyboardInput) { - const GpKeyboardInputEvent &kbEvent = evt.m_vosEvent.m_event.m_keyboardInputEvent; + const GpKeyboardInputEvent &kbEvent = evt->m_vosEvent.m_event.m_keyboardInputEvent; if (kbEvent.m_eventType == GpKeyboardInputEventTypes::kDownChar || kbEvent.m_eventType == GpKeyboardInputEventTypes::kAutoChar) { PlayPrioritySound(kTypingSound, kTypingPriority); + keyStroke = true; return -1; // Don't capture, need this to forward to the editbox } else if (kbEvent.m_eventType == GpKeyboardInputEventTypes::kDown) @@ -544,7 +548,7 @@ void UpdateBannerDialog (Dialog *theDialog) //-------------------------------------------------------------- BannerFilter // Dialog filter for the "Enter Message" dialog. -int16_t BannerFilter(Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t BannerFilter(Dialog *dial, const TimeTaggedVOSEvent *evt) { short nChars; @@ -555,13 +559,17 @@ int16_t BannerFilter(Dialog *dial, const TimeTaggedVOSEvent &evt) keyStroke = false; } - if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kKeyboardInput) + if (!evt) + return -1; + + if (evt->m_vosEvent.m_eventType == GpVOSEventTypes::kKeyboardInput) { - const GpKeyboardInputEvent &kbEvent = evt.m_vosEvent.m_event.m_keyboardInputEvent; + const GpKeyboardInputEvent &kbEvent = evt->m_vosEvent.m_event.m_keyboardInputEvent; if (kbEvent.m_eventType == GpKeyboardInputEventTypes::kDownChar || kbEvent.m_eventType == GpKeyboardInputEventTypes::kAutoChar) { PlayPrioritySound(kTypingSound, kTypingPriority); + keyStroke = true; return -1; // Don't capture, need this to forward to the editbox } else if (kbEvent.m_eventType == GpKeyboardInputEventTypes::kDown) diff --git a/GpApp/SelectHouse.cpp b/GpApp/SelectHouse.cpp index 65847d7..eb739a2 100644 --- a/GpApp/SelectHouse.cpp +++ b/GpApp/SelectHouse.cpp @@ -44,7 +44,7 @@ void UpdateLoadDialog (Dialog *); void PageUpHouses (Dialog *); void PageDownHouses (Dialog *); -int16_t LoadFilter (Dialog *, const TimeTaggedVOSEvent &); +int16_t LoadFilter (Dialog *, const TimeTaggedVOSEvent *); void SortHouseList (void); void DoDirSearch (void); @@ -201,13 +201,16 @@ void PageDownHouses (Dialog *theDial) //-------------------------------------------------------------- LoadFilter #ifndef COMPILEDEMO -int16_t LoadFilter(Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t LoadFilter(Dialog *dial, const TimeTaggedVOSEvent *evt) { short screenCount, i, wasIndex; - if (evt.IsKeyDownEvent()) + if (!evt) + return -1; + + if (evt->IsKeyDownEvent()) { - const intptr_t keyCode = PackVOSKeyCode(evt.m_vosEvent.m_event.m_keyboardInputEvent); + const intptr_t keyCode = PackVOSKeyCode(evt->m_vosEvent.m_event.m_keyboardInputEvent); switch (keyCode) { case PL_KEY_SPECIAL(kEnter): @@ -316,20 +319,20 @@ int16_t LoadFilter(Dialog *dial, const TimeTaggedVOSEvent &evt) return -1; } } - else if (evt.IsLMouseDownEvent()) + else if (evt->IsLMouseDownEvent()) { - const GpMouseInputEvent &mouseEvt = evt.m_vosEvent.m_event.m_mouseInputEvent; + const GpMouseInputEvent &mouseEvt = evt->m_vosEvent.m_event.m_mouseInputEvent; - lastWhenClick = evt.m_timestamp - lastWhenClick; + lastWhenClick = evt->m_timestamp - lastWhenClick; lastWhereClick -= Point::Create(mouseEvt.m_x, mouseEvt.m_y); return -1; } - else if (evt.IsLMouseUpEvent()) + else if (evt->IsLMouseUpEvent()) { - const GpMouseInputEvent &mouseEvt = evt.m_vosEvent.m_event.m_mouseInputEvent; + const GpMouseInputEvent &mouseEvt = evt->m_vosEvent.m_event.m_mouseInputEvent; - lastWhenClick = evt.m_timestamp; + lastWhenClick = evt->m_timestamp; lastWhereClick = Point::Create(mouseEvt.m_x, mouseEvt.m_y); return -1; diff --git a/GpApp/Settings.cpp b/GpApp/Settings.cpp index 61dae52..9060c7e 100644 --- a/GpApp/Settings.cpp +++ b/GpApp/Settings.cpp @@ -64,27 +64,27 @@ void SetBrainsToDefaults (Dialog *); void UpdateSettingsBrains (Dialog *); -int16_t BrainsFilter (Dialog *, const TimeTaggedVOSEvent &); +int16_t BrainsFilter (Dialog *, const TimeTaggedVOSEvent *); void DoBrainsPrefs (void); void SetControlsToDefaults (Dialog *); void UpdateControlKeyName (Dialog *); void UpdateSettingsControl (Dialog *); -int16_t ControlFilter (Dialog *, const TimeTaggedVOSEvent &); +int16_t ControlFilter (Dialog *, const TimeTaggedVOSEvent *); void DoControlPrefs (void); void SoundDefaults (Dialog *); void UpdateSettingsSound (Dialog *); void HandleSoundMusicChange (short, Boolean); -int16_t SoundFilter(Dialog *, const TimeTaggedVOSEvent &); +int16_t SoundFilter(Dialog *, const TimeTaggedVOSEvent *); void DoSoundPrefs (void); void DisplayDefaults (void); void FrameDisplayIcon (Dialog *, const PortabilityLayer::RGBAColor &color); void DisplayUpdate (Dialog *); -int16_t DisplayFilter(Dialog *dialog, const TimeTaggedVOSEvent &); +int16_t DisplayFilter(Dialog *dialog, const TimeTaggedVOSEvent *); void DoDisplayPrefs (void); void SetAllDefaults (void); void FlashSettingsButton (DrawSurface *, short); void UpdateSettingsMain (Dialog *); -int16_t PrefsFilter(Dialog *dialog, const TimeTaggedVOSEvent &evt); +int16_t PrefsFilter(Dialog *dialog, const TimeTaggedVOSEvent *evt); void BitchAboutChanges (void); @@ -145,11 +145,14 @@ void UpdateSettingsBrains (Dialog *theDialog) //-------------------------------------------------------------- BrainsFilter -int16_t BrainsFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t BrainsFilter (Dialog *dial, const TimeTaggedVOSEvent *evt) { - if (evt.IsKeyDownEvent()) + if (!evt) + return -1; + + if (evt->IsKeyDownEvent()) { - intptr_t keyCode = PackVOSKeyCode(evt.m_vosEvent.m_event.m_keyboardInputEvent); + intptr_t keyCode = PackVOSKeyCode(evt->m_vosEvent.m_event.m_keyboardInputEvent); switch (keyCode) { @@ -186,6 +189,19 @@ int16_t BrainsFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) } } + if (evt->m_vosEvent.m_eventType == GpVOSEventTypes::kKeyboardInput) + { + const GpKeyboardInputEvent &keyEvent = evt->m_vosEvent.m_event.m_keyboardInputEvent; + if (keyEvent.m_keyIDSubset == GpKeyIDSubsets::kUnicode) + return 0; + if (keyEvent.m_keyIDSubset == GpKeyIDSubsets::kASCII) + { + char asciiChar = (keyEvent.m_key.m_asciiChar); + if (asciiChar < '0' || asciiChar > '9') + return 0; + } + } + return -1; } @@ -220,6 +236,8 @@ void DoBrainsPrefs (void) UpdateSettingsBrains(prefDlg); + prefDlg->GetWindow()->FocusWidget(prefDlg->GetItems()[kMaxFilesItem - 1].GetWidget()); + while (!leaving) { itemHit = prefDlg->ExecuteModal(BrainsFilter); @@ -353,18 +371,21 @@ void UpdateSettingsControl (Dialog *theDialog) //-------------------------------------------------------------- ControlFilter -int16_t ControlFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t ControlFilter (Dialog *dial, const TimeTaggedVOSEvent *evt) { intptr_t wasKeyMap; - if (evt.IsKeyDownEvent()) + if (!evt) + return -1; + + if (evt->IsKeyDownEvent()) { - GpKeyIDSubset_t subset = evt.m_vosEvent.m_event.m_keyboardInputEvent.m_keyIDSubset; + GpKeyIDSubset_t subset = evt->m_vosEvent.m_event.m_keyboardInputEvent.m_keyIDSubset; // Ignore Unicode (for now) and gamepad buttons if (subset == GpKeyIDSubsets::kASCII || subset == GpKeyIDSubsets::kSpecial || subset == GpKeyIDSubsets::kNumPadNumber || subset == GpKeyIDSubsets::kNumPadSpecial || subset == GpKeyIDSubsets::kFKey) { - wasKeyMap = PackVOSKeyCode(evt.m_vosEvent.m_event.m_keyboardInputEvent); + wasKeyMap = PackVOSKeyCode(evt->m_vosEvent.m_event.m_keyboardInputEvent); switch (whichCtrl) { @@ -626,13 +647,16 @@ void HandleSoundMusicChange (short newVolume, Boolean sayIt) //-------------------------------------------------------------- SoundFilter -int16_t SoundFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t SoundFilter (Dialog *dial, const TimeTaggedVOSEvent *evt) { short newVolume; - if (evt.IsKeyDownEvent()) + if (!evt) + return -1; + + if (evt->IsKeyDownEvent()) { - intptr_t keyCode = PackVOSKeyCode(evt.m_vosEvent.m_event.m_keyboardInputEvent); + intptr_t keyCode = PackVOSKeyCode(evt->m_vosEvent.m_event.m_keyboardInputEvent); switch (keyCode) { @@ -885,11 +909,14 @@ void DisplayUpdate (Dialog *theDialog) //-------------------------------------------------------------- DisplayFilter -int16_t DisplayFilter(Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t DisplayFilter(Dialog *dial, const TimeTaggedVOSEvent *evt) { - if (evt.IsKeyDownEvent()) + if (!evt) + return -1; + + if (evt->IsKeyDownEvent()) { - switch (PackVOSKeyCode(evt.m_vosEvent.m_event.m_keyboardInputEvent)) + switch (PackVOSKeyCode(evt->m_vosEvent.m_event.m_keyboardInputEvent)) { case PL_KEY_SPECIAL(kEnter): case PL_KEY_NUMPAD_SPECIAL(kEnter): @@ -1194,14 +1221,17 @@ void UpdateSettingsMain (Dialog *theDialog) //-------------------------------------------------------------- PrefsFilter -int16_t PrefsFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) +int16_t PrefsFilter (Dialog *dial, const TimeTaggedVOSEvent *evt) { short i; Boolean foundHit; - if (evt.IsKeyDownEvent()) + if (!evt) + return -1; + + if (evt->IsKeyDownEvent()) { - intptr_t packedKey = PackVOSKeyCode(evt.m_vosEvent.m_event.m_keyboardInputEvent); + intptr_t packedKey = PackVOSKeyCode(evt->m_vosEvent.m_event.m_keyboardInputEvent); switch (packedKey) { @@ -1226,10 +1256,10 @@ int16_t PrefsFilter (Dialog *dial, const TimeTaggedVOSEvent &evt) return -1; } } - else if (evt.IsLMouseDownEvent()) + else if (evt->IsLMouseDownEvent()) { const Window *window = dial->GetWindow(); - const GpMouseInputEvent &mouseEvent = evt.m_vosEvent.m_event.m_mouseInputEvent; + const GpMouseInputEvent &mouseEvent = evt->m_vosEvent.m_event.m_mouseInputEvent; const Point testPt = Point::Create(mouseEvent.m_x - window->m_wmX, mouseEvent.m_y - window->m_wmY); diff --git a/MacRomanConversion/MacRomanConversion.cpp b/MacRomanConversion/MacRomanConversion.cpp index 0bfef68..78b024d 100644 --- a/MacRomanConversion/MacRomanConversion.cpp +++ b/MacRomanConversion/MacRomanConversion.cpp @@ -57,4 +57,25 @@ namespace MacRoman else return mac_roman_2uni[character - 0x80]; } + + bool FromUnicode(uint8_t &outChar, uint16_t codePoint) + { + if (codePoint < 0x80) + { + outChar = static_cast(codePoint); + return true; + } + else + { + for (unsigned int i = 0; i < 128; i++) + { + if (mac_roman_2uni[i] == codePoint) + { + outChar = static_cast(i + 0x80); + return true; + } + } + return false; + } + } } diff --git a/MacRomanConversion/MacRomanConversion.h b/MacRomanConversion/MacRomanConversion.h index 1bf3412..cafdf15 100644 --- a/MacRomanConversion/MacRomanConversion.h +++ b/MacRomanConversion/MacRomanConversion.h @@ -4,5 +4,6 @@ namespace MacRoman { - uint16_t ToUnicode(uint8_t character); + uint16_t ToUnicode(uint8_t character); + bool FromUnicode(uint8_t &outChar, uint16_t codePoint); } diff --git a/PortabilityLayer/DialogManager.cpp b/PortabilityLayer/DialogManager.cpp index e9c072c..4b422e4 100644 --- a/PortabilityLayer/DialogManager.cpp +++ b/PortabilityLayer/DialogManager.cpp @@ -345,13 +345,19 @@ namespace PortabilityLayer for (;;) { TimeTaggedVOSEvent evt; - if (WaitForEvent(&evt, 1)) + + const bool haveEvent = WaitForEvent(&evt, 1); + + if (window->IsHandlingTickEvents()) + window->OnTick(); + + const int16_t selection = filterFunc(this, haveEvent ? &evt : nullptr); + + if (selection >= 0) + return selection; + + if (haveEvent) { - const int16_t selection = filterFunc(this, evt); - - if (selection >= 0) - return selection; - if (capturingWidget != nullptr) { const WidgetHandleState_t state = capturingWidget->ProcessEvent(evt); @@ -571,7 +577,7 @@ namespace PortabilityLayer void PositionWindow(Window *window, const Rect &rect); Dialog *LoadDialogFromTemplate(int16_t templateResID, const Rect &rect, bool visible, bool hasCloseBox, uint32_t referenceConstant, uint16_t positionSpec, Window *behindWindow, const PLPasStr &title, const DialogTextSubstitutions *substitutions); - static int16_t AlertFilter(Dialog *dialog, const TimeTaggedVOSEvent &evt); + static int16_t AlertFilter(Dialog *dialog, const TimeTaggedVOSEvent *evt); static DialogManagerImpl ms_instance; }; @@ -650,7 +656,7 @@ namespace PortabilityLayer return dialog; } - int16_t DialogManagerImpl::AlertFilter(Dialog *dialog, const TimeTaggedVOSEvent &evt) + int16_t DialogManagerImpl::AlertFilter(Dialog *dialog, const TimeTaggedVOSEvent *evt) { return -1; } diff --git a/PortabilityLayer/MenuManager.cpp b/PortabilityLayer/MenuManager.cpp index ed0465d..04b2965 100644 --- a/PortabilityLayer/MenuManager.cpp +++ b/PortabilityLayer/MenuManager.cpp @@ -132,6 +132,8 @@ namespace PortabilityLayer bool SetItemText(const THandle &menu, unsigned int index, const PLPasStr &str) override; bool IsPointInMenuBar(const Vec2i &point) const override; + + bool FindMenuShortcut(uint16_t &menuID, uint16_t &itemID, uint8_t shortcutChar) override; void MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem) override; void DrawMenuBar() override; @@ -520,6 +522,33 @@ namespace PortabilityLayer return point.m_y >= 0 && static_cast(point.m_y) < kMenuBarHeight; } + + bool MenuManagerImpl::FindMenuShortcut(uint16_t &menuID, uint16_t &itemID, uint8_t shortcutChar) + { + MenuHandle menuH = m_firstMenu; + while (menuH) + { + Menu *menu = *menuH; + + const MenuItem *items = menu->menuItems; + const size_t numItems = menu->numMenuItems; + + for (size_t i = 0; i < numItems; i++) + { + if (items[i].key == shortcutChar) + { + menuID = menu->menuID; + itemID = static_cast(i); + return true; + } + } + + menuH = menu->nextMenu; + } + + return false; + } + void MenuManagerImpl::MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem) { RefreshMenuBarLayout(); diff --git a/PortabilityLayer/MenuManager.h b/PortabilityLayer/MenuManager.h index a65f642..39b1b98 100644 --- a/PortabilityLayer/MenuManager.h +++ b/PortabilityLayer/MenuManager.h @@ -36,6 +36,7 @@ namespace PortabilityLayer virtual bool IsPointInMenuBar(const Vec2i &point) const = 0; + virtual bool FindMenuShortcut(uint16_t &menuID, uint16_t &itemID, uint8_t shortcutChar) = 0; virtual void MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem) = 0; virtual void DrawMenuBar() = 0; diff --git a/PortabilityLayer/PLCore.cpp b/PortabilityLayer/PLCore.cpp index 4f888f8..4cf7db4 100644 --- a/PortabilityLayer/PLCore.cpp +++ b/PortabilityLayer/PLCore.cpp @@ -247,10 +247,19 @@ long MenuSelect(Point point) return (static_cast(menuID) << 16) | (static_cast(menuItem)); } -long MenuKey(int charCode) +long MenuKey(intptr_t charCode) { - PL_NotYetImplemented(); - return PLErrors::kNone; + if (PL_KEY_GET_EVENT_TYPE(charCode) != KeyEventType::KeyEventType_ASCII) + return 0; + + const uint8_t asciiChar = PL_KEY_GET_VALUE(charCode); + + uint16_t menuID; + uint16_t itemID; + if (PortabilityLayer::MenuManager::GetInstance()->FindMenuShortcut(menuID, itemID, asciiChar)) + return (menuID << 16) | (itemID + 1); + + return 0; } long TickCount() @@ -713,8 +722,8 @@ Window::Window() , m_wmY(0) , m_widgets(nullptr) , m_numWidgets(0) - , m_widgetWithFocus(0) - , m_haveFocus(false) + , m_widgetWithFocus(nullptr) + , m_numTickReceivingWidgets(0) { } @@ -762,9 +771,32 @@ bool Window::AddWidget(PortabilityLayer::Widget *widget) m_widgets[m_numWidgets++] = widget; + if (widget->HandlesTickEvents()) + m_numTickReceivingWidgets++; + return true; } +void Window::FocusWidget(PortabilityLayer::Widget *widget) +{ + if (m_widgetWithFocus != widget) + { + assert(widget->GetWindow() == this); + + if (m_widgetWithFocus) + m_widgetWithFocus->LoseFocus(); + + m_widgetWithFocus = widget; + + widget->GainFocus(); + } +} + +PortabilityLayer::Widget *Window::GetWidgetWithFocus() const +{ + return m_widgetWithFocus; +} + void Window::DrawControls() { DrawSurface *surface = GetDrawSurface(); @@ -776,3 +808,17 @@ void Window::DrawControls() widget->DrawControl(surface); } } + +bool Window::IsHandlingTickEvents() +{ + return m_numTickReceivingWidgets > 0; +} + +void Window::OnTick() +{ + if (m_numTickReceivingWidgets == 0) + return; + + for (size_t i = 0; i < m_numWidgets; i++) + m_widgets[i]->OnTick(); +} diff --git a/PortabilityLayer/PLCore.h b/PortabilityLayer/PLCore.h index fa1e65e..0982db2 100644 --- a/PortabilityLayer/PLCore.h +++ b/PortabilityLayer/PLCore.h @@ -82,7 +82,13 @@ struct Window bool AddWidget(PortabilityLayer::Widget *widget); + void FocusWidget(PortabilityLayer::Widget *widget); + PortabilityLayer::Widget *GetWidgetWithFocus() const; + void DrawControls(); + bool IsHandlingTickEvents(); + + void OnTick(); DrawSurface m_surface; // Must be the first item until the immediate mode draw API is completely removed @@ -96,9 +102,9 @@ protected: PortabilityLayer::Widget **m_widgets; size_t m_numWidgets; + size_t m_numTickReceivingWidgets; - size_t m_widgetWithFocus; - bool m_haveFocus; + PortabilityLayer::Widget *m_widgetWithFocus; }; struct DateTimeRec @@ -241,7 +247,7 @@ void SetWTitle(WindowPtr window, const PLPasStr &title); long MenuSelect(Point point); // Breaks into menu select routine (in practice we'll just forward one from the queue?) -long MenuKey(int charCode); +long MenuKey(intptr_t charCode); long TickCount(); short LoWord(Int32 v); diff --git a/PortabilityLayer/PLDialogs.h b/PortabilityLayer/PLDialogs.h index 4c551e9..53d1efe 100644 --- a/PortabilityLayer/PLDialogs.h +++ b/PortabilityLayer/PLDialogs.h @@ -14,7 +14,7 @@ class PLPasStr; struct Control; struct Dialog; -typedef int16_t(*DialogFilterFunc_t)(Dialog *dialog, const TimeTaggedVOSEvent &evt); +typedef int16_t(*DialogFilterFunc_t)(Dialog *dialog, const TimeTaggedVOSEvent *evt); struct DialogTextSubstitutions { diff --git a/PortabilityLayer/PLEditboxWidget.cpp b/PortabilityLayer/PLEditboxWidget.cpp index 31922a8..0aae72c 100644 --- a/PortabilityLayer/PLEditboxWidget.cpp +++ b/PortabilityLayer/PLEditboxWidget.cpp @@ -1,7 +1,12 @@ #include "PLEditboxWidget.h" -#include "PLStandardColors.h" -#include "MemoryManager.h" + #include "FontFamily.h" +#include "InputManager.h" +#include "MacRomanConversion.h" +#include "MemoryManager.h" +#include "PLKeyEncoding.h" +#include "PLStandardColors.h" +#include "PLTimeTaggedVOSEvent.h" #include @@ -14,6 +19,8 @@ namespace PortabilityLayer , m_chars(nullptr) , m_selStartChar(0) , m_selEndChar(0) + , m_hasFocus(false) + , m_caratTimer(0) { } @@ -48,12 +55,63 @@ namespace PortabilityLayer surface->SetForeColor(StdColors::White()); surface->FillRect(innerRect); - surface->SetForeColor(StdColors::Black()); surface->SetSystemFont(12, PortabilityLayer::FontFamilyFlag_None); int32_t ascender = surface->MeasureFontAscender(); + int32_t lineGap = surface->MeasureFontLineGap(); + + const PLPasStr str = this->GetString(); + + assert(m_selStartChar <= str.Length()); + assert(m_selEndChar <= str.Length()); + assert(m_selStartChar <= m_selEndChar); + + const char *strChars = str.Chars(); + + size_t preSelWidth = 0; + size_t selWidth = 0; + //size_t postSelWidth = 0; + if (m_selStartChar > 0) + preSelWidth = surface->MeasureString(PLPasStr(static_cast(m_selStartChar), strChars)); + + if (m_selEndChar > m_selStartChar) + selWidth = surface->MeasureString(PLPasStr(static_cast(m_selEndChar - m_selStartChar), strChars + m_selStartChar)); + + //if (m_selEndChar < str.Length()) + // postSelWidth = surface->MeasureString(PLPasStr(static_cast(m_selEndChar - str.Length()), strChars + m_selEndChar)); Point basePoint = Point::Create(textRect.left, (textRect.top + textRect.bottom + ascender + 1) / 2); + + if (m_hasFocus && selWidth > 0) + { + Rect selRect = Rect::Create(m_rect.top, static_cast(basePoint.h + preSelWidth), m_rect.bottom, m_rect.right); + if (m_selEndChar != str.Length()) + selRect.right = static_cast(basePoint.h + preSelWidth + selWidth); + + selRect = selRect.Intersect(m_rect); + + if (selRect.IsValid()) + { + PortabilityLayer::RGBAColor focusColor = PortabilityLayer::RGBAColor::Create(153, 153, 255, 255); + surface->SetForeColor(focusColor); + surface->FillRect(selRect); + } + } + + surface->SetForeColor(StdColors::Black()); surface->DrawStringConstrained(basePoint, this->GetString(), true, m_rect); + + if (m_hasFocus && m_selEndChar == m_selStartChar && m_caratTimer < kCaratBlinkRate) + { + int16_t caratTop = (textRect.top + textRect.bottom - lineGap + 1) / 2; + int16_t caratBottom = (textRect.top + textRect.bottom + lineGap + 1) / 2; + int16_t caratH = static_cast(basePoint.h + preSelWidth); + Rect caratRect = Rect::Create(caratTop, caratH, caratBottom, caratH + 1); + + caratRect = caratRect.Intersect(m_rect); + + if (caratRect.IsValid()) + surface->FillRect(caratRect); + } } void EditboxWidget::SetString(const PLPasStr &str) @@ -69,6 +127,11 @@ namespace PortabilityLayer DrawControl(surface); } + + if (m_selStartChar > len) + m_selStartChar = len; + if (m_selEndChar > len) + m_selEndChar = len; } PLPasStr EditboxWidget::GetString() const @@ -76,4 +139,181 @@ namespace PortabilityLayer const uint8_t len = static_cast(std::min(255, m_length)); return PLPasStr(len, reinterpret_cast(m_chars)); } + + void EditboxWidget::GainFocus() + { + m_hasFocus = true; + m_selStartChar = 0; + m_selEndChar = this->GetString().Length(); + + if (m_window) + { + DrawSurface *surface = m_window->GetDrawSurface(); + DrawControl(surface); + } + } + + void EditboxWidget::LoseFocus() + { + m_hasFocus = false; + m_selStartChar = 0; + m_selEndChar = 0; + + Redraw(); + } + + WidgetHandleState_t EditboxWidget::ProcessEvent(const TimeTaggedVOSEvent &evt) + { + if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kKeyboardInput) + { + const GpKeyboardInputEvent &keyEvent = evt.m_vosEvent.m_event.m_keyboardInputEvent; + + if (keyEvent.m_eventType == GpKeyboardInputEventTypes::kAutoChar || keyEvent.m_eventType == GpKeyboardInputEventTypes::kDownChar) + { + // Resolve character + bool resolvedChar = false; + uint8_t ch = 0; + + if (keyEvent.m_keyIDSubset == GpKeyIDSubsets::kASCII) + { + ch = static_cast(keyEvent.m_key.m_asciiChar); + resolvedChar = true; + } + else if (keyEvent.m_keyIDSubset == GpKeyIDSubsets::kUnicode) + { + uint32_t codePoint = keyEvent.m_key.m_unicodeChar; + + if (codePoint < 0xffff) + resolvedChar = MacRoman::FromUnicode(ch, keyEvent.m_key.m_unicodeChar); + } + + if (resolvedChar) + { + if (ch >= 0x20 && ch <= 0x7e) + HandleCharacter(ch, keyEvent.m_repeatCount); + else if (ch == '\b') + HandleBackspace(keyEvent.m_repeatCount); + + return WidgetHandleStates::kDigested; + } + } + else if (keyEvent.m_eventType == GpKeyboardInputEventTypes::kAuto || keyEvent.m_eventType == GpKeyboardInputEventTypes::kDown) + { + const KeyDownStates *downStates = PortabilityLayer::InputManager::GetInstance()->GetKeys(); + const bool isShiftHeld = downStates->m_special.Get(GpKeySpecials::kLeftShift) || downStates->m_special.Get(GpKeySpecials::kRightShift); + + if (keyEvent.m_keyIDSubset == GpKeyIDSubsets::kSpecial) + { + if (keyEvent.m_key.m_specialKey == GpKeySpecials::kBackspace) + { + + return WidgetHandleStates::kDigested; + } + else if (keyEvent.m_key.m_specialKey == GpKeySpecials::kDelete) + { + return WidgetHandleStates::kDigested; + } + } + } + } + + return WidgetHandleStates::kIgnored; + } + + void EditboxWidget::Redraw() + { + if (m_window) + { + DrawSurface *surface = m_window->GetDrawSurface(); + DrawControl(surface); + } + } + + bool EditboxWidget::HandlesTickEvents() const + { + return true; + } + + void EditboxWidget::OnTick() + { + if (m_hasFocus) + { + m_caratTimer++; + if (m_caratTimer == kCaratBlinkRate) + Redraw(); + else if (m_caratTimer == kCaratBlinkRate * 2) + { + m_caratTimer = 0; + Redraw(); + } + } + } + + void EditboxWidget::HandleCharacter(uint8_t ch, const uint32_t numRepeatsRequested) + { + const size_t numPostSelChars = m_length - m_selEndChar; + const size_t numSelChars = m_selEndChar - m_selStartChar; + const size_t numPreSelChars = m_selStartChar; + + const size_t lengthWithSelectionRemoved = m_length - numSelChars; + const size_t availableInsertions = m_capacity - lengthWithSelectionRemoved; + + const size_t numInsertions = std::min(availableInsertions, numRepeatsRequested); + + if (m_selEndChar != m_length) + { + size_t moveSize = m_length - m_selEndChar; + uint8_t *moveSrc = m_chars + m_selEndChar; + uint8_t *moveDest = m_chars + m_selStartChar + numInsertions; + if (moveSrc != moveDest) + memmove(moveDest, moveSrc, numPostSelChars); + } + + uint8_t *insertPos = m_chars + m_selStartChar; + + for (size_t r = 0; r < numInsertions; r++) + insertPos[r] = ch; + + // Reset selection + m_selStartChar += numInsertions; + m_selEndChar = m_selStartChar; + + // Reset length; + m_length = numPreSelChars + numInsertions + numPostSelChars; + + m_caratTimer = 0; + Redraw(); + } + + void EditboxWidget::HandleBackspace(uint32_t numRepeatsRequested) + { + const size_t numPostSelChars = m_length - m_selEndChar; + const size_t numSelChars = m_selEndChar - m_selStartChar; + const size_t numPreSelChars = m_selStartChar; + + size_t prefixTrim = numRepeatsRequested; + if (numSelChars != 0) + prefixTrim--; + + if (prefixTrim > numPreSelChars) + prefixTrim = numPreSelChars; + + const size_t prefixKeep = numPreSelChars - prefixTrim; + const size_t suffixKeep = numPostSelChars; + + if (suffixKeep > 0) + { + uint8_t *moveSrc = m_chars + m_selEndChar; + uint8_t *moveDest = m_chars + prefixKeep; + + if (moveSrc != moveDest) + memmove(moveDest, moveSrc, suffixKeep); + } + + m_length = prefixKeep + suffixKeep; + m_selStartChar = m_selEndChar = prefixKeep; + + m_caratTimer = 0; + Redraw(); + } } diff --git a/PortabilityLayer/PLEditboxWidget.h b/PortabilityLayer/PLEditboxWidget.h index 8b30cd3..c4e3e10 100644 --- a/PortabilityLayer/PLEditboxWidget.h +++ b/PortabilityLayer/PLEditboxWidget.h @@ -17,11 +17,30 @@ namespace PortabilityLayer void SetString(const PLPasStr &str) override; PLPasStr GetString() const override; + void GainFocus() override; + void LoseFocus() override; + + WidgetHandleState_t ProcessEvent(const TimeTaggedVOSEvent &evt) override; + + bool HandlesTickEvents() const; + private: + static const unsigned int kCaratBlinkRate = 20; + + void OnTick() override; + void Redraw(); + + void HandleCharacter(uint8_t keyChar, const uint32_t numRepeatsRequested); + void HandleBackspace(const uint32_t numRepeatsRequested); + void HandleForwardDelete(const uint32_t numRepeatsRequested); + uint8_t *m_chars; size_t m_capacity; size_t m_length; size_t m_selStartChar; size_t m_selEndChar; + + bool m_hasFocus; + uint16_t m_caratTimer; }; } diff --git a/PortabilityLayer/PLWidgets.cpp b/PortabilityLayer/PLWidgets.cpp index e4b66dd..77574d4 100644 --- a/PortabilityLayer/PLWidgets.cpp +++ b/PortabilityLayer/PLWidgets.cpp @@ -84,6 +84,19 @@ namespace PortabilityLayer (void)style; } + bool Widget::HandlesTickEvents() const + { + return false; + } + + void Widget::GainFocus() + { + } + + void Widget::LoseFocus() + { + } + void Widget::SetString(const PLPasStr &str) { (void)str; @@ -99,6 +112,11 @@ namespace PortabilityLayer return m_rect; } + Window *Widget::GetWindow() const + { + return m_window; + } + Widget::Widget(const WidgetBasicState &state) : m_rect(state.m_rect) , m_window(state.m_window) @@ -120,6 +138,10 @@ namespace PortabilityLayer { } + void Widget::OnTick() + { + } + void Widget::BaseRelease(void *storage) { PortabilityLayer::MemoryManager::GetInstance()->Release(storage); diff --git a/PortabilityLayer/PLWidgets.h b/PortabilityLayer/PLWidgets.h index 1406dcc..8bafc17 100644 --- a/PortabilityLayer/PLWidgets.h +++ b/PortabilityLayer/PLWidgets.h @@ -64,14 +64,23 @@ namespace PortabilityLayer virtual void SetHighlightStyle(int16_t style); + virtual bool HandlesTickEvents() const; + const Rect &GetRect() const; + Window *GetWindow() const; protected: + friend struct Window; + + virtual void GainFocus(); + virtual void LoseFocus(); + explicit Widget(const WidgetBasicState &state); virtual ~Widget(); virtual void OnEnabledChanged(); virtual void OnStateChanged(); + virtual void OnTick(); static void BaseRelease(void *storage); static void *BaseAlloc(size_t sz);