How to draw buttons in TStringGrid cells - c++

I am trying to customize a TStringGrid by adding visual objects to cells within the grid. One column needs to contain standard windows push buttons in each row and another column needs to contain a drop down with pre-defined options.
From what I have read the best way to achieve this is to draw the buttons manually in the OnDrawCell event handler. All of the examples I have found use DrawFrameControl() which does not draw a themed button like you would expect in Windows 7 or later.
Is there an equivalent function to DrawFrameControl() that will allow me to draw a themed button and if so can someone please give an example of how I might use it?
I also tried creating a vector of TButtons and setting the parent of each button to be the StringGrid and placing each button within the relevant cell. This also works but does now allow for scrolling of the grid when there are more cells that can be displayed visible area.
I am using RAD Studio 10.2 C++ builder and using the BCC32C compiler (clang-enhanced). It is a VCL WIN32 application.

Is there an equivalent function to DrawFrameControl() that will allow me to draw a themed button
The Win32 DrawFrameControl() function is for drawing non-themed UI controls. To draw themed UI controls, you need to use the Win32 Theming functions instead - DrawThemeBackground(), DrawThemeEdge(), DrawThemeText(), etc. These functions are wrapped for you by the VCL's Vcl.Themes unit. In particular, use the TThemeServices class, which has various Draw...() methods that you can use when TThemeServices.Available and TThemeServices.Enabled are both true.
I also tried creating a vector of TButtons and setting the parent of each button to be the StringGrid and placing each button within the relevant cell. This also works but does now allow for scrolling of the grid when there are more cells that can be displayed visible area.
Correct. You would have to subclass the StringGrid to intercept the scrolling so you can reposition the buttons manually.

Just for completeness here is the code I got working in order to draw a Windows Themed button in a TStringGrid cell:
void __fastcall TForm_Controller::StringGrid1DrawCell(TObject *Sender, int ACol,
int ARow, TRect &Rect, TGridDrawState State)
{
TStringGrid *grid;
bool ButtonDown = false, ButtonHot = false, ButtonInFocus = false;
TThemedElementDetails LDetails;
TTextFormatFlags LTextFormat;
TColor LColor, TempColor;
TCustomStyleServices *LStyle;
int XPos, YPos;
TPoint points[3];
grid = (TStringGrid *)Sender;
//a cell with a button ('+' or '-')
if((ACol == 0) && (ARow > 0) && grid->Cells[ACol][ARow].Length())
{
Rect.Left -= 4; //override padding so button fills entire cell
if ((FocusCell.X == ACol) && (FocusCell.Y == ARow))
ButtonHot = true;
if ((CellDown.X == ACol) && (CellDown.Y == ARow))
ButtonDown = true;
LStyle = StyleServices();
if (LStyle->Enabled && LStyle->Available)
{
if (ButtonDown)
LDetails = LStyle->GetElementDetails(tbPushButtonPressed);
else if (ButtonHot)
LDetails = LStyle->GetElementDetails(tbPushButtonHot);
else if (ButtonInFocus)
LDetails = LStyle->GetElementDetails(tbPushButtonDefaulted);
else
LDetails = LStyle->GetElementDetails(tbPushButtonNormal);
LStyle->DrawElement(grid->Canvas->Handle, LDetails, Rect);
if (LStyle->GetElementColor(LDetails, ecTextColor, LColor))
grid->Canvas->Font->Color = LColor;
grid->Canvas->Font->Size = 13;
LTextFormat = (tfCenter);
LStyle->DrawTextA(grid->Canvas->Handle, LDetails, grid->Cells[ACol][ARow], Rect, TTextFormat() << tfCenter << tfVerticalCenter);
}
else //themed drawing not available so revert to old style
DrawButtonFace(grid->Canvas, Rect, 1, bsAutoDetect, true, ButtonDown, ButtonInFocus);
}
}
I then use the OnMouseDown, OnMouseMove, OnMouseLeave and OnMouseUp events to determine what state the buttons need to be in using 3 TPoint objects FocusCell, PrevCell, CellDown.

Related

