How can I change the thickness of a GTKWidget? - user-interface

This image shows a scene in my current app. This appearance is undesirable. The empty GtkList is only taking half the screen. How can I make it take four fifths of the screen and the done button take up one fifth? I am using the C programming language as always and Gtk3 which I just upgraded to. I am also having trouble with fat text entries, if there is a way to adjust the thickness of widgets. Making it homogeneous makes them all the same, but how can I make it NOT homogeneous but let me decide how much of the screen each widget gets?
#include "DisplayHelp.h"
#define NOTHING
void DisplayHelp(void) {
gtk_main_quit(NOTHING);
gtk_widget_destroy(Box);
Box = gtk_vbox_new(0, 0);
GtkWidget *Button = NULL;
GtkWidget *List = gtk_list_box_new();
gtk_container_add(GTK_CONTAINER(Box), List);
gtk_container_add(GTK_CONTAINER(Window), Box);
Button = gtk_button_new_with_label("Done");
gtk_box_pack_start(GTK_BOX(Box), Button, 1, 1, FALSE);
g_signal_connect(Button, "clicked", DisplayOptions, NULL);
// I need a function to adjust the size of the button here
printf("Entering the screen: Help\n");
gtk_widget_show_all(Window);
gtk_main();
}

You can use GtkGrid, set it's vertical homogenity to TRUE and set height for each widget with respect to desired proportions:
grid = gtk_grid_new ();
gtk_grid_set_row_homogeneous (grid, TRUE);
/*l t w h*/
gtk_grid_attach (grid, top_widget, 0, 0, 1, 4);
gtk_grid_attach (grid, bot_widget, 0, 0, 1, 1);
However, it may be not the best design solution, as you are wasting space for button.

Related

How to create an imagelist from a PNG?

I've seen here that you can create an image list with transparency. It works... sort of.
I used this to create an image list for a list control. The results were a little disappointing:
The one on the left is how it should look. The one on the right is how the list control is displaying it. It looks like it just tried to use the alpha as a mask and any blended area is attempted to be approximated by dithering. Is there a way of getting this better so that I get an actual alpha blended image?
Here is the source if that makes any difference:
class CDlg : public CDialog
{
DECLARE_DYNCREATE(CDlg)
public:
CDlg(CWnd* pParent = NULL); // standard constructor
virtual ~CDlg();
// Dialog Data
enum { IDD = IDD_BS_PRINT };
CGdiPlusBitmapResource m_pBitmap;
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
virtual BOOL OnInitDialog();
DECLARE_MESSAGE_MAP()
public:
CListCtrl m_printOptions;
};
BOOL CDlg::OnInitDialog()
{
__super::OnInitDialog();
m_pBitmap.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle());
HBITMAP hBitmap;
m_pBitmap.m_pBitmap->GetHBITMAP(RGB(0, 0, 0), &hBitmap);
CImageList *pList = new CImageList;
CBitmap bm;
bm.Attach(hBitmap);
pList->Create(32, 32, ILC_COLOR32, 0, 4);
pList->Add(&bm, RGB(255, 0, 255));
m_printOptions.SetImageList(pList, LVSIL_NORMAL);
//...
return TRUE;
}
IMPLEMENT_DYNCREATE(CDlg, CDialog)
CBSPrintDlg::CBSPrintDlg(CWnd* pParent /*=NULL*/)
: CBCGPDialog(CBSPrintDlg::IDD, pParent)
{
}
CBSPrintDlg::~CBSPrintDlg()
{
}
void CBSPrintDlg::DoDataExchange(CDataExchange* pDX)
{
CBCGPDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PRINT_OPTIONS, m_printOptions);
}
For source of CGdiPlusBitmapResource implementation look here.
The original image with transparency is this:
#Barmak tried with a different image and it looks fine. I think that is because the transparency is near the edge and not located within the image. See here:
Edit ----------
First parameter in Gdiplus::GetHBITMAP should be the background color. Using RGB(0, 0, 0) as background color causes the semi-transparent pixels to match with black.
Using Gdiplus::Color(255,255,255,255) (white) it will improve the appearance (because ListView background is also white). But it's better to change the background to Gdiplus::Color(0,255,255,255) (transparent) to match any background.
{
CGdiPlusBitmapResource gdibmp;
if (gdibmp.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle()))
{
HBITMAP hBitmap;
gdibmp.m_pBitmap->GetHBITMAP(Gdiplus::Color::Transparent, &hBitmap);
ImageList_AddMasked(*pList, hBitmap, 0);
}
}
This assume images are all 32x32 pixels. If images are different sizes, they have to be resized before being added to image list.
{
CGdiPlusBitmapResource gdibmp;
if (gdibmp.Load(id, _T("PNG"), AfxGetResourceHandle()))
{
//resize image to 32x32 pixels
Gdiplus::Bitmap newBmp(32, 32, PixelFormat32bppPARGB);
double oldh = (double)gdibmp.m_pBitmap->GetHeight();
double oldw = (double)gdibmp.m_pBitmap->GetWidth();
double neww = 32;
double newh = 32;
double ratio = oldw / oldh;
if (oldw > oldh)
newh = neww / ratio;
else
neww = newh * ratio;
Gdiplus::Graphics graphics(&newBmp);
graphics.SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQualityBicubic);
graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
graphics.DrawImage(gdibmp.m_pBitmap, 0, 0, (int)neww, (int)newh);
//add `newBmp` to image list
HBITMAP hBitmap;
newBmp.GetHBITMAP(Gdiplus::Color::Transparent, &hBitmap);
ImageList_AddMasked(m_ImageList, hBitmap, 0);
}
}
Using GdiPlus::GetHICON to get the icon handle... With CGdiPlusBitmapResource class, it should be possible to use the following:
HICON hicon;
m_pBitmap.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle());
m_pBitmap.m_pBitmap->GetHICON(&hicon);
pList->Add(hicon);
or using GetHBITMAP
Also make sure Visual Styles is enabled for improved appearance of ListView icons.
Test image with transparent background:
The PNG image contains pixels that are partially transparent (alpha < 255). That's a pretty common accident with a program like Photoshop, the most likely cause is overlaying the spyglass image on top of the document image and not merging the layers correctly.
As given, the image can only look good when it is displayed on top of the light-gray or white background. But that didn't happen, the background was black. Now making the anti-aliasing pixels around the spyglass painfully obvious, they turned various shades of dark-gray depending on their alpha value and no longer blend with the white background of the document image. A very typical mishap when you use GDI functions, it does not like alpha.
You can doctor it with GDI+, ensuring that the background color is correct. But that's a fair amount of work and it still leaves you with the trouble of guessing at the original background color correctly.
Just really rather best to go back to whatever painting tool you used and fix the problem there. Quickest fix ought to be re-saving it as a 24bpp BMP image file, ymmv.

