在minimo中用方向键切换焦点
转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>
用XUL编写的minimo实在太慢了,起动过程足足要40秒钟,我对它已经完全失去信心了。前几天发现用GTK+编写的minimo则要快得多,第二次起动仅仅花了7秒钟时间,这要让我精神大振,可惜mozilla中所带的GTK+编写的minimo已经没有人维护了,代码很凌乱,很多功能也都不支持。
最后我们决定用GTK+重新编写minimo,而界面完全模仿Windows Mobile5.0 中的IE。经过一周时间的努力,基本功能已经完成,只是还有少数功能需要完善。其中方向键切换焦点就是其中的问题之一,在firefox里,默认情况是不能用方向键在网页中切换焦点的,切换焦点只能用tab键,而普通手机根本没有tab键,我们希望能用方向键代替tab实现焦点切换。
以前在阅读mozilla代码时,我记得有个扩展可以实现这个功能,不过忘了具体是哪个扩展了,今天在extension目录里找了一下,很快确认是spatialnavigation扩展,spatial取得真不怎么样,我还以为是什么3D相关的东西的呢,还好navigation给了一些提示。
Spatialnavigation的实现原理很简单:
NS_IMETHODIMP nsSpatialNavigation::Init(nsIDOMWindow *aWindow) { mTopWindow = aWindow;
nsCOMPtr<nsIDOM3EventTarget> target; nsCOMPtr<nsIDOMEventGroup> systemGroup; nsresult rv = getEventTargetFromWindow(aWindow, getter_AddRefs(target), getter_AddRefs(systemGroup)); if (NS_FAILED(rv)) return rv; target->AddGroupedEventListener(NS_LITERAL_STRING("keypress"), static_cast<nsIDOMKeyListener*>(this), PR_FALSE, systemGroup); return NS_OK; }
|
这个函数向DOMWindow注册按键事件,当有按键发生时,nsSpatialNavigation相关虚函数就会被调用。
NS_IMETHODIMP nsSpatialNavigation::KeyDown(nsIDOMEvent* aEvent) { nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID); PRBool enabled; prefBranch->GetBoolPref("snav.enabled", &enabled); if (!enabled) // this doesn't work. wtf? if (!mService->mEnabled) return NS_OK;
nsCOMPtr<nsIDOMNSUIEvent> uiEvent(do_QueryInterface(aEvent)); if (uiEvent) { // If a web page wants to use the keys mapped to our // move, they have to use evt.preventDefault() after // they get the key
PRBool preventDefault; uiEvent->GetPreventDefault(&preventDefault); if (preventDefault) return NS_OK; }
PRInt32 formControlType = -1; // check to see if we are in a text field. // based on nsTypeAheadFind. //nsEvent should be renamed. nsCOMPtr<nsIDOMNSEvent> nsEvent = do_QueryInterface(aEvent); if (!nsEvent) return NS_ERROR_FAILURE; nsCOMPtr<nsIDOMEventTarget> domEventTarget; nsEvent->GetOriginalTarget(getter_AddRefs(domEventTarget)); nsCOMPtr<nsIContent> targetContent = do_QueryInterface(domEventTarget);
if (targetContent->IsNodeOfType(nsINode::eXUL)) return NS_OK; if (targetContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) { nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(targetContent)); formControlType = formControl->GetType(); if (mService->mIgnoreTextFields) { if (formControlType == NS_FORM_TEXTAREA || formControlType == NS_FORM_INPUT_TEXT || formControlType == NS_FORM_INPUT_PASSWORD || formControlType == NS_FORM_INPUT_FILE) { return NS_OK; } } } else if (mService->mIgnoreTextFields && targetContent->IsNodeOfType(nsINode::eHTML)) { // Test for isindex, a deprecated kind of text field. We're using a string // compare because <isindex> is not considered a form control, so it does // not support nsIFormControl or eHTML_FORM_CONTROL, and it's not worth // having a table of atoms just for it. if (isContentOfType(targetContent, "isindex")) return NS_OK; } … if (formControlType == NS_FORM_INPUT_TEXT || formControlType == NS_FORM_INPUT_PASSWORD) { PRInt32 selectionStart, textLength; nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(targetContent); if (input) { input->GetSelectionStart (&selectionStart); input->GetTextLength (&textLength); } else { nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent); if (textArea) { textArea->GetSelectionStart (&selectionStart); textArea->GetTextLength (&textLength); } } if (textLength != 0 && selectionStart != 0) return NS_OK; }
// We're using this key, no one else should aEvent->StopPropagation(); aEvent->PreventDefault(); return Left(); } if (keyCode == mService->mKeyCodeRight) { // ************************************************************************************ // NS_FORM_TEXTAREA cases:
// ************************************************************************************ // NS_FORM_INPUT_TEXT | NS_FORM_INPUT_PASSWORD | NS_FORM_INPUT_FILE cases
if (formControlType == NS_FORM_INPUT_TEXT || formControlType == NS_FORM_INPUT_PASSWORD) { PRInt32 selectionEnd, textLength; nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(targetContent); if (input) { input->GetSelectionEnd (&selectionEnd); input->GetTextLength (&textLength); } else { nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea = do_QueryInterface(targetContent); if (textArea) { textArea->GetSelectionEnd (&selectionEnd); textArea->GetTextLength (&textLength); } } // going down.
if (textLength != selectionEnd) return NS_OK; } aEvent->StopPropagation(); aEvent->PreventDefault(); return Right(); } if (keyCode == mService->mKeyCodeUp) {
// If we are going up or down, in a select, lets not // navigate. // // FIX: What we really want to do is determine if we are // at the start or the end fo the form element, and // based on the selected position we decide to nav. or // not.
// ************************************************************************************ // NS_FORM_SELECT cases: // * if it is a select form of 'size' attr != than '1' then we do as above.
// * if it is a select form of 'size' attr == than '1', snav can take care of it. // if (formControlType == NS_FORM_SELECT) // return NS_OK;
aEvent->StopPropagation(); aEvent->PreventDefault(); return Up(); } if (keyCode == mService->mKeyCodeDown) { // If we are going up or down, in a select, lets not // navigate. // // FIX: What we really want to do is determine if we are // at the start or the end fo the form element, and // based on the selected position we decide to nav. or // not.
// ************************************************************************************ // NS_FORM_SELECT cases: // * if it is a select form of 'size' attr != than '1' then we do as above.
// * if it is a select form of 'size' attr == than '1', snav can take care of it. // if (formControlType == NS_FORM_SELECT) // return NS_OK;
aEvent->StopPropagation(); // We're using this key, no one else should aEvent->PreventDefault(); return Down(); } return NS_OK; }
|
以上函数负责对按键事件的处理。我发现该函数被调用了,但是就是没有切换焦点,调试了好一会才发现,原来是两个设置没有设,后来加了一个函数,在初始时调用,一切正常了:
gboolean mozilla_pref_init(void) { nsresult rv; nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if(NS_SUCCEEDED(rv) && pref != nsnull) { pref->SetIntPref("snav.keyCode.modifier", 0); pref->SetBoolPref("snav.enabled", true); }
return TRUE; }
|
Spatialnavigation是一个非常典型的扩展,对于实现自己的扩展很有参考价值,特别是添加自己的快捷键处理。
~~end~~