Qt GUI application on macOS: how to find the currently active screen?

We are developing a macOS application whose GUI is relying on Qt.
At startup, we want to show() the QMainWindow at a specific location on the currently active screen (with multi screen systems in mind).
Is there a way to get the QScreen representing the currently active screen?
From our test, QGuiApplication::primaryScreen() is the first screen (which is consistent with the name), but we cannot find an equivalent for the active screen.
Qt5 provides functionality to do so, the QWindow::setScreen method sets the screen on which the window should be shown.
Any widget provides access to this pointer via QWidget::windowHandle():
QWidget * widget = new QWidget();
auto screens = qApp->screens();
// compute the index
widget->windowHandle()->setScreen(screens[index]);
widget->showFullScreen();
To get the screen number, you can use the mouse position and assume that the screen with the mouse is the one with the current focus:
QPoint globalCursorPos = QCursor::pos();
int mouseScreen = qApp->desktop()->screenNumber(globalCursorPos);
So the final code can be something like that:
QWidget * widget = new QWidget();
const auto globalCursorPos = QCursor::pos();
const auto mouseScreen = qApp->desktop()->screenNumber(globalCursorPos);
widget->windowHandle()->setScreen(qApp->screens()[mouseScreen]);
widget->showFullScreen();
Windows
If this approach does not fit your needs, you will need to perform some OS calls.
For instance, on Windows, you can use MonitorFromWindow:
HMONITOR active_monitor_number = MonitorFromWindow(GetActiveWindow(), MONITOR_DEFAULTTONEAREST);
If you need more information about the screen, you can use Qt or GetMonitorInfo.
I am not a Mac OS X developer, but it may exist a similar API
I did it in the following way:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QPoint topLeft = QPoint(0, 0);
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
if (nullptr != currentScreen) {
topLeft = currentScreen->geometry().topLeft();
}
#else
QPoint topLeft =
qApp->desktop()
->screenGeometry(qApp->desktop()->screenNumber(QCursor::pos()))
.topLeft();
#endif
someWidget->move(mapFromGlobal(topLeft) + QPoint(offset, offset));
Note that, sometimes, you may get nullptr for currentScreen (in my case if primary screen is at the bottom and mouse pos at the bottom or left edge on the primary screen).

Gtk Move window beyond constraints

I am currently writing a gtk program that uses a custom title bar (i.e., it is not being decorated by the window manager). But since using a custom title bar also disables support of dragging the window around, I wrote my custom drag function which moves the window by calling window.move(x, y):
bool on_titlebar_drag(GdkEvent* event)
{
static int drag_x_offset = 0;
static int drag_y_offset = 0;
int x, y;
if (event->type == GDK_BUTTON_PRESS)
{
drag_x_offset = event->button.x;
drag_y_offset = event->button.y;
} else if(event->type == GDK_BUTTON_RELEASE) {
drag_x_offset = 0;
drag_y_offset = 0;
} else if(event->type == GDK_MOTION_NOTIFY) {
x = event->motion.x_root - drag_x_offset;
y = event->motion.y_root - drag_y_offset;
mainWindow.move(x, y);
}
return true;
}
This works just fine except of the fact that it cannot move the window beyond the screen limits, like the normal behaviour for other windows, so you can drag it "out of sight" to make place for others.
I am trying to resize the window smaller as soon as it touches the screen by calling window.resize(width, height) but this is not what I intend to do, because resizing also resizes the window contents to the smaller scale, while I would just like to make its physical size smaller.
I have also tried using set_allocation and size_allocate, these two didnt make any change at all.
My question is, do you know a way to either be able to move the window beyond the screen borders (not totally, but in a way that the window is not fully on-screen), or to change the size of the window without resizing its contents?
If you are using GTK 3.10 or newer you can use gtk_window_set_titlebar
gtk_window_set_titlebar (GtkWindow *window, GtkWidget *titlebar) the only arguments you need are the window that you want to customise and the GtkWidget that will serve as the titlebar. Using GtkHeaderBar is suggested but in your case you can use any custom GtkWidget and get draggable bar which will tug the whole window as would the one from the window manager.

