Как сделать так, чтобы всплывающее меню GWT оставалось в окне браузера?

В моем приложении GWT для основного макета используется DockLayoutPanel, а сама страница не прокручивается. У меня есть PopupPanel с MenuBar, и иногда, когда выбран MenuItem, строка подменю уходит за нижнюю часть экрана, внезапно заставляя новую полосу прокрутки в браузере и портя макет.

Как мне заставить всплывающее меню вести себя правильно и перемещать себя вверх, когда при позиционировании по умолчанию оно выводится из области просмотра браузера (как работает позиционирование PopupPanel.showRelativeTo (uiTarget))?

Глядя на исходный код MenuBar, кажется, что весь макет выполнен в частных методах, поэтому я не могу исправить его в подклассе, и я не вижу никаких событий, которые я мог бы прослушать, которые позволили бы мне самостоятельно выполнить изменение положения. .


person Mocky    schedule 13.10.2010    source источник
comment
Как это происходит? Вы нашли способ получить размеры?   -  person user592704    schedule 14.08.2012


Ответы (3)


Взгляните на http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/6185225fec64c091/4954d91d1461c71f?lnk=gst&q=context+menu#4954d91d1461c71f.

Мы довольно успешно используем эту стратегию уже некоторое время.

Обновление: нужно сделать еще немного. Конкретно:

  • Создайте метод reposition (), который:

    • Determines the max width of all the menu items
    • Проверяет левый край меню + максимальную ширину; если больше ширины окна, используйте "DOM.setStyleAttribute (elem," left ", left +" px ");" переместить меню
    • Получить высоту меню; если верх меню + высота меню> высота окна, используйте "DOM.setStyleAttribute (elem," top ", top +" px ");" чтобы поднять его.
  • В методе onAttach () используйте отложенную команду для вызова метода reposition ().

person jgindin    schedule 13.10.2010
comment
Код, опубликованный в этой ветке, касается контекстных меню, вызываемых правой кнопкой мыши в GWT 1.1. Единственная ссылка на позиционирование меню - это заметный комментарий внизу кода: // TODO: если мы приближаемся к нижней части окна, то смещаем Y на высоту меню, которое будет нарисовано. - person Mocky; 14.10.2010
comment
Да, извините, я недостаточно внимательно присмотрелся. Наш код расширяет идеи, изложенные в этом посте. Я отредактировал свой ответ выше, чтобы показать наш подход. - person jgindin; 14.10.2010
comment
Можно использовать репозицию из отложенной команды в методе onAttach (). Однако изменение положения происходит после того, как меню было отрисовано и принудительно ретранслировать страницу вокруг новой полосы прокрутки. После того, как отложенная команда исправляет это, происходит еще одна ретрансляция полной страницы, как это было. Мне нужен способ разместить всплывающее окно после того, как оно будет прикреплено, но до того, как оно станет видимым. - person Mocky; 19.10.2010
comment
Я не нашел способа получить размеры, пока он не прикреплен. У вас может быть такой стиль в меню, который сначала не будет виден, выполнить вычисления, а затем изменить видимость. (Никогда не пробовал, так что YMMV.) - person jgindin; 19.10.2010

Вы можете перехватить всплывающее окно непосредственно перед его отображением, но после создания его размера. Таким образом, у вас есть ширина всплывающего окна и вы можете переместить его в другое положение:

@Override
public void onContextMenu(ContextMenuEvent evt) {
    int x = evt.getNativeEvent().getClientX();
    int y = evt.getNativeEvent().getClientY();

    popupMenu.setPopupPositionAndShow(new PositionCallback() {
        @Override
        public void setPosition(int offsetWidth, int offsetHeight) {
            if (x + offsetWidth > Window.getClientWidth()) {
                x = Window.getClientWidth() - offsetWidth;
            }

            //use same technique for height if you want for y, then
            setPosition(x, y);
        }
    });
}

(Я знаю, что это старый вопрос, но он все равно возникает, если вы его ищете, поэтому я подумал о предоставлении настоящего решения)

person membersound    schedule 02.05.2013
comment
Где мне поставить этот метод? - person Ninju Bohra; 20.02.2014
comment
По-разному. Вы можете расширить Composite и переопределить его существующий onContextMenu(), вы также можете implement ContextMenuHander и добавить его в компонент через .addHandler(new YourCustomContextMenuHandler()). - person membersound; 20.02.2014
comment
Ах ... Я использую для MenuBar Спасибо - person Ninju Bohra; 20.02.2014
comment
@membersound У меня такая же проблема с позиционированием в 2015 году, и мне все еще интересно, где именно я бы поместил это, чтобы перехватить MenuPopup, созданный MenuBar. Как мне получить доступ к объекту popupMenu, чтобы я мог вызвать PopupPanel#setPopupPositionAndShow(PositionCallback), учитывая, что это экземпляр частного класса в MenuBar, а также имеет частный доступ в MenuBar? - person Will Byrne; 05.06.2015
comment
Я точно не помню, но я думаю, что вам нужно расширить класс Popup и переопределить метод onContextMenu, но не рассчитывайте на меня. Я, наконец, бросил разработку gwt из-за множества досадных проблем и перешел на vaadin (который, конечно, построен на основе gwt, но преодолевает все эти проблемы). Я действительно могу порекомендовать взглянуть на это! - person membersound; 06.06.2015

Эмм ...

Это интересный вопрос ...