Reduce Drag & Drop Flickering

I've a problem with my code. I've created a class for Drag & Drop but during the dragging of the object i see a annoying flickering effect for the object that i'm dragging
void CDragDropListBox::DrawDragRect(CPoint point, CDragItem* DragItem,SIZE Size)
{
CDC* pDC = GetDC();
ScreenToClient(&point);
//Rect centered under mouse pointer
point.x -= Size.cx/2;
point.y -= Size.cy/2;
CRect rectFull(point,m_RectSize);
//Delete previous rect
ClientToScreen(&m_OldRect);
_DstWnd->ScreenToClient(&m_OldRect);
_DstWnd->InvalidateRect(m_OldRect, true);
_DstWnd->UpdateWindow();
//Draw new rect based on mouse position
DrawSelectFrame(pDC,rectFull);
DrawSingleItem(DragItem,pDC,rectFull);
m_OldRect = rectFull;
}
In my code every time i move the mouse i delete the previously drawn drag-rect and paint a new one, but the flickering is very fastidious...
There is anything i can do?
The main problem here is, that the erasing and painting is done in two steps.
And even if it is executed fast. It looks like flickering.
So don't use WM_ERASEBKGND, and use double buffering (with a memory DC) in WM_PAINT.
See CMemDC in Codeproject for a good and easy class.
I've edited my code for remove the use of UpdateWindow()
void CDragDropListBox::DrawDragRect(CPoint point, CDragItem* DragItem,SIZE Size)
{
CDC* pDC = _DstWnd->GetDC();
CDC dcMemory;
ScreenToClient(&point);
dcMemory.CreateCompatibleDC(pDC);
CDC* olddc= pDC;
CRect tmprect;
pDC->GetClipBox(&tmprect);
CBitmap tmpbmp;
tmpbmp.CreateCompatibleBitmap(pDC, tmprect.Width(), tmprect.Height());
CBitmap* OldBmp;
OldBmp = dcMemory.SelectObject(&tmpbmp);
point.x -= Size.cx/2;
point.y -= Size.cy/2;
CRect rectFull(point,m_RectSize);
ClientToScreen(&m_OldRect);
_DstWnd->ScreenToClient(&m_OldRect);
_DstWnd->InvalidateRect(m_OldRect, TRUE);
m_OldRect = rectFull;
ClientToScreen(&rectFull);
_DstWnd->ScreenToClient(&rectFull);
DrawSelectFrame(pDC,rectFull);
DrawSingleItem(DragItem,pDC,rectFull);
_DstWnd->ValidateRect(rectFull);
//_DstWnd->UpdateWindow();
olddc->BitBlt(tmprect.left, tmprect.top, tmprect.Width(), tmprect.Height(), pDC, tmprect.left, tmprect.top, SRCCOPY);
dcMemory.SelectObject(OldBmp);
}
Flickering less, but can i further improve?