QT Graphic scene/view - moving around with mouse

I created my own classes (view and scene) to display image and objects I added to it, even got zoom in/out function implemented to my view, but now I have to add new functionality and I don't even know how to start looking for it.
Whenever I press the scroll button of my mouse and hold it - I wish to move around the scene, to see different parts of it - just like I would with sliders. It is supposed to be similar to any other program allowing to zoom in/out to image and move around zoomed picture to see different parts of it.
Unfortunately - I don't even know how to look for some basic, because "moving" and similar refer to dragging objects around.
EDIT 1
void CustomGraphicView::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() == Qt::MidButton)
{
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
translate(event->x(),event->y());
}
}
Tried this - but it is working in reverse.
I suppose you know how to handle events using Qt.
So, to translate (move) your view use the QGraphicsView::translate() method.
EDIT
How to use it:
void CustomGraphicsView::mousePressEvent(QMouseEvent* event)
{
if (e->button() == Qt::MiddleButton)
{
// Store original position.
m_originX = event->x();
m_originY = event->y();
}
}
void CustomGraphicsView::mouseMoveEvent(QMouseEvent* event)
{
if (e->buttons() & Qt::MidButton)
{
QPointF oldp = mapToScene(m_originX, m_originY);
QPointF newP = mapToScene(event->pos());
QPointF translation = newp - oldp;
translate(translation.x(), translation.y());
m_originX = event->x();
m_originY = event->y();
}
}

Highlighting TGrid row in code

I have a custom TGrid control which I am trying to make it so when the mouse hovers over a row, that row is highlighted. Rows are automatically highlighted if I used the arrow keys to navigate the grid. However, I am not sure how to replicate this effect for navigation with the mouse.
Currently, I have a MouseMove function which can detect which row of the grid the mouse is hovering over.
void __fastcall TFmSearchBar::GridMouseMove(TObject *Sender, TShiftState Shift, float X, float Y)
{
int rowSelected = FGrid->RowByPoint(X, Y);
if(rowSelected >= FGrid->RowCount)
rowSelected = FGrid->RowCount - 1;
if(rowSelected != -1)
{
FGrid->SelectRow(rowSelected);
}
}
I originally thought using the SelectRow function would achieve the desired effect, however nothing happens when that method is used. Additionally I have tried using
FGrid->SelectCell(0, rowSelected);
which did not work either.
I have verified that I am getting the right row from the function by setting a row's text to bold when the mouse hovers over it using
FGrid->RowObjects[rowSelected]->SetBold();
you must enable following options for TGrid component:
1) RowSelect = True
2) AlwaysShowSelection = True
Tested with Delphi XE8 -- works fine. My code:
procedure TForm1.Grid1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
begin
Grid1.SelectRow(Grid1.RowByPoint(X, Y));
end;
if you want, i could provide you DFM file either.

Usage of CScrollView in MFC application

I am using CSCrollView window for our Application in which i have table drawn in View.
I have derived the CMYclass from CSCrollView, But whenevr i am scrolling the window up and down whatever i have drwan is getting erased. How i can acheive it this... i need to perform same actvity like a Word Pad is doing with images and Text. I want to keep scroll the View Vertically. Till the page ends.
Here is the code snippet:-
void CMyView::OnInitialUpdate()
{
CSize sizeTotal;
// TODO: calculate the total size of this view
sizeTotal.cx = 450;
sizeTotal.cy = 700;
SetScrollSizes(MM_TEXT, sizeTotal);
}
void CMyView::OnDraw(CDC* pDC)
{
for(int i = 1;i<50;++i)
{
AddRow(pDC);
TopPos = Height+TopPos;// ![Outpu Window Image][1]
nCountRow++;
}
}
it is only drawing 18 rows, but when iam scrolling down above drawn content is no more and also there is nothing coming up in scrolled area.
Is there anything more need to add?
Thanking for help
Regards,
Mukesh
Long time I have used CScrollView. Here is my hint: Try mapping modes other than MM_TEXT. Also look up other functions in CScrollView. I suggest to draw simple stuff first than some complicated rows.