Qt/Windows, resizable frameless window - c++

I need to create a frameless Qt Windows app that supports resizing.
If I use
setWindowFlags(Qt::FramelessWindowHint);
then I can resize only from bottom-right corner (like with the size grip, I guess QMainWindow includes it somehow?).
Is there any easy way to make it resizable from all sides like a normal window? Maybe something platform-specific (Windows)?

Solved it using WM_NCHITTEST, based on code from https://bugreports.qt.io/browse/QTBUG-40578 (bug report not related to this).
.h
class MainWindow : public QMainWindow
{
Q_OBJECT
......
protected:
bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;
};
.cpp
bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_NCHITTEST)
{
if (isMaximized())
{
return false;
}
*result = 0;
const LONG borderWidth = 8;
RECT winrect;
GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);
// must be short to correctly work with multiple monitors (negative coordinates)
short x = msg->lParam & 0x0000FFFF;
short y = (msg->lParam & 0xFFFF0000) >> 16;
bool resizeWidth = minimumWidth() != maximumWidth();
bool resizeHeight = minimumHeight() != maximumHeight();
if (resizeWidth)
{
//left border
if (x >= winrect.left && x < winrect.left + borderWidth)
{
*result = HTLEFT;
}
//right border
if (x < winrect.right && x >= winrect.right - borderWidth)
{
*result = HTRIGHT;
}
}
if (resizeHeight)
{
//bottom border
if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOM;
}
//top border
if (y >= winrect.top && y < winrect.top + borderWidth)
{
*result = HTTOP;
}
}
if (resizeWidth && resizeHeight)
{
//bottom left corner
if (x >= winrect.left && x < winrect.left + borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOMLEFT;
}
//bottom right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOMRIGHT;
}
//top left corner
if (x >= winrect.left && x < winrect.left + borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth)
{
*result = HTTOPLEFT;
}
//top right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth)
{
*result = HTTOPRIGHT;
}
}
if (*result != 0)
return true;
QWidget *action = QApplication::widgetAt(QCursor::pos());
if (action == this){
*result = HTCAPTION;
return true;
}
}
return false;
}
implementation from Qt: Resize borderless widget did not work well: sometimes the window is moved during resizing (even in the first version without dragging)

Related

Simplifying a code snippet - vector rotation in a circle

Given the following code pattern, wherein I am trying to state a vector direction in increments of 45 degrees over the integers int x and int y, inside of a circle positioned at the origin
// x == 0 && y == 0 case is already taken cared of
if(x > 1) {
if(y == 0) {
// horizontal right
m_rotation = 0;
}else if(y < 1) {
// diagonal down right
m_rotation = 315;
} else if(y > 1) {
// diagonal up right
m_rotation = 45;
}
} else if(x == 0) {
if(y < 1) {
// vertical down
m_rotation = 270;
} else if(y > 1) {
// vertical up
m_rotation = 90;
}
} else if(x < 1){
if(y == 0) {
// horizontal left
m_rotation = 180;
}else if(y < 1) {
// diagonal down left
m_rotation = 225;
} else if(y > 1) {
// diagonal up left
m_rotation = 135;
}
}
I am looking for an elegant way to make this compact. I know there's the spaceship operator <=>, but I need to restrict myself to C++17.
Things I have tried
Nesting ternary operators with m_rotation = x > 1? (y < 1? (y == 0? 0: 315): 45): (x == 0? (y < 1? 270: 90): (y < 1? (y == 0? 180: 225): 135));, but this looks weird
I tried putting the x == 0 case inside x < 1 case and transform the later into else, but that does not simplify enough the code
Using absolute values to compare x and y, but I quickly get lost
Nothing else really, I don't know what else to try
Something like
constexpr int rotation[3][3] = {
{225, 180, 135},
{270, 0, 90},
{315, 0, 45},
};
if (x != 0 || y != 0) // if (!(x == 0 && y == 0))
m_rotation = rotation[1 + sign(x)][1 + sign(y)];
There is a closed form:
// standard sign functions
int xs = x < 0 ? -1 : x > 0;
int ys = y < 0 ? -1 : y > 0;
return 180 - 45 * (xs + 2) * ys + 90 * (xs * xs + xs) * (ys * ys - 1);
or shorter
return 180 * (x < 0 || y) - 45 * (xs + 2) * ys;

Implementing a collision in 2D platform game ( Icy Tower )

I'm fighting with this for like few days, and I have no idea, how to do that, so I'd like to ask you for help. I've got no idea how collision should look like right here, so player could jump through 'down zone' of the block, and stay right on the block.
block.cpp
bool block::CollidingWithPlayer(character& player) {
for (int i = 1; i < MAX_BLOCKS; i++) {
if (player.x + player.width >= coordinateX[i] && player.x <= coordinateX[i] + width[i] && player.y + player.height >= coordinateY[i] && player.y <= coordinateY[i] + block_height) {
player.onGround = true;
return true;
}
}
}
character.cpp
void character::startJump(map& Map, character& player) {
if (onGround)
{
vel[1] = -11.0;
onGround = false;
}
}
void character::updateJump(block& Block, character& player) {
if (!onGround) {
Block.CollidingWithPlayer(player);
vel[1] += 0.5;
y += vel[1];
x += vel[0];
}
if (y > 460){
y = 460;
vel[1] = 0.0;
onGround = true;
vel[0] = 0.0;
}
if ((x + width >= START_OF_RIGHT_WALL && x <= WALL_WIDTH + START_OF_RIGHT_WALL) || (x + width >= START_OF_LEFT_WALL &&x <= START_OF_LEFT_WALL + WALL_WIDTH)){
vel[0] *= -1;
bound = true;
if (direction == 1)
direction = 2;
else if (direction == 2)
direction = 1;
}
}