Win32 tooltip appears on the top of the screen when it goes out on the bottom

I am using my own tooltip to display quick help about a currently selected item in my autocomplete listbox used in my editor. Just like VS does it for source code editor, when a new selection happens, the tooltip permanently and immediately pops up next to the current selection, and remains there until new selection of autocomplete listbox is gone.
This is a WTL project if that matters.
The way I create amd display my tooltip:
m_hwndTooltip = CreateWindowEx(WS_EX_TOPMOST,
TOOLTIPS_CLASS,
NULL,
TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
NULL,
NULL
);
// INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE
m_toolTipInfo.cbSize = TTTOOLINFOA_V2_SIZE;
m_toolTipInfo.uFlags = TTF_TRACK;
m_toolTipInfo.hwnd = NULL;
m_toolTipInfo.hinst = NULL;
m_toolTipInfo.uId = 0; // ??
m_toolTipInfo.lpszText = (LPTSTR) messsssssssage.c_str();
// Tool_tip control will cover the whole window
m_toolTipInfo.rect.left = 0;
m_toolTipInfo.rect.top = 0;
m_toolTipInfo.rect.right = 0;
m_toolTipInfo.rect.bottom = 0;
::SendMessage(m_hwndTooltip, TTM_SETMAXTIPWIDTH, 0, MAX_TOOLTIP_WIDTH); //set max width in pixels, AND(!!) enable multi-line support
// SEND AN ADDTOOL MESSAGE TO THE TOOLTIP CONTROL WINDOW
::SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_toolTipInfo);
::SendMessage(m_hwndTooltip, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG(x, y));
::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, true, (LPARAM)(LPTOOLINFO) &m_toolTipInfo);
This is all fine, works like a charm.
However, when I pass too high Y coord which would make tooltip out of the screen on the bottom side (eg: Screen height: 1000, and I pass Y: 950, and tooltip will be 100 height), then the Tooltip appears on the Y=0, so the screen top position, instead of repositioning to 900.
However, this works horizontally, so if it would be out on the right side (so too high X is passed), it moves left the Tooltip until it can fit on the screen.
This is very strange and weird?!
Can anyone give me a tip what is the problem here?
Not to mention, the Tooltip size is determined by the win tooltip automatically, based on the message to display + max width + font used + line numbers, so I cannot calculate a correct X,Y position before displaying it, so I need to rely on the Tooltip control.
As no reply, and I found the solution, I will share it for others:
It seems the initially 0,0 is a kind of fallback when tooltip fails to fit on the screen.
So what I do, instead of
m_toolTipInfo.rect.top = 0;
m_toolTipInfo.rect.buttom = 0;
--->
m_toolTipInfo.rect.top = rcWorkingArea.bottom - 5;
m_toolTipInfo.rect.bottom = rcWorkingArea.bottom - 5;
If tooltip cannot fit on the screen bottom side, this value will be used.
Still strange why does it work automatically with X coord...but never mind.

Flicker/dead region issues maximizing an MFC window

