Getting notifications about sizeHint changes from internal widgets - c++

Small picture to depict what the problem is:
external_widget
+--------------------------------------------------+
| |
| |
| layout_widget |
| +-------------------------------+ |
| | | |
| | [internal_layout] | |
| | | |
| | | |
| | [Some Another Widget] | |
| +-------------------------------+ |
| |
+--------------------------------------------------+
layout_widget - just QWidget with vertical layout. It contains another internal_layout and another widgets.
internal_layout - layout where the widgets will be added and deleted in future.
external_widget - just QWidget with no layout, I control the position and size of layout widget and other widgets manually.
When I add some widgets in internal_layout - it sends LayoutRequest event (as far as I know, I saw this in Qt source codes). This event is catched by parent widget and parent widget tells his layout that something is changed which forces layout to recalculate layout_widget sizeHint() etc.
Do I understand this process right?
The problem is that sizeHint() is changed, but my external_widget doesn't know anything about that. I want to get notification about layout_widget sizeHint() changes. Is it possible?
I even tried to do this:
external widget ctor:
{
layout_widget->installEventFilter(this);
}
and tried to catch them:
void ...eventFilter(QObject* obj, QEvent* event )
{
if (obj == layout_widget && event == QEvent::LayoutRequest)
{
// TODO;
}
}
and I never got into "TODO" block.
Do layout_widget send some events or notifications that something was changed inside him and it should be resized, i.e. his sizeHint() has been changed?

Related

MFC How to change link text color of CLinkCtrl?

From this post, I have set up the code by putting in the OnInitDialog event:
LITEM* pItem = new LITEM;
pItem->iLink = 0; // Url index is 0
//LIF_ITEMINDEX is required for iLink, LIF_STATE is required for modifing state
pItem->mask = LIF_ITEMINDEX | LIF_STATE;
//using LIS_DEFAULTCOLORS state
pItem->state = LIS_ENABLED | LIS_FOCUSED | LIS_DEFAULTCOLORS;
pItem->stateMask = LIS_ENABLED | LIS_FOCUSED | LIS_DEFAULTCOLORS;
// Send the LM_SETITEM MESSAGE with pItem
HWND m_hWnd=nil; GetDlgItem(IDC_lbstackoverflow, &m_hWnd);
And from OnCtrlColor event:
pDC->SetTextColor(RGB(255, 0, 0));
pDC->SetBkColor(normal);
pDC->SetDCBrushColor(RGB(255, 255, 255));
return (HBRUSH)GetStockObject(DC_BRUSH);
After the code runs, my result is:
If I click on it, now it works:
But, how I can change text color?
You may not be setting the control state correctly.
Please try the MFC way (it works for me). Add to your dialog class:
CLinkCtrl syslink;
Connect it to the resource ID of your link:
void CMFCApplication4Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_SYSLINK1, syslink);
}
and in OnInitDialog add this:
syslink.SetItemState(0, LIS_ENABLED | LIS_FOCUSED | LIS_DEFAULTCOLORS,
LIS_ENABLED | LIS_FOCUSED | LIS_DEFAULTCOLORS);
I see red link with your code in OnCtlColor.

How can I move the icon above text in QComboBox

I have been looking at the QComboBox source file for a while now and I can't figure out what I need to change so that the icon is positioned above text in a QComboBox.
|-----------------------|
| ----- |
| |icn| |
| ----- |
| Text label here |
-------------------------
The paint method in QCombobox is very simple and references something called QStyleOptionComboBox, but I don't think I want to be making changes here though as this will impact portability.
Would I be better inventing something new to act and behave like a QComboBox?
I should have added that it I am looking at changing both the ListView and selected item i.e the button portion.
In order to handle the icon's (decoration) position in the combo box's pull down view, you need to override its view options QAbstractItemView::viewOptions(). Let's create a custom view and replace the native combo box view with ours:
class ComboView : public QListView
{
protected:
QStyleOptionViewItem viewOptions() const
{
// Set icon on the top and center of combo box item.
QStyleOptionViewItem option = QListView::viewOptions();
option.decorationAlignment = Qt::AlignHCenter | Qt::AlignCenter;
option.decorationPosition = QStyleOptionViewItem::Top;
option.displayAlignment = Qt::AlignCenter;
return option;
}
};
and for the combo box:
QComboBox cb;
cb.setView(new ComboView); // Sets the custom view.
cb.addItem(QIcon("icon.png"), "Item1");
cb.addItem(QIcon("icon.png"), "Item2");
cb.show();

Alignment of qtgraphics items in qt graphics scene

