📄 htmlselectelement.cpp
字号:
{ if (renderer()) return isFocusable(); return HTMLFormControlElementWithState::isKeyboardFocusable(event);}bool HTMLSelectElement::isMouseFocusable() const{ if (renderer()) return isFocusable(); return HTMLFormControlElementWithState::isMouseFocusable();}bool HTMLSelectElement::canSelectAll() const{ return !usesMenuList(); }void HTMLSelectElement::selectAll(){ ASSERT(!usesMenuList()); if (!renderer() || !multiple()) return; // Save the selection so it can be compared to the new selectAll selection when we call onChange saveLastSelection(); m_activeSelectionState = true; setActiveSelectionAnchorIndex(nextSelectableListIndex(-1)); setActiveSelectionEndIndex(previousSelectableListIndex(-1)); updateListBoxSelection(false); listBoxOnChange();}RenderObject* HTMLSelectElement::createRenderer(RenderArena* arena, RenderStyle*){ if (usesMenuList()) return new (arena) RenderMenuList(this); return new (arena) RenderListBox(this);}bool HTMLSelectElement::appendFormData(FormDataList& list, bool){ if (name().isEmpty()) return false; bool successful = false; const Vector<HTMLElement*>& items = listItems(); unsigned i; for (i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]); if (option->selected()) { list.appendData(name(), option->value()); successful = true; } } } // ### this case should not happen. make sure that we select the first option // in any case. otherwise we have no consistency with the DOM interface. FIXME! // we return the first one if it was a combobox select if (!successful && !m_multiple && m_size <= 1 && items.size() && (items[0]->hasLocalName(optionTag))) { HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[0]); if (option->value().isNull()) list.appendData(name(), option->text().stripWhiteSpace()); else list.appendData(name(), option->value()); successful = true; } return successful;}int HTMLSelectElement::optionToListIndex(int optionIndex) const{ const Vector<HTMLElement*>& items = listItems(); int listSize = (int)items.size(); if (optionIndex < 0 || optionIndex >= listSize) return -1; int optionIndex2 = -1; for (int listIndex = 0; listIndex < listSize; listIndex++) { if (items[listIndex]->hasLocalName(optionTag)) { optionIndex2++; if (optionIndex2 == optionIndex) return listIndex; } } return -1;}int HTMLSelectElement::listToOptionIndex(int listIndex) const{ const Vector<HTMLElement*>& items = listItems(); if (listIndex < 0 || listIndex >= int(items.size()) || !items[listIndex]->hasLocalName(optionTag)) return -1; int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list for (int i = 0; i < listIndex; i++) if (items[i]->hasLocalName(optionTag)) optionIndex++; return optionIndex;}PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options(){ return HTMLOptionsCollection::create(this);}void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const{ m_listItems.clear(); HTMLOptionElement* foundSelected = 0; for (Node* current = firstChild(); current; current = current->traverseNextSibling(this)) { if (current->hasTagName(optgroupTag) && current->firstChild()) { // FIXME: It doesn't make sense to add an optgroup to the list items, // when it has children, but not to add it if it happens to have, // children (say some comment nodes or text nodes), yet that's what // this code does! m_listItems.append(static_cast<HTMLElement*>(current)); current = current->firstChild(); // FIXME: It doesn't make sense to handle an <optgroup> inside another <optgroup> // if it's not the first child, but not handle it if it happens to be the first // child, yet that's what this code does! } if (current->hasTagName(optionTag)) { m_listItems.append(static_cast<HTMLElement*>(current)); if (updateSelectedStates) { if (!foundSelected && (usesMenuList() || (!m_multiple && static_cast<HTMLOptionElement*>(current)->selected()))) { foundSelected = static_cast<HTMLOptionElement*>(current); foundSelected->setSelectedState(true); } else if (foundSelected && !m_multiple && static_cast<HTMLOptionElement*>(current)->selected()) { foundSelected->setSelectedState(false); foundSelected = static_cast<HTMLOptionElement*>(current); } } } if (current->hasTagName(hrTag)) m_listItems.append(static_cast<HTMLElement*>(current)); } m_recalcListItems = false;}void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta){ setRecalcListItems(); HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); if (AXObjectCache::accessibilityEnabled() && renderer()) renderer()->document()->axObjectCache()->childrenChanged(renderer());}void HTMLSelectElement::setRecalcListItems(){ m_recalcListItems = true; if (renderer()) { if (usesMenuList()) static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true); else static_cast<RenderListBox*>(renderer())->setOptionsChanged(true); } if (!inDocument()) m_collectionInfo.reset(); setChanged();}void HTMLSelectElement::reset(){ bool optionSelected = false; HTMLOptionElement* firstOption = 0; const Vector<HTMLElement*>& items = listItems(); unsigned i; for (i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]); if (!option->getAttribute(selectedAttr).isNull()) { option->setSelectedState(true); optionSelected = true; } else option->setSelectedState(false); if (!firstOption) firstOption = option; } } if (!optionSelected && firstOption && usesMenuList()) firstOption->setSelectedState(true); setChanged();}void HTMLSelectElement::dispatchFocusEvent(){ if (usesMenuList()) // Save the selection so it can be compared to the new selection when we call onChange during dispatchBlurEvent. saveLastSelection(); HTMLFormControlElementWithState::dispatchFocusEvent();}void HTMLSelectElement::dispatchBlurEvent(){ // We only need to fire onChange here for menu lists, because we fire onChange for list boxes whenever the selection change is actually made. // This matches other browsers' behavior. if (usesMenuList()) menuListOnChange(); HTMLFormControlElementWithState::dispatchBlurEvent();}void HTMLSelectElement::defaultEventHandler(Event* evt){ if (!renderer()) return; if (usesMenuList()) menuListDefaultEventHandler(evt); else listBoxDefaultEventHandler(evt); if (evt->defaultHandled()) return; if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) { KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(evt); if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() && isPrintableChar(keyboardEvent->charCode())) { typeAheadFind(keyboardEvent); evt->setDefaultHandled(); return; } } HTMLFormControlElementWithState::defaultEventHandler(evt);}void HTMLSelectElement::menuListDefaultEventHandler(Event* evt){ if (evt->type() == eventNames().keydownEvent) { if (!renderer() || !evt->isKeyboardEvent()) return; String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); bool handled = false;#if ARROW_KEYS_POP_MENU if (keyIdentifier == "Down" || keyIdentifier == "Up") { focus(); // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(); if (RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer())) menuList->showPopup(); handled = true; }#else int listIndex = optionToListIndex(selectedIndex()); if (keyIdentifier == "Down" || keyIdentifier == "Right") { int size = listItems().size(); for (listIndex += 1; listIndex >= 0 && listIndex < size && (listItems()[listIndex]->disabled() || !listItems()[listIndex]->hasTagName(optionTag)); ++listIndex) { } if (listIndex >= 0 && listIndex < size) setSelectedIndex(listToOptionIndex(listIndex)); handled = true; } else if (keyIdentifier == "Up" || keyIdentifier == "Left") { int size = listItems().size(); for (listIndex -= 1; listIndex >= 0 && listIndex < size && (listItems()[listIndex]->disabled() || !listItems()[listIndex]->hasTagName(optionTag)); --listIndex) { } if (listIndex >= 0 && listIndex < size) setSelectedIndex(listToOptionIndex(listIndex)); handled = true; }#endif if (handled) evt->setDefaultHandled(); } // Use key press event here since sending simulated mouse events // on key down blocks the proper sending of the key press event. if (evt->type() == eventNames().keypressEvent) { if (!renderer() || !evt->isKeyboardEvent()) return; int keyCode = static_cast<KeyboardEvent*>(evt)->keyCode(); bool handled = false;#if ARROW_KEYS_POP_MENU if (keyCode == ' ') { focus(); // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(); if (RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer())) menuList->showPopup(); handled = true; } if (keyCode == '\r') { menuListOnChange(); if (form()) form()->submitClick(evt); handled = true; }#else int listIndex = optionToListIndex(selectedIndex()); if (keyCode == '\r') { // listIndex should already be selected, but this will fire the onchange handler. setSelectedIndex(listToOptionIndex(listIndex), true, true); handled = true; }#endif if (handled) evt->setDefaultHandled(); } if (evt->type() == eventNames().mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { focus(); if (RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer())) { if (menuList->popupIsVisible()) menuList->hidePopup(); else { // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(); menuList->showPopup(); } } evt->setDefaultHandled(); }}void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt){ if (evt->type() == eventNames().mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { focus(); // Convert to coords relative to the list box if needed. MouseEvent* mouseEvent = static_cast<MouseEvent*>(evt); int offsetX = mouseEvent->offsetX(); int offsetY = mouseEvent->offsetY(); Node* target = evt->target()->toNode(); if (target != this) { FloatPoint absPos = renderer()->absoluteToLocal(FloatPoint(offsetX, offsetY), false, true); offsetX = absPos.x(); offsetY = absPos.y(); } int listIndex = static_cast<RenderListBox*>(renderer())->listIndexAtOffset(offsetX, offsetY); if (listIndex >= 0) { // Save the selection so it can be compared to the new selection when we call onChange during mouseup, or after autoscroll finishes. saveLastSelection(); m_activeSelectionState = true; bool multiSelectKeyPressed = false;#if PLATFORM(MAC) multiSelectKeyPressed = mouseEvent->metaKey();#else multiSelectKeyPressed = mouseEvent->ctrlKey();#endif bool shiftSelect = multiple() && mouseEvent->shiftKey(); bool multiSelect = multiple() && multiSelectKeyPressed && !mouseEvent->shiftKey(); HTMLElement* clickedElement = listItems()[listIndex]; HTMLOptionElement* option = 0; if (clickedElement->hasLocalName(optionTag)) { option = static_cast<HTMLOptionElement*>(clickedElement); // Keep track of whether an active selection (like during drag selection), should select or deselect if (option->selected() && multiSelectKeyPressed) m_activeSelectionState = false; if (!m_activeSelectionState) option->setSelectedState(false); } // If we're not in any special multiple selection mode, then deselect all other items, excluding the clicked option.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -