From 62a466e53cbb49c5338f8ea7f96b46e00d3b0bc4 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Fri, 14 Dec 2012 01:18:59 +0300 Subject: Add popup list. Replace listbox in dropdown to popup list. --- src/gui/gui.cpp | 10 ++ src/gui/gui.h | 7 ++ src/gui/itemamountwindow.cpp | 2 +- src/gui/popupmenu.cpp | 17 +++ src/gui/popupmenu.h | 3 + src/gui/widgets/dropdown.cpp | 244 ++++++++++++++++++++++++++++++++---------- src/gui/widgets/dropdown.h | 68 ++++++++++-- src/gui/widgets/popuplist.cpp | 115 ++++++++++++++++++++ src/gui/widgets/popuplist.h | 74 +++++++++++++ 9 files changed, 472 insertions(+), 68 deletions(-) create mode 100644 src/gui/widgets/popuplist.cpp create mode 100644 src/gui/widgets/popuplist.h (limited to 'src/gui') diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 268f05fc1..7b2da4bff 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -696,3 +696,13 @@ void Gui::handleMouseInput() } BLOCK_END("Gui::handleMouseInput") } + +void Gui::addGlobalFocusListener(gcn::FocusListener* focusListener) +{ + mFocusListeners.push_back(focusListener); +} + +void Gui::removeGlobalFocusListener(gcn::FocusListener* focusListener) +{ + mFocusListeners.remove(focusListener); +} diff --git a/src/gui/gui.h b/src/gui/gui.h index db373193a..04b6ddba3 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -142,6 +142,10 @@ class Gui final : public gcn::Gui void getAbsolutePosition(gcn::Widget *widget, int &x, int &y); + void addGlobalFocusListener(gcn::FocusListener* focusListener); + + void removeGlobalFocusListener(gcn::FocusListener* focusListener); + protected: void handleMouseMoved(const gcn::MouseInput &mouseInput); @@ -163,6 +167,9 @@ class Gui final : public gcn::Gui float mMouseCursorAlpha; int mMouseInactivityTimer; int mCursorType; + + typedef std::list FocusListenerList; + FocusListenerList mFocusListeners; }; extern Gui *gui; /**< The GUI system */ diff --git a/src/gui/itemamountwindow.cpp b/src/gui/itemamountwindow.cpp index e25647436..82f85a2e7 100644 --- a/src/gui/itemamountwindow.cpp +++ b/src/gui/itemamountwindow.cpp @@ -137,7 +137,7 @@ void ItemAmountWindow::finish(Item *const item, const int amount, ItemAmountWindow::ItemAmountWindow(const Usage usage, Window *const parent, Item *const item, const int maxRange) : - Window("", true, parent, "amount.xml"), + Window("", false, parent, "amount.xml"), gcn::ActionListener(), gcn::KeyListener(), mItemAmountTextField(new IntTextField(this, 1)), diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 4d781f5ac..5a75cb71d 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -70,6 +70,8 @@ #include "utils/gettext.h" +#include + #include "debug.h" extern int serverVersion; @@ -2187,6 +2189,21 @@ void PopupMenu::addPickupFilter(const std::string &name) } } +void PopupMenu::showPopup(const int x, const int y, gcn::ListModel *model) +{ + if (!model) + return; + + mBrowserBox->clearRows(); + for (int f = 0, sz = model->getNumberOfElements(); f < sz; f ++) + { + mBrowserBox->addRow(strprintf("dropdown_%d", f), + model->getElementAt(f).c_str()); + } + mBrowserBox->addRow("cancel", _("Cancel")); + showPopup(x, y); +} + RenameListener::RenameListener() : gcn::ActionListener(), mMapItem(nullptr), diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h index 9ebf8d163..431d7e7de 100644 --- a/src/gui/popupmenu.h +++ b/src/gui/popupmenu.h @@ -29,6 +29,7 @@ #include "actorsprite.h" #include +#include #include "localconsts.h" @@ -165,6 +166,8 @@ class PopupMenu final : public Popup, public LinkHandler void showChangePos(const int x, const int y); + void showPopup(const int x, const int y, gcn::ListModel *model); + /** * Handles link action. */ diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp index bde16760b..6c3e3e9cc 100644 --- a/src/gui/widgets/dropdown.cpp +++ b/src/gui/widgets/dropdown.cpp @@ -28,8 +28,10 @@ #include "keyevent.h" #include "gui/sdlinput.h" +#include "gui/viewport.h" #include "gui/widgets/listbox.h" +#include "gui/widgets/popuplist.h" #include "gui/widgets/scrollarea.h" #include "resources/image.h" @@ -58,15 +60,27 @@ DropDown::DropDown(const Widget2 *const widget, gcn::ListModel *const listModel, gcn::ActionListener *const listener, const std::string &eventId): - gcn::DropDown::DropDown(listModel, - new ScrollArea, new ListBox(widget, listModel)), + gcn::ActionListener(), + gcn::BasicContainer(), + gcn::KeyListener(), + gcn::MouseListener(), + gcn::FocusListener(), + gcn::SelectionListener(), + Widget2(widget), + mPopup(new PopupList(this, listModel)), mShadowColor(getThemeColor(Theme::DROPDOWN_SHADOW)), mHighlightColor(getThemeColor(Theme::HIGHLIGHT)), mPadding(1), - mImagePadding(2) + mImagePadding(2), + mDroppedDown(false), + mPushed(false), + mFoldedUpHeight(0), + mIsDragged(false) { mFrameSize = 2; + mPopup->setHeight(100); + // Initialize graphics if (instances == 0) { @@ -113,7 +127,19 @@ DropDown::DropDown(const Widget2 *const widget, instances++; - mListBox->setForegroundColor(getThemeColor(Theme::DROPDOWN)); + setWidth(100); + setFocusable(true); + setListModel(listModel); + + if (mPopup->getSelected() < 0) + mPopup->setSelected(0); + + addMouseListener(this); + addKeyListener(this); + addFocusListener(this); + + adjustHeight(); + mPopup->setForegroundColor(getThemeColor(Theme::DROPDOWN)); setForegroundColor(getThemeColor(Theme::DROPDOWN)); if (!eventId.empty()) @@ -122,8 +148,8 @@ DropDown::DropDown(const Widget2 *const widget, if (listener) addActionListener(listener); - if (mListBox) - mListBox->adjustSize(); + if (mPopup) + mPopup->adjustSize(); if (mSkin) { @@ -154,8 +180,6 @@ DropDown::~DropDown() theme->unloadRect(skinRect); } } - delete mScrollArea; - mScrollArea = nullptr; } void DropDown::updateAlpha() @@ -201,12 +225,12 @@ void DropDown::draw(gcn::Graphics* graphics) mHighlightColor.a = alpha; mShadowColor.a = alpha; - if (mListBox->getListModel() && mListBox->getSelected() >= 0) + if (mPopup->getListModel() && mPopup->getSelected() >= 0) { graphics->setFont(getFont()); graphics->setColor(mForegroundColor); - graphics->drawText(mListBox->getListModel()->getElementAt( - mListBox->getSelected()), mPadding, mPadding); + graphics->drawText(mPopup->getListModel()->getElementAt( + mPopup->getSelected()), mPadding, mPadding); } if (isFocused()) @@ -220,7 +244,7 @@ void DropDown::draw(gcn::Graphics* graphics) if (mDroppedDown) { - drawChildren(graphics); +// drawChildren(graphics); // Draw two lines separating the ListBox with selected // element view. graphics->setColor(mHighlightColor); @@ -255,7 +279,6 @@ void DropDown::drawButton(gcn::Graphics *graphics) } } -// -- KeyListener notifications void DropDown::keyPressed(gcn::KeyEvent& keyEvent) { if (keyEvent.isConsumed()) @@ -283,9 +306,9 @@ void DropDown::keyPressed(gcn::KeyEvent& keyEvent) break; case Input::KEY_GUI_END: - if (mListBox->getListModel()) + if (mPopup->getListModel()) { - setSelected(mListBox->getListModel()-> + setSelected(mPopup->getListModel()-> getNumberOfElements() - 1); } break; @@ -297,26 +320,57 @@ void DropDown::keyPressed(gcn::KeyEvent& keyEvent) keyEvent.consume(); } -void DropDown::focusLost(const gcn::Event& event) +void DropDown::hideDrop() { - gcn::DropDown::focusLost(event); - releaseModalMouseInputFocus(); + distributeActionEvent(); + mPopup->setVisible(false); } void DropDown::mousePressed(gcn::MouseEvent& mouseEvent) { - gcn::DropDown::mousePressed(mouseEvent); - - if (0 <= mouseEvent.getY() && mouseEvent.getY() < getHeight() && - mouseEvent.getX() >= 0 && mouseEvent.getX() < getWidth() && - mouseEvent.getButton() == gcn::MouseEvent::LEFT && mDroppedDown && - mouseEvent.getSource() == mListBox) + // If we have a mouse press on the widget. + if (mouseEvent.getButton() == gcn::MouseEvent::LEFT + && !mDroppedDown && mouseEvent.getSource() == this) + { + mPushed = true; + dropDown(); + } + else { mPushed = false; foldUp(); - releaseModalMouseInputFocus(); - distributeActionEvent(); + hideDrop(); + } +} + +void DropDown::mouseReleased(gcn::MouseEvent &mouseEvent) +{ + if (mIsDragged) + mPushed = false; + + // Released outside of widget. Can happen when we have modal + // input focus. + if ((0 > mouseEvent.getY() + || mouseEvent.getY() >= getHeight() + || mouseEvent.getX() < 0 + || mouseEvent.getX() >= getWidth()) + && mouseEvent.getButton() == gcn::MouseEvent::LEFT) + { + if (mIsDragged) + foldUp(); } + else if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) + { + mPushed = false; + } + + mIsDragged = false; +} + +void DropDown::mouseDragged(gcn::MouseEvent &mouseEvent) +{ + mIsDragged = true; + mouseEvent.consume(); } void DropDown::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent) @@ -333,7 +387,7 @@ void DropDown::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) void DropDown::setSelectedString(std::string str) { - gcn::ListModel *const listModel = mListBox->getListModel(); + gcn::ListModel *const listModel = mPopup->getListModel(); if (!listModel) return; @@ -349,7 +403,7 @@ void DropDown::setSelectedString(std::string str) std::string DropDown::getSelectedString() const { - gcn::ListModel *const listModel = mListBox->getListModel(); + gcn::ListModel *const listModel = mPopup->getListModel(); if (!listModel) return ""; @@ -358,48 +412,120 @@ std::string DropDown::getSelectedString() const void DropDown::adjustHeight() { - if (!mScrollArea || !mListBox) + setHeight(getFont()->getHeight() + 2 * mPadding); +} + +void DropDown::dropDown() +{ + if (!mPopup) return; - const int listBoxHeight = mListBox->getHeight(); - const int h2 = getFont()->getHeight() + 2 * mPadding; - int newHeight = h2; + if (!mDroppedDown) + { + mDroppedDown = true; + mFoldedUpHeight = getHeight(); + adjustHeight(); + +// if (getParent()) +// getParent()->moveToTop(this); + int x = 0; + int y = 0; + getAbsolutePosition(x, y); + const int frame = getParent()->getFrameSize(); + const int pad = mPopup->getPadding(); + const int pad2 = pad * 2; + + // here width should be adjusted on some other parameters + mPopup->setWidth(getWidth() - pad2 + 10); + mPopup->show(x - mPadding - frame - 1, y + getHeight()); + mPopup->requestMoveToTop(); + mPopup->requestFocus(); + } +} - if (mDroppedDown && getParent()) +void DropDown::foldUp() +{ + if (mDroppedDown) { - const int h = getParent()->getChildrenArea().height - getY(); - const int h0 = h - h2; - if (listBoxHeight > h0) - { - mScrollArea->setHeight(h0); - newHeight = h; - } - else - { - newHeight = listBoxHeight + h2; - mScrollArea->setHeight(listBoxHeight); - } + mDroppedDown = false; + adjustHeight(); +// mInternalFocusHandler.focusNone(); } +} - setHeight(newHeight); +int DropDown::getSelected() const +{ + return mPopup->getSelected(); +} - mScrollArea->setWidth(getWidth()); - mListBox->setWidth(mScrollArea->getChildrenArea().width); - mScrollArea->setPosition(0, 0); +void DropDown::setSelected(int selected) +{ + if (selected >= 0) + mPopup->setSelected(selected); } -void DropDown::dropDown() +void DropDown::setListModel(gcn::ListModel *listModel) { - const bool dropped = mDroppedDown; - gcn::DropDown::dropDown(); - if (!dropped) - adjustHeight(); + mPopup->setListModel(listModel); + + if (mPopup->getSelected() < 0) + mPopup->setSelected(0); + + adjustHeight(); } -void DropDown::foldUp() +gcn::ListModel *DropDown::getListModel() { - const bool dropped = mDroppedDown; - gcn::DropDown::foldUp(); - if (dropped) - adjustHeight(); + return mPopup->getListModel(); +} + +void DropDown::action(const gcn::ActionEvent &actionEvent A_UNUSED) +{ + foldUp(); + distributeActionEvent(); +} + +gcn::Rectangle DropDown::getChildrenArea() +{ + if (mDroppedDown) + { + // Calculate the children area (with the one pixel border in mind) + return gcn::Rectangle(1, mFoldedUpHeight + 1, + getWidth() - 2, getHeight() - mFoldedUpHeight - 2); + } + + return gcn::Rectangle(); +} + +void DropDown::valueChanged(const gcn::SelectionEvent& event A_UNUSED) +{ +} + +void DropDown::updateSelection() +{ + mDroppedDown = false; + mPushed = false; + distributeActionEvent(); + distributeValueChangedEvent(); +} + +void DropDown::addSelectionListener(SelectionListener* selectionListener) +{ + mSelectionListeners.push_back(selectionListener); +} + +void DropDown::removeSelectionListener(SelectionListener* listener) +{ + mSelectionListeners.remove(listener); +} + +void DropDown::distributeValueChangedEvent() +{ + for (SelectionListenerIterator iter = mSelectionListeners.begin(); + iter != mSelectionListeners.end(); + ++iter) + { + gcn::SelectionEvent event(this); + (*iter)->valueChanged(event); + } } diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h index d02b86672..6b3b6503a 100644 --- a/src/gui/widgets/dropdown.h +++ b/src/gui/widgets/dropdown.h @@ -25,12 +25,25 @@ #include "gui/widgets/widget2.h" -#include +#include "guichan/actionlistener.hpp" +#include "guichan/basiccontainer.hpp" +#include "guichan/focushandler.hpp" +#include "guichan/focuslistener.hpp" +#include "guichan/keylistener.hpp" +#include "guichan/listmodel.hpp" +#include "guichan/mouselistener.hpp" +#include "guichan/selectionlistener.hpp" + +#include "guichan/widgets/listbox.hpp" +#include "guichan/widgets/scrollarea.hpp" #include "localconsts.h" class Image; class ImageRect; +class ListBox; +class PopupList; +class ScrollArea; class Skin; /** @@ -40,7 +53,12 @@ class Skin; * DropDown you must give DropDown an implemented ListModel which represents * your list. */ -class DropDown final : public gcn::DropDown, +class DropDown final : public gcn::ActionListener, + public gcn::BasicContainer, + public gcn::KeyListener, + public gcn::MouseListener, + public gcn::FocusListener, + public gcn::SelectionListener, public Widget2 { public: @@ -70,10 +88,6 @@ class DropDown final : public gcn::DropDown, void drawFrame(gcn::Graphics *graphics) override; - // Inherited from FocusListener - - void focusLost(const gcn::Event& event) override; - // Inherited from KeyListener void keyPressed(gcn::KeyEvent& keyEvent) override; @@ -82,6 +96,10 @@ class DropDown final : public gcn::DropDown, void mousePressed(gcn::MouseEvent& mouseEvent) override; + void mouseReleased(gcn::MouseEvent& mouseEvent) override; + + void mouseDragged(gcn::MouseEvent& mouseEvent) override; + void mouseWheelMovedUp(gcn::MouseEvent& mouseEvent) override; void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) override; @@ -90,11 +108,35 @@ class DropDown final : public gcn::DropDown, std::string getSelectedString() const A_WARN_UNUSED; + void valueChanged(const gcn::SelectionEvent& event); + + void updateSelection(); + void adjustHeight(); - void dropDown() override; + void dropDown(); + + void foldUp(); + + void hideDrop(); + + int getSelected() const; - void foldUp() override; + void setSelected(int selected); + + void setListModel(gcn::ListModel *listModel); + + gcn::ListModel *getListModel(); + + void addSelectionListener(SelectionListener* listener); + + void removeSelectionListener(SelectionListener* selectionListener); + + gcn::Rectangle getChildrenArea(); + + void action(const gcn::ActionEvent &actionEvent); + + void distributeValueChangedEvent(); protected: /** @@ -104,11 +146,21 @@ class DropDown final : public gcn::DropDown, */ void drawButton(gcn::Graphics *graphics); + PopupList *mPopup; gcn::Color mShadowColor; gcn::Color mHighlightColor; int mPadding; int mImagePadding; + bool mDroppedDown; + bool mPushed; + int mFoldedUpHeight; +// gcn::FocusHandler mInternalFocusHandler; + bool mIsDragged; + typedef std::list SelectionListenerList; + SelectionListenerList mSelectionListeners; + typedef SelectionListenerList::iterator SelectionListenerIterator; + // Add own Images. static int instances; static Image *buttons[2][2]; diff --git a/src/gui/widgets/popuplist.cpp b/src/gui/widgets/popuplist.cpp new file mode 100644 index 000000000..e32cd5b70 --- /dev/null +++ b/src/gui/widgets/popuplist.cpp @@ -0,0 +1,115 @@ +/* + * The ManaPlus Client + * Copyright (C) 2011-2012 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gui/widgets/popuplist.h" + +#include "gui/widgets/dropdown.h" +#include "gui/widgets/listbox.h" +#include "gui/widgets/scrollarea.h" + +#include "utils/gettext.h" + +#include "debug.h" + +PopupList::PopupList(DropDown *const widget, + gcn::ListModel *const listModel): + Popup("PopupList", "popuplist.xml"), + gcn::FocusListener(), + mListModel(listModel), + mListBox(new ListBox(widget, listModel)), + mScrollArea(new ScrollArea), + mDropDown(widget) +{ + setFocusable(true); + + mListBox->setDistributeMousePressed(true); + mListBox->addSelectionListener(this); + mScrollArea->setContent(mListBox); + add(mScrollArea); + + if (getParent()) + getParent()->addFocusListener(this); +} + +void PopupList::show(int x, int y) +{ + int len = mListBox->getHeight() + 8; + if (len > 250) + len = 250; + setContentSize(mListBox->getWidth() + 8, len); + if (mainGraphics->mWidth < (x + getWidth() + 5)) + x = mainGraphics->mWidth - getWidth(); + if (mainGraphics->mHeight < (y + getHeight() + 5)) + y = mainGraphics->mHeight - getHeight(); + setPosition(x, y); + setVisible(true); + requestMoveToTop(); +} + +void PopupList::widgetResized(const gcn::Event &event) +{ + Popup::widgetResized(event); + adjustSize(); +} + +void PopupList::setSelected(int selected) +{ + if (!mListBox) + return; + + mListBox->setSelected(selected); +} + +int PopupList::getSelected() const +{ + if (!mListBox) + return -1; + + return mListBox->getSelected(); +} + +void PopupList::setListModel(gcn::ListModel *model) +{ + if (mListBox) + mListBox->setListModel(model); + mListModel = model; +} + +void PopupList::adjustSize() +{ + mScrollArea->setWidth(getWidth() - 8); + mScrollArea->setHeight(getHeight() - 8); + mListBox->adjustSize(); +} + +void PopupList::valueChanged(const gcn::SelectionEvent& event A_UNUSED) +{ + if (mDropDown) + mDropDown->updateSelection(); + setVisible(false); +} + +void PopupList::focusLost(const gcn::Event& event A_UNUSED) +{ + logger->log("lost focus"); + if (mDropDown) + mDropDown->updateSelection(); +// setVisible(false); +} diff --git a/src/gui/widgets/popuplist.h b/src/gui/widgets/popuplist.h new file mode 100644 index 000000000..f0b195a47 --- /dev/null +++ b/src/gui/widgets/popuplist.h @@ -0,0 +1,74 @@ +/* + * The ManaPlus Client + * Copyright (C) 2011-2012 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef POPUP_LIST_H +#define POPUP_LIST_H + +#include "gui/widgets/linkhandler.h" +#include "gui/widgets/popup.h" + +#include +#include "guichan/focuslistener.hpp" +#include +#include "guichan/selectionlistener.hpp" + +#include "localconsts.h" + +class DropDown; +class ListBox; +class ScrollArea; + +class PopupList final : public Popup, + public gcn::FocusListener, + public gcn::SelectionListener +{ + public: + PopupList(DropDown *const widget, + gcn::ListModel *const listModel); + + A_DELETE_COPY(PopupList) + + void show(int x, int y); + + void widgetResized(const gcn::Event &event) override; + + void setSelected(int selected); + + int getSelected() const; + + void setListModel(gcn::ListModel *model); + + gcn::ListModel *getListModel() const + { return mListModel; } + + void adjustSize(); + + void valueChanged(const gcn::SelectionEvent& event); + + void focusLost(const gcn::Event& event A_UNUSED); + + private: + gcn::ListModel *mListModel; + ListBox *mListBox; + ScrollArea *mScrollArea; + DropDown *mDropDown; +}; + +#endif -- cgit v1.2.3-70-g09d2