Глядя на MenuBar исходный код ... особенно метод openPopup

 private void openPopup(final MenuItem item) {
    // Only the last popup to be opened should preview all event
    if (parentMenu != null && parentMenu.popup != null) {
      parentMenu.popup.setPreviewingAllNativeEvents(false);
    }

    // Create a new popup for this item, and position it next to
    // the item (below if this is a horizontal menu bar, to the
    // right if it's a vertical bar).
    popup = new DecoratedPopupPanel(true, false, "menuPopup") {
      {
        setWidget(item.getSubMenu());
        setPreviewingAllNativeEvents(true);
        item.getSubMenu().onShow();
      }

      @Override
      protected void onPreviewNativeEvent(NativePreviewEvent event) {
        // Hook the popup panel's event preview. We use this to keep it from
        // auto-hiding when the parent menu is clicked.
        if (!event.isCanceled()) {

          switch (event.getTypeInt()) {
            case Event.ONMOUSEDOWN:
              // If the event target is part of the parent menu, suppress the
              // event altogether.
              EventTarget target = event.getNativeEvent().getEventTarget();
              Element parentMenuElement = item.getParentMenu().getElement();
              if (parentMenuElement.isOrHasChild(Element.as(target))) {
                event.cancel();
                return;
              }
              super.onPreviewNativeEvent(event);
              if (event.isCanceled()) {
                selectItem(null);
              }
              return;
          }
        }
        super.onPreviewNativeEvent(event);
      }
    };
    popup.setAnimationType(AnimationType.ONE_WAY_CORNER);
    popup.setAnimationEnabled(isAnimationEnabled);
    popup.setStyleName(STYLENAME_DEFAULT + "Popup");
    String primaryStyleName = getStylePrimaryName();
    if (!STYLENAME_DEFAULT.equals(primaryStyleName)) {
      popup.addStyleName(primaryStyleName + "Popup");
    }
    popup.addPopupListener(this);

    shownChildMenu = item.getSubMenu();
    item.getSubMenu().parentMenu = this;

    // Show the popup, ensuring that the menubar's event preview remains on top
    // of the popup's.
    popup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {

      public void setPosition(int offsetWidth, int offsetHeight) {

        // depending on the bidi direction position a menu on the left or right
        // of its base item
        if (LocaleInfo.getCurrentLocale().isRTL()) {
          if (vertical) {
            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft() - offsetWidth
                + 1, item.getAbsoluteTop());
          } else {
            popup.setPopupPosition(item.getAbsoluteLeft()
                + item.getOffsetWidth() - offsetWidth,
                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight()
                    - 1);
          }
        } else {
          if (vertical) {
            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft()
                + MenuBar.this.getOffsetWidth() - 1, item.getAbsoluteTop());
          } else {
            popup.setPopupPosition(item.getAbsoluteLeft(),
                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight()
                    - 1);
          }
        }
      }
    });
  }

Интересно указать фрагмент как

...
popup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {

      public void setPosition(int offsetWidth, int offsetHeight) {

        // depending on the bidi direction position a menu on the left or right
        // of its base item
        if (LocaleInfo.getCurrentLocale().isRTL()) {
          if (vertical) {
            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft() - offsetWidth
                + 1, item.getAbsoluteTop());
          } else {
            popup.setPopupPosition(item.getAbsoluteLeft()
                + item.getOffsetWidth() - offsetWidth,
                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight()
                    - 1);
          }
        } else {
          if (vertical) {
            popup.setPopupPosition(MenuBar.this.getAbsoluteLeft()
                + MenuBar.this.getOffsetWidth() - 1, item.getAbsoluteTop());
          } else {
            popup.setPopupPosition(item.getAbsoluteLeft(),
                MenuBar.this.getAbsoluteTop() + MenuBar.this.getOffsetHeight()
                    - 1);
          }
        }
      }
    });

...

... поэтому я могу предположить, что есть смысл поиграть с объектом MenuItem, особенно с его унаследованными методами UIObject, такими как getAbsoluteLeft () и getAbsoluteTop (), конечно ...

Я бы порекомендовал расширить MenuItem таким образом

    //not tested
    public class MyMenuItem extends MenuItem
    {

    private MenuBar aSubMenuBar;//ItemMenu's submenu

    //...


    @Override
        public int getAbsoluteTop() {
            // TODO Auto-generated method stub
            return super.getAbsoluteTop()+movePopupTo();
        }


        private int movePopupTo()
    {
        int moveTo=0;

        int bottom=RootPanel.getBodyElement().getAbsoluteBottom();
        int rest=bottom -(super.getAbsoluteTop()+this.getaSubMenuBar().getOffsetHeight());
        if(rest<0)
        {
            moveTo=rest;
        }

        return moveTo;



    }



    public MenuBar getaSubMenuBar() {
            return aSubMenuBar;
        }

        public void setaSubMenuBar(MenuBar aSubMenuBar) {
            this.aSubMenuBar = aSubMenuBar;
        }


   //...

    }

Это не окончательное решение, а основная концепция.


Сообщите, помогло ли это

Удачи

person user592704    schedule 03.08.2012
comment
Не будет работать, потому что способ, которым выполняется вызов getAbsoluteTop ... вызов кода GWT ограничен реализацией класса MenuBar (из-за синтаксиса **MenuBar**.this.getAbsoluteTop()) - person Ninju Bohra; 20.02.2014