I have a graphics view which contains a scene with some graphics items. How can I make those graphics items appear on the left side of the scene and not in the center ?
I have looked through many ideas but no luck.
The easiest way is to add an invisible QGraphicsRectItem as the first item.
Then add other items as children of that first item.
The center of the first item is the center of the coordination.
QGraphicsScene *scene = new QGraphicsScene(widget.graphicsView);
// ....
QGraphicsRectItem *frame = scene->addRect(-100,-100,100,100);
frame->setPen(Qt::NoPen);
// ....
// Add new items as children of frame
// Align them relative to their parent
^
|
|
-100,100 | 100,100
+-----------------------------+
| | |
| | |
| | |
| | |
| | |
| | |
| | |
---------------------------+------------------------>
| | |
| | |
| | |
| | |
| | |
| | |
| | |
+-----------------------------+
-100,100 | 100,-100
|
To make it aware to window size, override these methods:
class MainForm : public QMainWindow
{
//...
QGraphicsScene *scene; // Move it here
QGraphicsRectItem *frame; // Move it here
protected:
virtual void resizeEvent(QResizeEvent * event);
virtual bool event(QEvent * event);
virtual void resizeAction();
//...
};
void MainForm::resizeAction()
{
QRectF rect(field->rect());
widget.graphicsView->setSceneRect(rect);
widget.graphicsView->fitInView(rect, Qt::KeepAspectRatio);
}
void MainForm::resizeEvent(QResizeEvent * event)
{
resizeAction();
QMainWindow::resizeEvent(event);
}
bool MainForm::event(QEvent * event)
{
if (event->type() == QEvent::Show)
resizeAction();
return QMainWindow::event(event);
}
Scene_item.setSceneRect(x0,y0,x1,y1);

MFC toolbar on top row with right align

By default, MFC toolbar are docked on top row left align. Could anyone please tell me how to dock the MFC toolbar on top row with right align (meaning that all toolbar buttons are align to right on the top row)?
If u just want the tool bar at the right top corner, u can use the below code in CMainFrame
if (!m_wndRSToolBar.CreateEx(this, TTBSTYLE_FLAT, WS_OVERLAPPED | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMI,Rect(700,0,0,0),IDR_MAINFRAME) || !m_wndRSToolBar.LoadToolBar(IDR_MAINFRAME1))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}

CSplitterWnd flip between horizontal and vertical splitter?

Suppose I have a splitter with 2 rows.
--------
| |
--------
| |
--------
How do I make it to this
---------
| | |
| | |
| | |
---------
switch from horizontal split to vertical split
without having to re-create the whole splitter?
Code is:
if (!m_wndSplitter.CreateStatic(this, 1, 2, WS_CHILD|WS_VISIBLE))
{
TRACE0("Failed to create splitter window\n");
return FALSE;
}
if (!m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CWnd), CSize(200, 100), NULL))
{
TRACE0("Failed to create CView1\n");
return FALSE;
}
if (!m_wndSplitter.CreateView(0, 2, RUNTIME_CLASS(CWnd), CSize(500, 100), NULL))
{
TRACE0("Failed to create CView2\n");
return FALSE;
}
Don't use CreateStatic, just use Create on the splitter. Then you have a so called dynamic splitter, see more here.
When converting the splitter from horz to vert, you have to remove the views from the splitter and attach them again afterwards. You have to do this in your document-class. I can post a method to do this if needed.
Ok, here is a method to switch views in a pane:
CView* CGMBefundDoc::SwitchToView(CView* pNewView,int row,int col)
{
CMainFrame* pMainWnd = (CMainFrame*)AfxGetMainWnd();
CSplitterWnd* pSplitter = &pMainWnd->m_wndSplitter;
CView* pOldActiveView = reinterpret_cast<CView*>(pSplitter->GetPane(row,col));
ASSERT(pOldActiveView != pNewView);
// set flag so that document will not be deleted when view is destroyed
m_bAutoDelete = FALSE;
// Dettach existing view
RemoveView(pOldActiveView);
// set flag back to default
m_bAutoDelete = TRUE;
// Set the child window ID of the active view to the ID of the corresponding
// pane. Set the child ID of the previously active view to some other ID.
::SetWindowLong(pOldActiveView->m_hWnd, GWL_ID, 0);
::SetWindowLong(pNewView->m_hWnd,GWL_ID,pSplitter->IdFromRowCol(row,col));
// Show the newly active view and hide the inactive view.
pNewView->ShowWindow(SW_SHOW);
pOldActiveView->ShowWindow(SW_HIDE);
// Attach new view
AddView(pNewView);
// Set active
pSplitter->GetParentFrame()->SetActiveView(pNewView);
pSplitter->RecalcLayout();
return pOldActiveView;
}
I hope you get the idea, otherwise just ask.
-----------|----------|
| | |
| | |
| |----------|
| | |
| | |
-----------|----------|
2 column splitter with right side having 2 rows, one Up and one Down and another one with left side to the Up and Down ?