I am trying to figure out the basics of working with the Go GUI library, walk.
For starters, I would like to be able to
Control the window's position
hopefully in a way similar to what other languages provide (center on screen, center to parent, exact coordinates etc).
Make the window unresizable
This is the code I have, I was hoping the MaxSize declaration would solve the second problem, but it doesn't, and I was looking for some sort of a Position declaration but couldn't find anything that makes sense to me.
package main
import (
// "github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
func main() {
MainWindow{
Title: "Test",
MinSize: Size{300, 50},
MaxSize: Size{300, 50}, // Doesn't work
// Position: ... // Doesn't exist
Layout: VBox{},
Children: []Widget{
Label{Text: "Hello World"},
},
}.Run()
}
It looks like walk doesn't provide this functionality in its own API. However, walk created on top of win which actually is just a Go bindings to WinAPI. So you can easily call any WinAPI functions. For example, to show a main window on a specified position one can call SetWindowPos():
package main
import (
"github.com/lxn/win"
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
const (
SIZE_W = 600
SIZE_H = 400
)
type MyMainWindow struct {
*walk.MainWindow
}
func main() {
mw := new(MyMainWindow)
MainWindow{
Visible: false,
AssignTo: &mw.MainWindow,
}.Create()
defaultStyle := win.GetWindowLong(mw.Handle(), win.GWL_STYLE) // Gets current style
newStyle := defaultStyle &^ win.WS_THICKFRAME // Remove WS_THICKFRAME
win.SetWindowLong(mw.Handle(), win.GWL_STYLE, newStyle)
xScreen := win.GetSystemMetrics(win.SM_CXSCREEN);
yScreen := win.GetSystemMetrics(win.SM_CYSCREEN);
win.SetWindowPos(
mw.Handle(),
0,
(xScreen - SIZE_W)/2,
(yScreen - SIZE_H)/2,
SIZE_W,
SIZE_H,
win.SWP_FRAMECHANGED,
)
win.ShowWindow(mw.Handle(), win.SW_SHOW);
mw.Run()
}
To make window unresizable you need to unset WS_THICKFRAME style, as shown in the example. More information can be found here.
Related
I am working on a dialog box that is created and controlled by a host program. The host creates the window and then sends me all the messages, but this means I don't have full access to everything it's doing. (I mention this because it could be contributing to my issue.)
I would like to change the color of an LTEXT to red. I am handling the WM_CTLCOLORSTATIC message and it is working where the text is drawn. The problem I'm having is that the rectangle for the LTEXT is slightly wider than the length of the text. For the part of the control that does not contain text it is leaving the background white instead of COLOR_BTNFACE as I have specified.
Here is the code for my handler. I call it by way of HANDLE_WM_CTLCOLORSTATIC.
// color message handler
HBRUSH OnControlColor ( HWND hDlg, twobyte dlgItem, HDC hdcCtrl, int type ) override
{
if ( (dlgItem == ID_MANUAL_EDIT_WARNING) && (type == CTLCOLOR_STATIC) )
{
SetTextColor(hdcCtrl, RGB(204, 0, 0));
SetBkColor(hdcCtrl, GetSysColor(COLOR_BTNFACE));
return (HBRUSH)GetSysColorBrush(COLOR_BTNFACE);
}
return NO;
}
It seems like maybe I need to invalidate the entire client rect some way, but I am not sure how to do this. Obviously I could carefully make the rectangle the exact right size in the dialog designer, but this doesn't seem like the safest approach.
What you could do is to explicitly select your desired background brush into the control's device context; thus, when its rectangle is drawn, that brush will be used. You can do it with one additional line of code in your handler:
HBRUSH OnControlColor ( HWND hDlg, twobyte dlgItem, HDC hdcCtrl, int type ) override
{
if ( (dlgItem == ID_MANUAL_EDIT_WARNING) && (type == CTLCOLOR_STATIC) )
{
SetTextColor(hdcCtrl, RGB(204, 0, 0));
SetBkColor(hdcCtrl, GetSysColor(COLOR_BTNFACE));
SelectObject(hdcCtrl, GetSysColorBrush(COLOR_BTNFACE)); // Select the B/G brush
return (HBRUSH)GetSysColorBrush(COLOR_BTNFACE);
}
return NO;
}
My one concern here is that you're manipulating object selection in a device context that is 'owned' by another process; this could cause issues if the replaced object in that DC is user-created (but that doesn't seem to be so in this case).
I'm trying to have a grid layout with rows in which first row is a GroupWithScroller and second row is a ContainerWithLayout and has only two buttons, quit and Ok, so the second row's height must be short, but I don't know how to resize any of those. This is what I've tried:
a := app.New()
w := a.NewWindow("Title")
var (
quitButton = widget.NewButton("Quit", func() {
a.Quit()
})
okButton = widget.NewButton("Ok", func() {
confirmed = true
a.Quit()
})
)
var (
filesBox = widget.NewGroupWithScroller("Another Title",)
buttonsBox = fyne.NewContainerWithLayout(layout.NewAdaptiveGridLayout(2), quitButton, okButton)
)
for _, file := range files {
var fileCheck = check{
checked: false,
label: filepath.Base(file),
}
storeChecks = append(storeChecks, &fileCheck)
filesBox.Append(widget.NewCheck(fileCheck.label, fileCheck.toggle))
}
w.SetContent(
fyne.NewContainerWithLayout(
layout.NewGridLayoutWithRows(2),
filesBox,
buttonsBox,
),
)
w.Resize(fyne.Size{
Width: 320,
Height: 480,
})
w.ShowAndRun()
But the result window seems to halve the height for each row:
How to change second row's height?
The GridLayout is designed to keep all elements the same size. If you want the buttons to be minimum height at the bottom you probably want BorderLayout instead - setting the buttons to be in the bottom space should do what you describe.
I am trying to change cursor color when the mouse left button is in the 'hold down state'. This is supposed to work in Windows 10, so something at the OS level, not in any specific program. I would like to do this to know when "ClickLock" is enabled.
Is there anyway to achieve this?
I have tried with Autohotkey but nothing happens
; Cursor types
IDC_APPSTARTING := 32650
~LButton::
while GetKeyState("LButton", "P")
{
; this is the code to the Dll call, but I am not sure how to integrate it
hCursor:=DllCall("LoadCursor", "UInt", NULL,"Int", IDC_APPSTARTING, "UInt")
DllCall("SetCursor","UInt",hCursor)
}
return
For more info, please refer to: https://autohotkey.com/board/topic/32608-changing-the-system-cursor/
IDC_APPSTARTING := 32650
~LButton::
changeCursor(IDC_APPSTARTING)
Return
~LButton Up::
changeCursor()
Return
changeCursor(cursor := 0) {
if (cursor) {
CursorHandle := DllCall("LoadCursor", Uint, 0, Int, cursor)
Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
Loop, Parse, Cursors, `,
DllCall("SetSystemCursor", Uint, CursorHandle, Int, A_Loopfield )
} else {
DllCall("SystemParametersInfo", UInt, 0x57, UInt, 0, UInt, 0, UInt, 0 )
}
}
Have you tried anything yet? I would say probably start googling. I came across a few different resources on the first try that pointed me in the direction of the registry keys, and even a nifty powershell script to set them on demand. Do a little research, bro. Happy coding.
I'm trying to use VkKeyScan from the Window's API, however the program crashes whenever that function is called. I've had no problems with other Window's API functions I've imported and used in this way. Is there something wrong with my syscall.Syscall call?
var (
user32, _ = syscall.LoadLibrary("user32.dll")
vkKeyScan, _ = syscall.GetProcAddress(user32, "VkKeyScan")
)
func VkKeyScan(char byte) (int16, syscall.Errno) {
var nargs uintptr = 1
ret, _, callErr := syscall.Syscall(uintptr(vkKeyScan), nargs, uintptr(char), 0, 0)
return int16(ret), callErr
}
VkScanKey works in C because it’s #defined roughly like this:
#ifdef UNICODE
# define VkScanKey VkScanKeyW
#else
# define VkScanKey VkScanKeyA
#endif
So VkScanKey isn’t the real symbol—VkScanKeyW is, and that’s the only form GetProcAddress will take it in. If you had been doing proper error handling you might have noticed that GetProcAddress was failing rather than Syscall, which might have tipped you off to this fact.
A window should stay on top of all other windows. Is this somehow possible with plain x11/xlib? Googling for "Always on top" and "x11" / "xlib" didn't return anything useful.
I'd avoid toolkits like GTK+, if somehow possible.
I'm using Ubuntu with gnome desktop. In the window menu, there's an option "Always On Top". Is this provided by the X server or the window manager? If the second is the case, is there a general function that can be called for nearly any wm? Or how to do this in an "X11-generic" way?
Edit: I implemented fizzer's answer, now having following code:
XSelectInput(this->display, this->window,
ButtonPressMask |
StructureNotifyMask |
ExposureMask |
KeyPressMask |
PropertyChangeMask |
VisibilityChangeMask );
// ...
// In a loop:
if (XPending(this->display) >= 0)
{
XNextEvent(this->display, &ev);
switch(ev.type) {
// ...
case VisibilityNotify:
XRaiseWindow(this->display, this->window);
XFlush(this->display);
break;
// ...
}
}
But the eventhandling and raising nearly never gets executed even my mask is correct?!
#define _NET_WM_STATE_REMOVE 0 // remove/unset property
#define _NET_WM_STATE_ADD 1 // add/set property
#define _NET_WM_STATE_TOGGLE 2 // toggle property
Bool MakeAlwaysOnTop(Display* display, Window root, Window mywin)
{
Atom wmStateAbove = XInternAtom( display, "_NET_WM_STATE_ABOVE", 1 );
if( wmStateAbove != None ) {
printf( "_NET_WM_STATE_ABOVE has atom of %ld\n", (long)wmStateAbove );
} else {
printf( "ERROR: cannot find atom for _NET_WM_STATE_ABOVE !\n" );
return False;
}
Atom wmNetWmState = XInternAtom( display, "_NET_WM_STATE", 1 );
if( wmNetWmState != None ) {
printf( "_NET_WM_STATE has atom of %ld\n", (long)wmNetWmState );
} else {
printf( "ERROR: cannot find atom for _NET_WM_STATE !\n" );
return False;
}
// set window always on top hint
if( wmStateAbove != None )
{
XClientMessageEvent xclient;
memset( &xclient, 0, sizeof (xclient) );
//
//window = the respective client window
//message_type = _NET_WM_STATE
//format = 32
//data.l[0] = the action, as listed below
//data.l[1] = first property to alter
//data.l[2] = second property to alter
//data.l[3] = source indication (0-unk,1-normal app,2-pager)
//other data.l[] elements = 0
//
xclient.type = ClientMessage;
xclient.window = mywin; // GDK_WINDOW_XID(window);
xclient.message_type = wmNetWmState; //gdk_x11_get_xatom_by_name_for_display( display, "_NET_WM_STATE" );
xclient.format = 32;
xclient.data.l[0] = _NET_WM_STATE_ADD; // add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
xclient.data.l[1] = wmStateAbove; //gdk_x11_atom_to_xatom_for_display (display, state1);
xclient.data.l[2] = 0; //gdk_x11_atom_to_xatom_for_display (display, state2);
xclient.data.l[3] = 0;
xclient.data.l[4] = 0;
//gdk_wmspec_change_state( FALSE, window,
// gdk_atom_intern_static_string ("_NET_WM_STATE_BELOW"),
// GDK_NONE );
XSendEvent( display,
//mywin - wrong, not app window, send to root window!
root, // <-- DefaultRootWindow( display )
False,
SubstructureRedirectMask | SubstructureNotifyMask,
(XEvent *)&xclient );
XFlush(display);
return True;
}
return False;
}
You don't want to use XRaiseWindow() to try to stay on top. Some window managers will ignore it entirely. For those that don't, consider what happens if more than one app tries to do this. Boom! That's why the window manager is in charge of stacking windows, not the app.
The way you do this is to use the protocols defined in the Extended Window Manager Hints (EWMH), see: http://www.freedesktop.org/wiki/Specifications/wm-spec
Specifically here you want _NET_WM_STATE_ABOVE which is how the "Always on Top" menu item works.
If you aren't using a toolkit you'll want to get used to scavenging in toolkit source code to figure out how to do things. In this case you could look at the function gdk_window_set_keep_above() in GTK+'s X11 backend. That will show how to use the _NET_WM_STATE_ABOVE hint.
I wrote something like this in Xlib many years ago. It's a few lines of code. When your window is partially obscured you get a VisibilityNotify event, then call XRaiseWindow. Watch out for the case where two of your 'always on top' windows overlap.
Use Actual Title Buttons (http://www.actualtools.com/titlebuttons/) for example. It allows to stay any windows always on top , roll up, make transparency and etc..