I'm trying to make an MFC window (a CDialog) go fullscreen whenever the user attempts to maximize it. The window is being used as an OpenGL context. I'm attempting to do everything inside of the CDialog::OnSize callback. Here's the code that I'm using:
void MyCDialogSubclass::OnSize(UINT action, int width, int height) {
CDialog::OnSize(action, width, height);
switch (action) {
case SIZE_MAXIMIZED:
if (GetStyle() & WS_OVERLAPPEDWINDOW) {
MONITORINFO screen;
screen.cbSize = sizeof(screen);
if (GetMonitorInfo(MonitorFromWindow(GetSafeHwnd(), MONITOR_DEFAULTTOPRIMARY), &screen)) {
ModifyStyle(WS_OVERLAPPEDWINDOW, 0, 0);
width = screen.rcMonitor.right - screen.rcMonitor.left;
height = screen.rcMonitor.bottom - screen.rcMonitor.top;
SetWindowPos(&wndTop, screen.rcMonitor.left, screen.rcMonitor.top, width, height, SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
}
}
break;
case SIZE_MINIMIZED:
case SIZE_RESTORED:
if (!(GetStyle() & WS_OVERLAPPEDWINDOW)) {
ModifyStyle(0, WS_OVERLAPPEDWINDOW, 0);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
}
break;
}
if (wglMakeCurrent(my_hdc, my_hglrc))
my_opengl_reshape_call(width, height);
wglMakeCurrent(NULL, NULL);
}
If I comment out the ModifyStyle() calls, everything works fine, with the obvious proviso that the window style stays normal, so there's a standard window title bar across the top of the screen that I want to get rid of. If I keep the ModifyStyle() calls and comment out the SetWindowPos() calls, the title bar and everything else disappears, but the window has a black region along the top of the screen that is the exact height of the title bar—as though it is being reserved. If I don't comment out either of the pairs of calls, as shown in the code above, the screen flickers violently. I believe it's flickering back and forth between the black region being present and not being present, but it's difficult to tell. This flickering also appears to corrupt video memory, as I get persistent artifacts in window title bars (in different applications, no less) and, once, the login picture in the Start menu was replaced with one of my OpenGL textures.
The code that I'm using is adapted from the code that Stefan linked in an answer below, from The Old New Thing, which is working better than my original code. I'm assuming this problem isn't arising from my decision not to insert code to save the window placement (per The Old New Thing), because this happens before I ever try to restore the window.
Don't maximize the window if you want it to be full screen.
Use this approach instead.

Variable item height in TreeView gives broken lines

Wee.
So I finally figured out how the iIntegral member of TVITEMEX works. The MSDN docs didn't think to mention that setting it while inserting an item has no effect, but setting it after the item is inserted works. Yay!
However, when using the TVS_HASLINES style with items of variable height, the lines are only drawn for the top part of an item with iIntegral > 1. E.g. if I set TVS_HASLINES and TVS
Here's what it looks like (can't post images WTF?)
Should I manually draw more of the lines in response to NM_CUSTOMDRAW or something?
Yes, Windows doesn't do anything with the blank space obtained from changing the height.
From the MSDN:
The tree-view control does not draw in the
extra area, which appears below the
item content, but this space can be
used by the application for drawing
when using custom draw. Applications
that are not using custom draw should
set this value to 1, as otherwise the
behavior is undefined.
Alright, problem solved.
I failed to find an easy answer, but I did work around it the hard way. It's basically just drawing the extra line segments in custom draw:
// _cd is the NMTVCUSTOMDRAW structure
// ITEMHEIGHT is the fixed height set in TreeView_SetItemHeight
// linePen is HPEN of a suitable pen to draw the lines (PS_ALTERNATE etc.)
// indent is the indentation size returned from TreeView_GetIndent
case CDDS_ITEMPREPAINT : {
// Expand line because TreeView is buggy
RECT r = _cd->nmcd.rc;
HDC hdc = _cd->nmcd.hdc;
HTREEITEM hItem = (HTREEITEM) _cd->nmcd.dwItemSpec;
if( r.bottom - r.top > ITEMHEIGHT ) {
HGDIOBJ oldPen = SelectObject( hdc, linePen );
// Draw any lines left of current item
HTREEITEM hItemScan = hItem;
for( int i = _cd->iLevel; i >= 0; --i ) {
// Line should be drawn only if node has a next sibling to connect to
if( TreeView_GetNextSibling( getHWnd(), hItemScan ) ) {
// Lines seem to start 17 pixels from left edge of control. But no idea
// where that constant comes from or if it is really constant.
int x = 17 + indent * i;
MoveToEx( hdc, x, r.top + ITEMHEIGHT, 0 );
LineTo( hdc, x, r.bottom );
}
// Do the same for the parent
hItemScan = TreeView_GetParent( getHWnd(), hItemScan );
}
SelectObject( hdc, oldPen );
}
}
The pattern from the PS_ALTERNATE brush sometimes doesn't align perfectly with line drawn by the control, but that's hardly noticeable. What's worse is that even though I have the latest common controls and all the service packs and hotfixes installed, there are still bugs in TreeView documented way back in 2005. Specifically, the TreeView doesn't update its height correctly. The only workaround I've found for that is to force some collapsing/expanding of nodes and do a few calls to InvalidateRect.
If the variable-height nodes are at the root level, though, there doesn't appear to be anything you can do. Luckily I don't need that.

Resources