Creating a right side menu on C++ using openGL

I'm working on a project that is creating a painting program using OpenGL and GLUT on C++.
So far I have a color menu on the left side and now I am trying to make a tool menu on the right side but I can't figure out how to get it on the right.
This is what i have so far:
int inwindow(int x, int y)
{
return (x > WLEFT && x < WRIGHT && y > WBOTTOM && y < WTOP);
}
static float colormenu[][8] = {{Red}, {Orange}, {Yellow}, {Green}, {Cyan}, {Blue}, {Purple}, {Black}};
int incolormenu(int x, int y)
{
return (x >= 0 && x <= MENUWIDTH && y >= 0 && y <= HEIGHT);
}
int colormenuindex(int x, int y)
{
if(!incolormenu(x, y))
return -1;
else
return(y / BOXHEIGHT);
}
static float toolmenu[][6] = {{Pencil}, {Line}, {Box}, {Rectangle}, {Circle}, {FCircle}};
int intoolmenu(int x, int y)
{
return (x >= 0 && x <= MENUWIDTH && y >= 0 && y <= HEIGHT);
}
int toolmenuindex(int x, int y)
{
if(!intoolmenu(x, y))
return -1;
else
return(y / BOXHEIGHT);
}
void drawSketch()
{
int i;
glClearColor(Grey, 1);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(Black);
for(i = 0; i < NCOLORS; i++)
{
glColor3f(colormenu[i][R], colormenu[i][G], colormenu[i][B]);
glRecti(1, BOXHEIGHT * i + 1, MENUWIDTH - 1, BOXHEIGHT * (i + 1) - 1);
}
for(i = 0; i < NCOLORS; i++)
{
glColor3f(toolmenu[i][Pencil], toolmenu[i][Line], toolmenu[i][Box]);
glRasterPos3f(0.2, -0.8, -1.5);
}
glFlush();
}
Not sure if you are willing to use a third party solution but I've used the following project with really good results.
NanoGUI Github Project

SFML Axis independent collision

I've implemented tilemap collision into my game, it works but the problem comes when I'm colliding on one axis and trying to move on the other. I can't slide along the wall.
in Player.cpp
void Player::update(float delta, std::vector<Tile>& tiles) {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) < -20) {
newPos.y -= speed * delta;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) < -20) {
newPos.x -= speed * delta;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down) || sf::Joystick::getAxisPosition(0, sf::Joystick::Y) > 20) {
newPos.y += speed * delta;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || sf::Joystick::getAxisPosition(0, sf::Joystick::X) > 20) {
newPos.x += speed * delta;
}
sf::Vector2f oldPos = sprite.getPosition();
move(delta, newPos);
for (int i = 0; i < tiles.size(); i++) {
if (Collision::PixelPerfectTest(sprite, tiles[i].sprite) && tiles[i].collision) {
sprite.setPosition(oldPos);
newPos = oldPos;
}
}
}
void Player::move(float delta, sf::Vector2f position) {
sprite.setPosition(position);
}
In Collision.cpp
bool PixelPerfectTest(const sf::Sprite& Object1, const sf::Sprite& Object2, sf::Uint8 AlphaLimit) {
sf::FloatRect Intersection;
if (Object1.getGlobalBounds().intersects(Object2.getGlobalBounds(), Intersection)) {
sf::IntRect O1SubRect = Object1.getTextureRect();
sf::IntRect O2SubRect = Object2.getTextureRect();
sf::Uint8* mask1 = Bitmasks.GetMask(Object1.getTexture());
sf::Uint8* mask2 = Bitmasks.GetMask(Object2.getTexture());
// Loop through our pixels
for (int i = Intersection.left; i < Intersection.left + Intersection.width; i++) {
for (int j = Intersection.top; j < Intersection.top + Intersection.height; j++) {
sf::Vector2f o1v = Object1.getInverseTransform().transformPoint(i, j);
sf::Vector2f o2v = Object2.getInverseTransform().transformPoint(i, j);
// Make sure pixels fall within the sprite's subrect
if (o1v.x > 0 && o1v.y > 0 && o2v.x > 0 && o2v.y > 0 &&
o1v.x < O1SubRect.width && o1v.y < O1SubRect.height &&
o2v.x < O2SubRect.width && o2v.y < O2SubRect.height) {
if (Bitmasks.GetPixel(mask1, Object1.getTexture(), (int)(o1v.x) + O1SubRect.left, (int)(o1v.y) + O1SubRect.top) > AlphaLimit &&
Bitmasks.GetPixel(mask2, Object2.getTexture(), (int)(o2v.x) + O2SubRect.left, (int)(o2v.y) + O2SubRect.top) > AlphaLimit)
return true;
}
}
}
}
return false;
}
That's because your collision test is all or nothing. I would do extra collision tests to see if the x or y new position is valid or not, something like:
if (tiles[i].collision && Collision::PixelPerfectTest(sprite, tiles[i].sprite))
{
sf::Vector2f checkPosX = newPos;
sf::Vector2f checkPosY = newPos;
checkPosX.y = oldPos.y;
checkPosY.x = oldPos.x;
sprite.setPosition(checkPosX);
if (!Collision::PixelPerfectTest(sprite, tiles[i].sprite))
{
newPos = checkPosX;
}
else
{
sprite.setPosition(checkPosY);
if (!Collision::PixelPerfectTest(sprite, tiles[i].sprite))
{
newPos = checkPosY;
}
else
{
sprite.setPosition(oldPos);
newPos = oldPos;
}
}
}
As an aside, if you do test tiles[i].collision first you will skip the more expensive PixelPerfectTest() test for non-collision tiles due to the expression short-circuiting.

