Codejock Forums Homepage
Forum Home Forum Home > Codejock Products > Visual C++ MFC > Controls
  New Posts New Posts RSS Feed - Can I add a control in the ribbon bar caption?
  FAQ FAQ  Forum Search   Events   Register Register  Login Login

Can I add a control in the ribbon bar caption?

 Post Reply Post Reply
Author
Message
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 867
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Topic: Can I add a control in the ribbon bar caption?
    Posted: 03 December 2021 at 3:43pm
I see the ribbonsample adds tabs that display in the caption area (context tabs). I have a need to add an edit control to the caption area.

I already add some buttons in the tab area by just getting the ribbonbar controls and calling Add. And, I know about AddCaptionButton API on the CXTPRibbonBar and of course the quick access popup (button) etc.

I just haven't found a way to add another control type to the caption. Is it possible? I'm thinking of something along the lines of the Visual Studio (and other application) "Search" control that apps are now positioning in the caption area.
Back to Top
dbrookes View Drop Down
Groupie
Groupie


Joined: 30 August 2019
Location: Australia
Status: Offline
Points: 69
Post Options Post Options   Thanks (0) Thanks(0)   Quote dbrookes Quote  Post ReplyReply Direct Link To This Post Posted: 20 December 2021 at 12:31am
You can do it with a bit of hackery. I have managed to do it. Since the ribbon draws the caption in its client area itself its possible to manually position a control up there yourself by overriding CalcDockingLayout. Similar to how the quick access toolbar buttons or minimize, maximize, close caption buttons are positioned.

One challenge of positioning an edit control in the caption title though is that context tab captions and caption text can get in your way. You kind of are forced to dodge your edit control around them. For that reason I would recommend just keeping the edit control in the tab area. You can then actually left align to the tabs.

Here is some code that you might be able to adapt. Use at your own peril :)

// MmRibbonBar.h

///
/// \enum eSearchPosition
///
enum eSearchPosition
  {
  eSearchPositionTabControl = 0, ///< Next to the tab control, similar to Office 2016 products.
  eSearchPositionTitleBar, ///< In the frame hook title bar, similar to Office 365 (late 2019).
  eSearchPositionDefault = eSearchPositionTabControl
  };

#define SEARCH_MINIMUM_WIDTH XPT_DPI_X(40)
#define SEARCH_MAXIMUM_WIDTH XTP_DPI_X(220)
#define SEARCH_HEIGHT        XTP_DPI_Y(22)

...

// MmRibbonBar.cpp

