diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java deleted file mode 100644 index 16330bfcda..0000000000 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ /dev/null @@ -1,352 +0,0 @@ -/** - * Copyright (C) 2017 SIL International. All rights reserved. - */ - -package com.keyman.android; - -import com.keyman.engine.util.DownloadFileUtils; -import com.tavultesoft.kmapro.BuildConfig; -import com.tavultesoft.kmapro.DefaultLanguageResource; -import com.tavultesoft.kmapro.KeymanSettingsActivity; -import com.tavultesoft.kmapro.PreferencesManager; -import com.keyman.engine.KMManager; -import com.keyman.engine.KMManager.KeyboardType; -import com.keyman.engine.KMHardwareKeyboardInterpreter; -import com.keyman.engine.KMManager.SuggestionType; -import com.keyman.engine.KeyboardEventHandler.OnKeyboardEventListener; -import com.keyman.engine.R; -import com.keyman.engine.data.Keyboard; -import com.keyman.engine.util.DependencyUtil; -import com.keyman.engine.util.DependencyUtil.LibraryType; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.graphics.Point; -import android.inputmethodservice.InputMethodService; -import android.text.InputType; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.ExtractedText; -import android.view.inputmethod.ExtractedTextRequest; -import android.view.inputmethod.InputConnection; -import android.widget.FrameLayout; -import android.content.Intent; - -import io.sentry.android.core.SentryAndroid; -import io.sentry.Sentry; - -public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener { - - private static View inputView = null; - private KMHardwareKeyboardInterpreter interpreter = null; - private int inputType = InputType.TYPE_NULL; - private int lastOrientation = Configuration.ORIENTATION_UNDEFINED; - - private static final String TAG = "SystemKeyboard"; - - /** - * Main initialization of the input method component. Be sure to call - * to super class. - */ - @Override - public void onCreate() { - super.onCreate(); - - if (DependencyUtil.libraryExists(LibraryType.SENTRY) && !Sentry.isEnabled()) { - Log.d(TAG, "Initializing Sentry"); - SentryAndroid.init(getApplicationContext(), options -> { - options.setEnableAutoSessionTracking(false); - options.setRelease(com.tavultesoft.kmapro.BuildConfig.KEYMAN_VERSION_GIT_TAG); - options.setEnvironment(com.tavultesoft.kmapro.BuildConfig.KEYMAN_VERSION_ENVIRONMENT); - }); - } - if (BuildConfig.DEBUG) { - KMManager.setDebugMode(true); - } - KMManager.addKeyboardEventListener(this); - Context context = getApplicationContext(); - KMManager.initialize(context, KeyboardType.KEYBOARD_TYPE_SYSTEM); - DefaultLanguageResource.install(context); - interpreter = new KMHardwareKeyboardInterpreter(context, KeyboardType.KEYBOARD_TYPE_SYSTEM); - KMManager.setInputMethodService(this); // for HW interface - - SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); - KMManager.SpacebarText spacebarText = KMManager.SpacebarText.fromString(prefs.getString(KeymanSettingsActivity.spacebarTextKey, KMManager.SpacebarText.LANGUAGE_KEYBOARD.toString())); - KMManager.setSpacebarText(spacebarText); - - // Set the system keyboard HTML banner - BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM); - - boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsActivity.hapticFeedbackKey, false); - KMManager.setHapticFeedback(mayHaveHapticFeedback); - - // Checking for updates should never be allowed to crash the keyboard. - // Just silently fail if this occurs. - if(DownloadFileUtils.getDownloadManager(this) != null) { - // Will try to emit a toast if it fails - i.e., is not silent. - KMManager.executeResourceUpdate(this); - } - } - - @Override - public void onDestroy() { - inputView = null; - KMManager.removeKeyboardEventListener(this); - interpreter = null; // Throw it away, since we're losing our application's context. - KMManager.onDestroy(); - super.onDestroy(); - } - - /** - * This is the point where you can do all of your UI initialization. It - * is called after creation and any configuration change. - */ - @Override - public void onInitializeInterface() { - super.onInitializeInterface(); - } - - /** - * Called by the framework when your view for creating input needs to - * be generated. This will be called the first time your input method - * is displayed, and every time it needs to be re-created such as due to - * a configuration change. - */ - @Override - public View onCreateInputView() { - if (inputView == null) { - inputView = KMManager.createInputView(this); - } - - ViewGroup parent = (ViewGroup) inputView.getParent(); - if (parent != null) { - parent.removeView(inputView); - } - - return inputView; - } - - /** - * Deal with the editor reporting movement of its cursor. - */ - @Override - public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { - super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd); - KMManager.updateSelectionRange(KMManager.KeyboardType.KEYBOARD_TYPE_SYSTEM); - } - - private void sendCurrentFontName() { - Keyboard keyboardInfo = KMManager.getCurrentKeyboardInfo(this); - if (keyboardInfo != null) { - String fontName = keyboardInfo.getFont(); - - Intent intent; - if (BuildConfig.DEBUG) { - intent = new Intent("com.tavultesoft.kmapro.debug.keyboard_changed"); - } else { - intent = new Intent("com.tavultesoft.kmapro.keyboard_changed"); - } - intent.putExtra("fontName", fontName); - sendBroadcast(intent); - } - } - - /** - * This is the main point where we do our initialization of the input method - * to begin operating on an application. At this point we have been - * bound to the client, and are now receiving all of the detailed information - * about the target of our edits. - */ - @Override - public void onStartInput(EditorInfo attribute, boolean restarting) { - attribute.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN; - super.onStartInput(attribute, restarting); - KMManager.onStartInput(attribute, restarting); - KMManager.resetContext(KeyboardType.KEYBOARD_TYPE_SYSTEM); - - Context appContext = getApplicationContext(); - // Temporarily disable predictions on certain fields (e.g. hidden password field or numeric) - inputType = attribute.inputType; - KMManager.setPredictionsSuspended(inputType, KeyboardType.KEYBOARD_TYPE_SYSTEM); - if (KMManager.getPredictionsSuspended(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { - KMManager.setBannerOptions(false); - // Set the system keyboard HTML banner - BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM); - } else if (KMManager.isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)){ - // Check if predictions needs to be re-enabled per Settings preference - Keyboard kbInfo = KMManager.getCurrentKeyboardInfo(appContext); - if (kbInfo != null) { - String langId = kbInfo.getLanguageID(); - SharedPreferences prefs = appContext.getSharedPreferences(appContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); - int maySuggest = prefs.getInt(KMManager.getLanguageAutoCorrectionPreferenceKey(langId), KMManager.KMDefault_Suggestion); - // Enable banner if maySuggest is not SuggestionType.SUGGESTIONS_DISABLED (0) - KMManager.setBannerOptions(maySuggest != SuggestionType.SUGGESTIONS_DISABLED.toInt()); - } else { - KMManager.setBannerOptions(false); - } - } - - // Determine special handling for ENTER key - KMManager.setEnterMode(attribute.imeOptions, inputType); - - InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); - /* - We do sometimes receive null `icText.text`, even though - getExtractedText() docs does not list this as a possible - return value, so we test for that as well (#11479) - */ - if (icText != null && icText.text != null) { - // Update the text selection but ignore the returned statuses - KMManager.updateText(KeyboardType.KEYBOARD_TYPE_SYSTEM, icText.text.toString()); - KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM); - } - } - - // Select numeric layer if applicable - if (KMManager.isNumericField(inputType)) { - KMManager.setNumericLayer(KeyboardType.KEYBOARD_TYPE_SYSTEM); - } - - if (KMManager.isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)) { - sendCurrentFontName(); - } - } - - @Override - public void onStartInputView(EditorInfo attribute, boolean restarting) { - super.onStartInputView(attribute, restarting); - setInputView(onCreateInputView()); - - // Update the input type - inputType = attribute.inputType; - } - - @Override - public void onUpdateExtractingVisibility(EditorInfo ei) { - super.onUpdateExtractingVisibility(ei); - } - - @Override - public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) { - super.onConfigureWindow(win, isFullscreen, isCandidatesOnly); - - // We don't currently use isFullscreen or isCandidatesOnly; we always want to MATCH_PARENT, - // unlike the default for height which is WRAP_CONTENT. We then adjust the touchable area - // in `onCalculateInsets` - win.setLayout(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); - } - - @Override - public void onComputeInsets(InputMethodService.Insets outInsets) { - super.onComputeInsets(outInsets); - - // This method (likely) includes the IME equivalent to `onResume` for `Activity`-based classes, - // making it an important time to detect orientation changes. - Context appContext = getApplicationContext(); - int newOrientation = KMManager.getOrientation(appContext); - if(newOrientation != lastOrientation) { - lastOrientation = newOrientation; - Configuration newConfig = this.getResources().getConfiguration(); - KMManager.onConfigurationChanged(newConfig); - } - - // Update the touchable region of the Keyman keyboard - Point size = KMManager.getWindowSize(getApplicationContext()); - - int inputViewHeight = 0; - if (inputView != null) { - inputViewHeight = inputView.getHeight(); - } - - int navigationHeight = KMManager.getNavigationBarHeight(this, KeyboardType.KEYBOARD_TYPE_SYSTEM); - int bannerHeight = KMManager.getBannerHeight(this); - int kbHeight = KMManager.getKeyboardHeight(this); - outInsets.contentTopInsets = inputViewHeight - bannerHeight - kbHeight - navigationHeight; - outInsets.visibleTopInsets = outInsets.contentTopInsets; - outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION; - outInsets.touchableRegion.set(0, outInsets.contentTopInsets, size.x, size.y); - } - - @Override - public void onKeyboardLoaded(KeyboardType keyboardType) { - // Do nothing - } - - @Override - public void onKeyboardChanged(String newKeyboard) { - // Refresh banner theme - BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM); - KMManager.showSystemKeyboard(); - sendCurrentFontName(); - } - - @Override - public void onKeyboardShown() { - // Refresh banner theme - BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM); - } - - @Override - public void onKeyboardDismissed() { - // Do nothing - } - - @Override - public boolean onEvaluateInputViewShown() { - // On Android API 36+, the OSK defaults to not appearing when a physical keyboard is connected. - // If the default implementation returns true, recommend honoring it - // Reference: https://android.googlesource.com/platform/frameworks/base/+/7b739a8%5E%21/ - if (super.onEvaluateInputViewShown()) { - return true; - }; - - Context context = getApplicationContext(); - SharedPreferences prefs = context.getSharedPreferences(PreferencesManager.kma_prefs_name, Context.MODE_PRIVATE); - boolean showOSK = prefs.getBoolean(KeymanSettingsActivity.oskWithPhysicalKeyboardKey, false); - return showOSK; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - // Determine if Physical keystroke should be passed off - if (inputType == InputType.TYPE_NULL) { - return false; // Revert to default handling - } - - if (event.getAction() == KeyEvent.ACTION_DOWN) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - // Dismiss the keyboard if currently shown - if (isInputViewShown()) { - KMManager.hideSystemKeyboard(); - return true; - } - break; - } - } - - return interpreter.onKeyDown(keyCode, event); // if false, will revert to default handling. - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return interpreter.onKeyUp(keyCode, event); - } - - @Override - public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { - return interpreter.onKeyMultiple(keyCode, count, event); - } - - @Override - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - return interpreter.onKeyLongPress(keyCode, event); - } -} \ No newline at end of file diff --git a/android/KMAPro/kMAPro/src/main/res/layout/nav_divider.xml b/android/KMAPro/kMAPro/src/main/res/layout/nav_divider.xml index 037e4e3b27..192100c926 100644 --- a/android/KMAPro/kMAPro/src/main/res/layout/nav_divider.xml +++ b/android/KMAPro/kMAPro/src/main/res/layout/nav_divider.xml @@ -7,5 +7,4 @@ android:orientation="vertical" android:layout_marginBottom="2dp" android:background="@color/keyman_orange"> - diff --git a/android/KMAPro/kMAPro/src/main/res/menu/nav_menu.xml b/android/KMAPro/kMAPro/src/main/res/menu/nav_menu.xml index a750588f65..76b7cfee8d 100644 --- a/android/KMAPro/kMAPro/src/main/res/menu/nav_menu.xml +++ b/android/KMAPro/kMAPro/src/main/res/menu/nav_menu.xml @@ -8,7 +8,7 @@ - + android:title="Preferences"> MainThreadID); -end; - -procedure TframeCEFHost.StartClose; -begin - AssertVclThread; - Visible := False; - FIsClosing := True; - cef.CloseBrowser(True); -end; - -procedure TframeCEFHost.StartShutdown(CompletionHandler: TShutdownCompletionHandlerEvent); -begin - AssertVclThread; - OutputDebugString(PChar('TframeCEFHost.StartShutdown')); - FIsClosing := True; - FShutdownCompletionHandler := CompletionHandler; - - // If the browser has not been initialized, we'll not get the close signal, so we - // post it to occur on next idle. - if cef.Initialized - then cef.CloseBrowser(False) - else PostMessage(FCallbackWnd, CEF_AFTERDESTROY, 0, 0); -end; - -procedure TframeCEFHost.FormCreate(Sender: TObject); -begin - AssertVclThread; - inherited; - FApplicationHandle := Application.Handle; // take a copy to avoid Vcl thread mismatches in cef callbacks - - // We need our own window handle for events, because VCL windows can be destroyed - // and recreated at any time. With our own handle, we can guarantee the lifetime - // of it across threads. - FCallbackWnd := AllocateHWnd(CallbackWndProc); - FInitializeCEF.RegisterWindow(Self); -// CreateBrowser; -end; - -procedure TframeCEFHost.FormDestroy(Sender: TObject); -begin - AssertVclThread; -// OutputDebugString(PChar('TframeCEFHost.FormDestroy')); - inherited; - FInitializeCEF.UnregisterWindow(Self); - DeallocateHWnd(FCallbackWnd); -end; - -procedure TframeCEFHost.FormShow(Sender: TObject); -begin - AssertVclThread; - inherited; - PostMessage(FCallbackWnd, CEF_SHOW, 0, 0); -end; - -function TframeCEFHost.GetDebugInfo: string; -begin - Result := FNextURL; - if Assigned(Owner) then Result := Owner.ClassName+':'+Result; -end; - -function TframeCEFHost.HasFocus: Boolean; -begin - AssertVclThread; - Result := Assigned(cefwp) and cefwp.HandleAllocated and IsChild(cefwp.Handle, GetFocus); -end; - -procedure TframeCEFHost.Handle_CEF_SHOW(var message: TMessage); -begin - AssertVclThread; - CreateBrowser; -end; - -procedure TframeCEFHost.Handle_CEF_TITLECHANGE(var message: TMessage); -var - p: PCEFTitleChangeEventData; -begin - AssertVclThread; - p := PCEFTitleChangeEventData(message.LParam); - - if Assigned(FOnTitleChange) then - FOnTitleChange(Self, p.title); - - FreeMem(p); -end; - -procedure TframeCEFHost.cefWidgetCompMsg(Sender: TObject; var aMessage: TMessage; - var aHandled: Boolean); -begin - AssertCefThread; - if aMessage.Msg = WM_SETFOCUS then - PostMessage(FCallbackWnd, CEF_SETFOCUS, 0, 0); -end; - -procedure TframeCEFHost.CreateBrowser; -begin - AssertVclThread; - FIsCreated := True; - tmrCreateBrowser.Enabled := not cef.CreateBrowser(cefwp); -end; - -procedure TframeCEFHost.Navigate(const url: string); -begin - AssertVclThread; - FNextURL := url; - Navigate; -end; - -procedure TframeCEFHost.Navigate; -begin - AssertVclThread; - if FNextURL = '' then - Exit; - - if not FIsCreated then - begin - cef.DefaultUrl := FNextURL; - FNextURL := ''; - Exit; - end; - - if not cef.Initialized then - begin - // After initialization, refresh will happen - // See cefAfterCreated - Exit; - end; - - cef.LoadURL(FNextURL); -end; - -procedure TframeCEFHost.SetFocus; -begin - AssertVclThread; - if not FIsClosing and Assigned(cefwp) and Assigned(cef) and cefwp.CanFocus then - begin - GetParentForm(Self).ActiveControl := Self; - cef.SetFocus(True); - end; -end; - -procedure TframeCEFHost.CallbackWndProc(var Message: TMessage); -begin - AssertVclThread; - case Message.Msg of - CEF_DESTROY: Handle_CEF_DESTROY(Message); - CEF_AFTERDESTROY: Handle_CEF_AFTERDESTROY(Message); - CEF_AFTERCREATE: Handle_CEF_AFTERCREATE(Message); - CEF_SHOW: Handle_CEF_SHOW(Message); - CEF_LOADEND: Handle_CEF_LOADEND(Message); - CEF_KEYEVENT: Handle_CEF_KEYEVENT(Message); - CEF_BEFOREBROWSE: Handle_CEF_BEFOREBROWSE(Message); - CEF_TITLECHANGE: Handle_CEF_TITLECHANGE(Message); - CEF_COMMAND: Handle_CEF_COMMAND(Message); - CEF_SETFOCUS: Handle_CEF_SETFOCUS(Message); - CEF_LOADINGSTATECHANGE: Handle_CEF_LOADINGSTATECHANGE(Message); - end; - - if Self <> nil then - Message.Result := DefWindowProc(FCallbackWnd, Message.Msg, Message.WParam, Message.LParam); -end; - -procedure TframeCEFHost.Handle_CEF_AFTERCREATE(var Message: TMessage); -begin - AssertVclThread; - Navigate; - if Assigned(FOnAfterCreated) then - FOnAfterCreated(Self); -end; - -procedure TframeCEFHost.cefAfterCreated(Sender: TObject; - const browser: ICefBrowser); -begin - AssertCefThread; - PostMessage(FCallbackWnd, CEF_AFTERCREATE, 0, 0); -end; - -procedure TframeCEFHost.Handle_CEF_AFTERDESTROY(var Message: TMessage); -begin - AssertVclThread; - if Assigned(FShutdownCompletionHandler) then - begin - FShutdownCompletionHandler(Self); - FShutdownCompletionHandler := nil; - end; -end; - -function IsLocalURL(URL: string): Boolean; -begin - Result := - URL.StartsWith('file:') or - URL.StartsWith('/') or - URL.StartsWith('http://localhost:') or - URL.StartsWith('http://localhost/') or - URL.StartsWith('http://127.0.0.1:') or - URL.StartsWith('http://127.0.0.1/'); -end; - -function IsPDFURL(URL: string): Boolean; -begin - Result := LowerCase(URL).EndsWith('.pdf'); -end; - -procedure TframeCEFHost.Handle_CEF_BEFOREBROWSE(var message: TMessage); -var - params: TStringList; - url: string; - isMain, isPopup, wasHandled, - shouldOpenUrlIfNotHandled: Boolean; -begin - AssertVclThread; - - params := TStringList(message.LParam); - url := params[0]; - wasHandled := (message.WParam and 1) = 1; - shouldOpenUrlIfNotHandled := (message.WParam and 2) = 2; - isPopup := (message.WParam and 4) = 4; - isMain := (message.WParam and 8) = 8; - - if wasHandled then - begin - if Assigned(FOnBeforeBrowseEx) then - begin - FOnBeforeBrowseEx(Self, url, isMain, isPopup, wasHandled); - end - else if Assigned(FOnBeforeBrowse) then - begin - FOnBeforeBrowse(Self, url, isPopup, wasHandled); - end; - - if FShouldOpenRemoteUrlsInBrowser and (not IsLocalURL(URL) or IsPDFURL(URL)) then - begin - {$MESSAGE HINT 'Refactor how remote URLs are handled'} - // TODO: refactor links - TUtilExecute.URL(url); - end; - end - else if shouldOpenUrlIfNotHandled then - cef.LoadURL(url); - - params.Free; -end; - -procedure TframeCEFHost.Handle_CEF_COMMAND(var message: TMessage); -var - params: TStringList; - command: string; -begin - AssertVclThread; - params := TStringList(message.LParam); - - if Assigned(FOnCommand) then - begin - params.Delete(0); // url; not really used currently - command := params[0]; - params.Delete(0); - FOnCommand(Self, command, params); - end; - - params.Free; -end; - -procedure TframeCEFHost.cefBeforeBrowse(Sender: TObject; - const browser: ICefBrowser; const frame: ICefFrame; - const request: ICefRequest; user_gesture, isRedirect: Boolean; - out Result: Boolean); -begin - AssertCefThread; - - DoBeforeBrowse(request.Url, frame.IsMain, False, False, Result); -end; - -procedure TframeCEFHost.DoBeforeBrowse(const url: string; isMain, isPopup, ShouldOpenUrlIfNotHandled: Boolean; out Handled: Boolean); -var - params: TStringList; - wParam: DWORD; -begin - AssertCefThread; - - Handled := False; - - if Assigned(FOnBeforeBrowseExSync) then - begin - FOnBeforeBrowseExSync(Self, url, isMain, isPopup, Handled); - end - else if Assigned(FOnBeforeBrowseSync) then - begin - FOnBeforeBrowseSync(Self, url, isPopup, Handled); - end; - - if not Handled and GetParamsFromURL(Url, params) then - begin - Handled := True; - // Use OnCommand for keyman: URLs - params.Insert(0, Url); - PostMessage(FCallbackWnd, CEF_COMMAND, 0, LPARAM(params)); - end - else - begin - // Use OnBeforeBrowse for other URLs - if not Handled and FShouldOpenRemoteUrlsInBrowser and (not IsLocalURL(URL) or IsPDFURL(URL)) then - Handled := True; - - params := TStringList.Create; - params.Add(Url); - wParam := 0; - if Handled then - wParam := wParam or 1; - if ShouldOpenUrlIfNotHandled then - wParam := wParam or 2; - if isPopup then - wParam := wParam or 4; - if isMain then - wParam := wParam or 8; - PostMessage(FCallbackWnd, CEF_BEFOREBROWSE, wParam, LPARAM(params)); - end; -end; - -procedure TframeCEFHost.cefBeforeClose(Sender: TObject; const browser: ICefBrowser); -begin - AssertCefThread; - PostMessage(FCallbackWnd, CEF_AFTERDESTROY, 0, 0); -end; - -procedure TframeCEFHost.cefClose(Sender: TObject; const browser: ICefBrowser; - var aAction: TCefCloseBrowserAction); -begin - AssertCefThread; - PostMessage(FCallbackWnd, CEF_DESTROY, 0, 0); - aAction := cbaClose; -end; - -procedure TframeCEFHost.Handle_CEF_DESTROY(var Message: TMessage); -begin - AssertVclThread; - if Assigned(cefwp) then - cefwp.DestroyChildWindow; - FreeAndNil(cefwp); -end; - -procedure TframeCEFHost.tmrCreateBrowserTimer(Sender: TObject); -begin - AssertVclThread; - tmrCreateBrowser.Enabled := False; - CreateBrowser; -end; - -procedure TframeCEFHost.cefLoadEnd(Sender: TObject; const browser: ICefBrowser; - const frame: ICefFrame; httpStatusCode: Integer); -begin - AssertCefThread; - PostMessage(FCallbackWnd, CEF_LOADEND, WPARAM(httpStatusCode), 0); -end; - -procedure TframeCEFHost.cefLoadingStateChange(Sender: TObject; - const browser: ICefBrowser; isLoading, canGoBack, canGoForward: Boolean); -var - v: Integer; -begin - AssertCefThread; - v := 0; - if isLoading then v := v or CEF_LOADINGSTATECHANGE_ISLOADING; - if canGoBack then v := v or CEF_LOADINGSTATECHANGE_CANGOBACK; - if canGoForward then v := v or CEF_LOADINGSTATECHANGE_CANGOFORWARD; - PostMessage(FCallbackWnd, CEF_LOADINGSTATECHANGE, v, 0); -end; - -procedure TframeCEFHost.Handle_CEF_KEYEVENT(var message: TMessage); -var - p: PCEFHostKeyEventData; - wasHandled: Boolean; - wasShortcut: Boolean; -begin - p := PCEFHostKeyEventData(message.LParam); - - if p.event.windows_key_code = VK_F1 then - begin - if Assigned(FOnHelpTopic) then FOnHelpTopic(Self); // TODO: frmKeymanDeveloper.HelpTopic(Self) - end - else if p.event.windows_key_code = VK_F12 then - begin - cef.ShowDevTools(Point(Low(Integer),Low(Integer)), nil); - end - else if Assigned(FOnKeyEvent) then - begin - wasHandled := message.WParamLo <> 0; - wasShortcut := message.WParamHi <> 0; - FOnKeyEvent(Self, p^, wasShortcut, wasHandled); - end; - FreeMem(p); -end; - -procedure TframeCEFHost.Handle_CEF_LOADEND(var message: TMessage); -begin - if csDestroying in ComponentState then - Exit; - - // The focus needs to be set again for key events to - // be passed to the CEF window, even if it appears - // to already be focused to the expected window. - if IsChild(Handle, GetFocus) then - SetFocus; - - if Assigned(FOnLoadEnd) then - FOnLoadEnd(Self); -end; - -procedure TframeCEFHost.Handle_CEF_LOADINGSTATECHANGE(var message: TMessage); -begin - if csDestroying in ComponentState then - Exit; - AssertVclThread; - - if Assigned(FOnLoadingStateChange) then - FOnLoadingStateChange(Self, - (message.WParam and CEF_LOADINGSTATECHANGE_ISLOADING) <> 0, - (message.WParam and CEF_LOADINGSTATECHANGE_CANGOBACK) <> 0, - (message.WParam and CEF_LOADINGSTATECHANGE_CANGOFORWARD) <> 0); -end; - -procedure TframeCEFHost.Handle_CEF_SETFOCUS(var message: TMessage); -begin - AssertVclThread; - if Assigned(cefwp) and cefwp.Visible and cefwp.CanFocus then - GetParentForm(cefwp).ActiveControl := cefwp; -end; - -procedure TframeCEFHost.cefPreKeyEvent(Sender: TObject; - const browser: ICefBrowser; const event: PCefKeyEvent; osEvent: TCefEventHandle; - out isKeyboardShortcut, Result: Boolean); -var - p: PCEFHostKeyEventData; -begin - AssertCefThread; - Result := False; - - p := AllocMem(Sizeof(TCEFHostKeyEventData)); - p.browserid := browser.Identifier; - p.event := event^; - if Assigned(osEvent) then - p.osEvent := osEvent^; - - if event.kind in [TCefKeyEventType.KEYEVENT_KEYDOWN, TCefKeyEventType.KEYEVENT_RAWKEYDOWN] then - begin - if Assigned(FOnPreKeySyncEvent) then - begin - FOnPreKeySyncEvent(Self, p^, isKeyboardShortcut, Result); - end; - - if not Result then // only run this if the prekeysyncevent didn't swallow the keystroke - begin - if event.windows_key_code = VK_F1 then - begin - isKeyboardShortcut := True; - Result := True; - end - else if event.windows_key_code = VK_F12 then - begin - isKeyboardShortcut := True; - Result := True; - end - else if event.windows_key_code <> VK_CONTROL then - begin - if SendMessage(FApplicationHandle, CM_APPKEYDOWN, event.windows_key_code, 0) = 1 then - begin - isKeyboardShortcut := True; - Result := True; - end; - end; - end; - - PostMessage(FCallbackWnd, CEF_KEYEVENT, MAKELONG(WORD(Result), WORD(isKeyboardShortcut)), LPARAM(p)); - end; -end; - - -procedure TframeCEFHost.cefBeforePopup(Sender: TObject; - const browser: ICefBrowser; const frame: ICefFrame; const targetUrl, - targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition; - userGesture: Boolean; const popupFeatures: TCefPopupFeatures; - var windowInfo: TCefWindowInfo; var client: ICefClient; - var settings: TCefBrowserSettings; - var extra_info: ICefDictionaryValue; - var noJavascriptAccess, Result: Boolean); -begin - AssertCefThread; - DoBeforeBrowse(targetUrl, frame.IsMain, True, True, Result); -end; - -procedure TframeCEFHost.cefRunContextMenu(Sender: TObject; - const browser: ICefBrowser; const frame: ICefFrame; - const params: ICefContextMenuParams; const model: ICefMenuModel; - const callback: ICefRunContextMenuCallback; var aResult: Boolean); -begin - AssertCefThread; - // Return FALSE to show default context menu - aResult := not FShouldShowContextMenu and (GetKeyState(VK_SHIFT) >= 0); -end; - -procedure TframeCEFHost.cefSetFocus(Sender: TObject; const browser: ICefBrowser; - source: TCefFocusSource; out Result: Boolean); -begin - Result := source = FOCUS_SOURCE_NAVIGATION; -end; - -procedure TframeCEFHost.cefTitleChange(Sender: TObject; - const browser: ICefBrowser; const title: ustring); -var - p: PCEFTitleChangeEventData; -begin - AssertCefThread; - - p := AllocMem(SizeOf(TCEFTitleChangeEventData)); - p.browserid := browser.Identifier; - p.title := title; - - PostMessage(FCallbackWnd, CEF_TITLECHANGE, 0, LPARAM(p)); -end; - -procedure TframeCEFHost.WMEnterMenuLoop(var aMessage: TMessage); -begin - AssertVclThread; - inherited; - if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then GlobalCEFApp.OsmodalLoop := True; -end; - -procedure TframeCEFHost.WMExitMenuLoop(var aMessage: TMessage); -begin - AssertVclThread; - inherited; - if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then GlobalCEFApp.OsmodalLoop := False; -end; - -procedure TframeCEFHost.WMMove(var aMessage: TWMMove); -begin - AssertVclThread; - inherited; - if cef <> nil then cef.NotifyMoveOrResizeStarted; -end; - -procedure TframeCEFHost.WMMoving(var aMessage: TMessage); -begin - AssertVclThread; - inherited; - if cef <> nil then cef.NotifyMoveOrResizeStarted; -end; - -end.