Simple Clipping in DirectX 9

I am interested in simple rect clipping in Directx9.
On the top picture you see what I get.
I want to get what is on the bottom picture without changing the coordinates and/or viewport.
Meaning, I will draw the entire circle but Directx9 will just clip it.
It would be preferable that clip rect will be given in WINDOW coordinates, so it will not be affected by current state transformations.
In additions, it should affect everything going from now on to window, including polygons, sprites, textures, text etc.
Can somone suggest how to do this?
You're describing a scissor test, which is built into the directx device.
See Scissor Test
More specifically, you just set the rectangle in screen coordinates using SetScissorRect
And then enable the scissor test by calling
device->SetRenderState( D3DRS_SCISSORTESTENABLE , TRUE );
I had the same question a month ago and I came up with a solution myself after trying to locate a clipping method, so I had to develop my own... This should work:
void Clip(LPDIRECT3DDEVICE9 device, LPDIRECT3DSURFACE9 surface, LPDIRECT3DSURFACE9 backbuffer, int x, int y, int width, int height, int destX, int destY, int destWidth, int destHeight)
{
RECT source;
if (x >= destX && (x+width) <= (destX+destWidth))
{
source.left = 0;
source.right = width;
}
else if ( (x >= destX && x <= (destX+destWidth)) && ((x+width) > (destX+destWidth)))
{
source.left = 0;
source.right = width - ((x+width) - (destX+destWidth));
source.right = abs(source.right);
}
else if (x < destX && (x+width) < (destX+destWidth))
{
source.left = abs(x);
source.right = width;
}
else if ( (x < destX) && ((x+width) > (destX+destWidth)))
{
source.left = abs(x);
source.right = source.left + (destWidth);
}
else
{
return;
}
if (y >= destY && (y+height) <= (destY+destHeight))
{
source.top = 0;
source.bottom = height;
}
else if ( (y >= destY && y <= (destY+destHeight)) && ((y+height) > (destY+destHeight)) )
{
source.top = 0;
source.bottom = height - ((y+height) - (destY+destHeight));
source.bottom = abs(source.bottom);
}
else if (y < destY && (y+height) > destY && (y+height) <= (destY+destHeight))
{
source.top = abs(y);
source.bottom = height;
}
else if ( (y < destY) && ((y+height) > (destY+destHeight)))
{
source.top = abs(y);
source.bottom = source.top + (destHeight);
}
else
{
return;
}
RECT destination;
if (x >= destX && (x+width) <= (destX+destWidth))
{
destination.left = x;
destination.right = x + width;
}
else if ( (x >= destX && x <= (destX+destWidth)) && ((x+width) > (destX+destWidth)))
{
destination.left = x;
destination.right = (destX+destWidth);
}
else if ( (x < destX) && ((x+width) < (destX+destWidth)) && ((x+width) >= x))
{
destination.left = destX;
destination.right = width - abs(x);
}
else if ( (x < destX) && ((x+width) > (destX+destWidth)))
{
destination.left = destX;
destination.right = (destX+destWidth);
}
else
{
return;
}
if (y >= destY && (y+height) <= (destY+destHeight))
{
destination.top = y;
destination.bottom = y + height;
}
else if ( (y >= destY && y <= (destY+destHeight)) && (y+height) > (destY+destHeight))
{
destination.top = y;
destination.bottom = (destY+destHeight);
}
else if (y < destY && (y+height) > destY && (y+height) <= (destY+destHeight))
{
destination.top = destY;
destination.bottom = height - abs(y);
}
else if ( (y < destY) && ((y+height) > (destY+destHeight)))
{
destination.top = destY;
destination.bottom = (destY+destHeight);
}
else
{
return;
}
device->StretchRect(surface, &source, backbuffer, &destination, D3DTEXF_NONE);
DeleteObject(&source);
DeleteObject(&destination);
};