CSize CMmRibbonBar::CalcDockingLayout(int nLength, DWORD dwMode, int nWidth /*= 0*/)
  {
  CXTPControl *pControlSearch = NULL;
  if (dwMode & LM_COMMIT)
    {
    pControlSearch = m_pControls->FindControl(xtpControlEdit, ID_COMMAND_SEARCH, TRUE, FALSE);
    if (pControlSearch)
      {
      if (m_searchPosition == eSearchPositionTabControl)
        {
        pControlSearch->SetVisible(TRUE);
        // Minimum width and padding.
        pControlSearch->SetWidth(SEARCH_MINIMUM_WIDTH + XTP_DPI_X(16));
        }
      else
        {
        // Hide before base calc docking layout to avoid this control effecting the size.
        pControlSearch->SetVisible(FALSE);
        }
      }
    }

  const CSize sz = CXTPRibbonBar::CalcDockingLayout(nLength, dwMode, nWidth);

  if (dwMode & LM_COMMIT)
    {
    if (pControlSearch)
      {
      // Left align the left-most right aligned edit control to the last visible tab item.
      CRect rcSearch = GetSearchControlRect(pControlSearch);

      if (IsBackstageViewVisible())
        {
        // Hide when backstage view is active.
        pControlSearch->SetVisible(FALSE);
        }
      else if (m_searchPosition == eSearchPositionTabControl)
        {
        ASSERT(pControlSearch->IsVisible());
        pControlSearch->SetWidth(rcSearch.Width());
        pControlSearch->SetRect(rcSearch);

        if (m_pControlTab && m_rcTabControl.right > rcSearch.left)
          {
          // Resize tab control.
          // Don't want overlap between the edit control and the tab control as it messes with hit testing.
          m_rcTabControl.right = rcSearch.left;
          m_pControlTab->SetRect(m_rcTabControl);
          }
        }
      else
        {
        // If there is not enough room, hide the control.
        if (rcSearch.Width() >= SEARCH_MINIMUM_WIDTH)
          {
          pControlSearch->SetRect(rcSearch);
          pControlSearch->SetVisible(TRUE);
          }
        }
      }
    }

  return sz;
  }
 
  CRect CMmRibbonBar::GetSearchControlRect(CXTPControl *pControlSearch) const
  {
  using std::min;
  using std::max;

  CRect rcControl;

  switch (m_searchPosition)
    {
    case eSearchPositionTabControl:
      {
      // Get last visible tab item.
      const CXTPTabManagerItem *pLastTabItem = NULL;
      for (int i = m_pControlTab->GetItemCount() - 1; i >= 0; --i)
        {
        const CXTPTabManagerItem *pTabItem = m_pControlTab->GetItem(i);
        if (pTabItem->IsVisible())
          {
          pLastTabItem = pTabItem;
          break;
          }
        }

      // Get next visible ribbon background control to the right of this control.
      CXTPControl *pRHSControl = NULL;
      for (int i = pControlSearch->GetIndex() + 1; i < GetControlCount(); ++i)
        {
        CXTPControl *pControl = GetControl(i);
        if (pControl &&
            pControl->IsVisible() &&
            pControl->GetFlags() & (xtpFlagRightAlign | xtpFlagRibbonTabBackground) &&
            pControl->GetID() != ID_COMMAND_SEARCH)
          {
          pRHSControl = pControl;
          break;
          }
        }

      if (pLastTabItem)
        {
        const CRect rcLastTab = pLastTabItem->GetRect();

        CRect rcClient;
        GetClientRect(&rcClient);

        int nLeft = rcLastTab.right + XTP_DPI_X(8);
        int nTop = rcLastTab.top;
        int nRight = rcClient.right - XTP_DPI_X(8);
        if (pRHSControl)
          nRight = pRHSControl->GetRect().left - XTP_DPI_X(8);
        int nBottom = rcLastTab.bottom - XTP_DPI_Y(2);

        int nSearchMinWidth = SEARCH_MINIMUM_WIDTH;
        int nSearchMaxWidth = SEARCH_MAXIMUM_WIDTH;

        int nSearchWidth = max(nSearchMinWidth, min(nSearchMaxWidth, nRight - nLeft));
        int nSearchHeight = SEARCH_HEIGHT;

        // Left align the left-most right aligned edit control to the last visible tab item.
        rcControl = CRect(nLeft,
                          nTop + (nBottom - nTop) / 2 - nSearchHeight / 2,
                          nLeft + nSearchWidth,
                          nTop + (nBottom - nTop) / 2 + nSearchHeight / 2);
        }
      }
      break;
    case eSearchPositionTitleBar:
      {
      // Get frame hook caption button.
      CXTPControl *pMinimize = m_pControls->FindControl(SC_MINIMIZE);

      // Get last visible tab item.
      const CXTPRibbonTabContextHeader *pFirstContextHeader = NULL;
      const CXTPRibbonTabContextHeader *pLastContextHeader = NULL;
      for (int i = m_pControlTab->GetItemCount() - 1; i >= 0; --i)
        {
        CXTPRibbonTab *pTab = DYNAMIC_DOWNCAST(CXTPRibbonTab, m_pControlTab->GetItem(i));
        if (pTab && pTab->IsVisible())
          {
          CXTPRibbonTabContextHeader *pContextHeader = pTab->GetContextHeader();
          if (pContextHeader)
            {
            if (!pLastContextHeader)
              pLastContextHeader = pContextHeader;
            pFirstContextHeader = pContextHeader;
            }
          }
        }

      if (pMinimize)
        {
        CRect rcMinimize = pMinimize->GetRect();
        CRect rcCaption = m_rcCaptionText;

        int nMaxSearchWidth = SEARCH_MAXIMUM_WIDTH;

        int nTop = rcMinimize.top + XTP_DPI_Y(4);
        int nBottom = rcMinimize.bottom - XTP_DPI_Y(4);
        int nLeft, nRight;
        if (pFirstContextHeader && pLastContextHeader)
          {
          // pFirstContextHeader can be equal to pLastContextHeader if only a single context is up.
          CRect rcFirstContextTab = pFirstContextHeader->m_rcRect;
          CRect rcLastContextTab = pLastContextHeader->m_rcRect;

          // Left side of context titles.
          int nLeftLeft = rcCaption.right + XTP_DPI_X(8);
          int nLeftRight = rcFirstContextTab.left - XTP_DPI_X(8);

          // Right side of context titles (preferred).
          int nRightLeft = std::max(rcCaption.right, rcLastContextTab.right) + XTP_DPI_X(8);
          int nRightRight = rcMinimize.left - XTP_DPI_X(8);

          if (nLeftRight - nLeftLeft > nRightRight - nRightLeft &&
              nRightRight - nRightLeft < nMaxSearchWidth) // Prefer right if it can fit max width.
            {
            nLeft = nLeftLeft;
            nRight = nLeftRight;
            }
          else
            {
            nLeft = nRightLeft;
            nRight = nRightRight;
            }
          }
        else
          {
          nLeft = rcCaption.right + XTP_DPI_X(8);
          nRight = rcMinimize.left - XTP_DPI_X(8);
          }

        int nSearchWidth = min(nMaxSearchWidth, nRight - nLeft);
        int nSearchHeight = SEARCH_HEIGHT;

        // Right align to the left-most caption button (minimize button).
        rcControl = CRect(nLeft + (nRight - nLeft) / 2 - nSearchWidth / 2,
                          nTop + (nBottom - nTop) / 2 - nSearchHeight / 2,
                          nLeft + (nRight - nLeft) / 2 + nSearchWidth / 2,
                          nTop + (nBottom - nTop) / 2 + nSearchHeight / 2);
        }
      }
      break;
    default:
      ASSERT(0);
    }

  return rcControl;
  }

In our ribbon resource, the ID_COMMAND_SEARCH control is an edit control on the defined on the tab background. This code then finds that control and manually repositions it. There is a minimum and maximum width defined for the search box to allow it to scale a bit for smaller screens. We do have some custom drawing as well to make it fit in the Office 2016 style if that theme is used.
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 867
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 20 December 2021 at 8:49am
Actually, by the time I got requirements, we wanted a logo in the caption area and the edit control under it. I made a couple of CJ virtual methods. One to create the caption buttons and one to create the caption button control (so I could subclass the caption button control) so I could paint it. CJ only supports a few caption buttons - minimize,maximize,resize and close. I had their code call the virtual methods. I had to use xtpAlignRight as the paint code ended up adding the size of the control to the spacing between the system (tab) button and the first ribbon tab and my control is just to the left of the system buttons CJ adds.

I also implemented my own auto-complete as CJ only supported system supplied behavior (folders ...) and my auto-complete is for finding stuff like command (controls) on the ribbon.
Back to Top
 Post Reply Post Reply
  Share Topic   

Forum Jump Forum Permissions View Drop Down

Forum Software by Web Wiz Forums® version 12.04
Copyright ©2001-2021 Web Wiz Ltd.

This page was generated in 0.188 seconds.