June 22, 2024, 07:33:48 PM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Scrolling and ScrollWindowEX()

Started by Bruce Peaslee, March 25, 2006, 12:09:19 PM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

Bruce Peaslee

I have a window with a lot of rectangles and print. The scollbars are working fine â€ââ,¬Â I even got the mouse wheel to workÂÃ,  ;) â€ââ,¬Â but I am redrawing everything which causes a lot of screen flicker. The window is cleared, then redrawn.

If I use ScrollWindowEX, I get a nice scrolling without flicker, but the top or bottom of the window (depending on scroll direction) gets messed up. I suppose I have to redraw that portion of the window that comes from "off screen", but I can't figure out how.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Parker

It looks like you have to override the WndProc and call BeginPaint and EndPaint yourself (don't let the paint message pass through though), because it will tell you what region needs to be redrawn, but since Aurora does that internally, GetUpdateRgn says it will return an empty region.

Mike Stefanik

March 25, 2006, 02:37:35 PM #2 Last Edit: March 25, 2006, 02:44:12 PM by Mike Stefanik
Yup, I think Parker is right in your case. I know when I was working on the terminal emulator class for SocketTools, I needed to handle all of the low-level drawing and updates myself, otherwise regions of the display wouldn't be redrawn correctly.

I get the impression that you either need to draw using the Aurora classes/functions (where it manages device contexts, objects, etc.) or you can do things at a lower-level using the Windows API directly ... but you want to use one way or the other. Mixing and matching isn't a good idea, in my experience.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Bruce Peaslee

I need more helpÂÃ,  ???

Is there an example I can study? It would seem my problem would be common, but I can't get anything useful off of the internet.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

Bruce,
Your handling the scrolling and calling ScrollWindowEx yourself correct?  Then figuring out the invalid area is just a matter of calculating the direction times the scroll amount.  For example if you've scrolled down then the invalid area at the bottom of the client will be the same size as the scroll amount;

rectClient = GetClientRect();
rectClient.top = rectClient.bottom - scrollamount;

And if you have scrolled up then

rectClient.bottom = rectClint.top + scrollamount;

This rectangle is the area of the windows client you need to redraw.

Paul
Ionic Wind Support Team

Bruce Peaslee

This is the code I am trying (without success):


myWindow::WndProc(unsigned int message, unsigned int wparam, unsigned int lparam), int
{
int nWheelClicks;
int nMinPosition;
int nMaxPosition;
int nPos;
PAINTSTRUCT ps;
rect rectClient;

select message
{
case WM_MOUSEWHEEL:
nWheelClicks = wparam/65536/120; // plus or minus 1, depending on wheel direction
GetSBRange(ASBS_VERT, nMinPosition, nMaxPosition);
nPos = GetSBPos(ASBS_VERT) - (100*nWheelClicks);
if(nPos < nMinPosition) nPos = nMinPosition;
SetSBPos(ASBS_VERT, nPos);
m_nYoffset = -nPos;
//CreateMap(m_nXoffset,m_nYoffset); // this works, but jerks
ScrollWindowEX(this->m_hwnd,0,100*nWheelClicks,null,null,null,0,2);
rectClient = GetClientRect();
if (nWheelClicks = -1)
{
rectClient.top = rectClient.bottom - 100;
}
else
{
rectClient.bottom = rectClient.top + 100;
}

InvalidateRect(this->m_hwnd,rectClient,true);
//UpdateWindow(this->m_hwnd);

case WM_PAINT:
MessageBox(this,"Case 15)","",0); // this never executes
BeginPaint(this->m_hwnd, &ps);
CreateMap(m_nXoffset,m_nYoffset);
EndPaint(this->m_hwnd, &ps);
}
returnÂÃ,  CWindow!!WndProc(message, wparam, lparam);
}


It doesn't appear that WM_PAINT works, among other thingsÂÃ,  ;)
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

WM_PAINT is only sent when the window is invalidated.  Which also means you can use an autodraw window using straight API code.  The autodraw window (AWS_AUTODRAW) captures the WM_PAINT message to blit the internal bitmap to the window.

If you use my method, of calculating the rectangle that needs to be drawn in, you don't need the API stuff.
Ionic Wind Support Team

Bruce Peaslee

Quote from: Ionic Wizard on March 25, 2006, 10:35:59 PM
WM_PAINT is only sent when the window is invalidated.ÂÃ,  Which also means you can use an autodraw window using straight API code.ÂÃ,  The autodraw window (AWS_AUTODRAW) captures the WM_PAINT message to blit the internal bitmap to the window.

If you use my method, of calculating the rectangle that needs to be drawn in, you don't need the API stuff.

I'm just not getting this. What exactly must I do if I make it an AWS_AUTODRAW window?

By the way, this works unless I move the mouse wheel too fast:


myWindow::WndProc(unsigned int message, unsigned int wparam, unsigned int lparam), int
{
int nWheelClicks;
int nMinPosition;
int nMaxPosition;
int nPos;
PAINTSTRUCT ps;
rect rectClient;
const dy = 50;

select message
{
case WM_MOUSEWHEEL:
nWheelClicks = wparam/65536/120; // plus or minus 1, depending on wheel direction
GetSBRange(ASBS_VERT, nMinPosition, nMaxPosition);
nPos = GetSBPos(ASBS_VERT) - (dy*nWheelClicks);

if(nPos < nMinPosition)
{
nPos = nMinPosition;
SetSBPos(ASBS_VERT, nPos);
m_nYoffset = -nPos;
return 0; // don't scroll
}
if(nPos > nMaxPosition)
{
nPos = nMaxPosition;
SetSBPos(ASBS_VERT, nPos);
m_nYoffset = -nPos;
return 0; // don't scroll
}

SetSBPos(ASBS_VERT, nPos);
m_nYoffset = -nPos;

rectClient = GetClientRect();
if (nWheelClicks = -1)
{
rectClient.top = rectClient.bottom - dy;
}
else
{
rectClient.bottom = rectClient.top + dy;
}

ScrollWindowEX(this->m_hwnd,0,dy*nWheelClicks,null,null,null,0,0);
InvalidateRect(this->m_hwnd,rectClient,true);

case WM_PAINT:
BeginPaint(this->m_hwnd, &ps);
CreateMap(m_nXoffset,m_nYoffset);
EndPaint(this->m_hwnd, &ps);
}
return CWindow!!WndProc(message, wparam, lparam);
}
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

LarryMc

QuoteBy the way, this works unless I move the mouse wheel too fast:

The solution couldn't be easier!

"If it hurts when you do that, then DON'T DO THAT!" ;D

Larry
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

Ionic Wind Support Team

WM_PAINT isn't sent for autodraw windows, and that is by design.   If you wan't to use WM_PAINT then you will have to do all the of the drawing yourself in response to that message and use a non autodrawn window...don't know how much clearer I can state that ;)

Also regardless of that fact your BeingPaint/EndPaint functioons won't work either.  Aurora based windows have a default API handler that gets called for every message.  So what happens is somehting like this:

sub DefaultHandler(hwnd as UINT, uMsg as UINT, wParam as UINT, lParam as UINT),INT
{
     select(uMsg)
     {
           case WM_PAINT:
                     pWnd->HandlePaint(wParam,lParam);
....
}

CWindow::HandlePaint calls BeginPaint after it gets the update rect from Windows and if it is an autodrawn window it calls BitBlt to copy the internal bitmap to the window.  If it is a non-autodrawn window then it calls the WndProc

WndProc(WM_PAINT,lparam,wparam);
EndPaint(m_hWnd,ps);

So if you really want to do it the hard way then you will have to create your own Window class (not Aurora class) and have your own API handler.  Or you can subclass an Aurora window to insert your own message handler. 

I still think your going about it the wrong way though.  Use an autodraw window, forget about WM_PAINT and InvalidateRect and just draw to the invalid rectangle directly in response to the WM_MOUSEWHEEL message.

  ScrollWindowEX(this->m_hwnd,0,dy*nWheelClicks,null,null,null,0,0);
  CreateMap(m_nXoffset,m_nYoffset);

But restrict your drawing to the area that has been scrolled.  I could help more if I could see the rest of the code.  Since I have no idea how your drawing in the first place.
Ionic Wind Support Team

Bruce Peaslee

I always prefer the simplest way. ;)ÂÃ,  ÂÃ, I'll give it another shot.

The source code without mousewheel scrolling is in Software Projects | Hood Map.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

I'll look at your code when I get the chance.  After thinking about it there is no reason I can' t have the handler send WM_PAINT to WndProc for an autodraw window as well.  I'll make that change for the next update.
Ionic Wind Support Team

Bruce Peaslee

Is this a Windows thing? I do not understand why the window data outside of the visible area isn't stored someplace to be simply scrolled into view as needed. In my program the painting far exceeds the size of the window. Do these painting commands just go into the ozone?

??? ???
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

In Windows the display area is temporary.  You draw into it using a device context and when something covers your window it is all lost and has to be redrawn again.  Drawing outside of the window is clipped since you would be drawing on other windows, the system screen ,etc.  There is only one display surface afterall. And yes the clipping is a part of Windows.

The Aurora AUTODRAW window creates an off screen bitmap the size of the system screen, for every window you create.  When you use any drawing command with an autodrawn window the operation is redirected to this bitmap instead of the display surface.

When an Aurora program receives a WM_PAINT message, it checks to see if the window is an autodrawn one and if so copies the contents of that off screen bitmap to the window display surface using BitBlt.   So in essance everything is saved off and you don't need to keep redrawing it when the window is invalidated.

All is good except an autodraw window won't work with ScrollWindowEx properly.  ScrollWindowEx makes a copy of the current window display to it's own bitmap, shifts it and recopies the shifted image back onto the display surface.  There is no way for the internal drawing routine to know you've done this so that is why you have to use a non-autodrawn window, the default BTW, and do things the normal 'Windows' way.

The normal 'Windows' way is to redraw everything in response to WM_PAINT, offsetting your drawing an appropriate amount for the scrollbar positions.  WM_PAINT is sent everytime the window is invalidated, meaning something corrupted the display and you need to redisplay the overwritten portions.

You avoid redrawing the entire display by using clipping regions in the device context.

So the answer is yes.  When you draw outside of the window border, with a non autodrawn window, your drawing operation is effectively ignored by windows. 

Paul.



Ionic Wind Support Team

Bruce Peaslee

Now that is a great explanation. Thanks a lot.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

I should add that it really isn't just a 'Windows' thing.  All OS's simulate discrete display surfaces by restricting drawing to the portion of the main display surface your window is associated with.  The illusion of multiple display surfaces, AKA windows, is just that.  An illusion since the video card is only showing you one surface, the primary one.   

The only OS I have used that had built in off screen drawing was the Amiga OS.  Which is what autodrawn windows are meant to simulate.

Ionic Wind Support Team

Bruce Peaslee

March 29, 2006, 01:53:48 PM #16 Last Edit: March 29, 2006, 07:07:50 PM by peaslee
If you haven't had time to look at how I draw the window data, I can tell you it's complicated. It will be hard to compute what part of it will need redrawing. So I was wondering if it would be possible to copy the window into memory (a bitmap) and then use BitBlt to copy back just the part that needs redrawing.

EDIT: I think I've almost got it to work. More later.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Bruce Peaslee

April 01, 2006, 11:38:47 AM #17 Last Edit: April 01, 2006, 11:43:37 AM by peaslee
I'm making progress. The scrolling works very well - no flicker - and I can get rectangles to draw. I also can get text to draw on the bitmap, but I am unable to change the font. I tried looking at SetFont.src, but the line

font = CreateFontIndirectA(lf);

gives an "invalid assignment" error. I was wondering if there is an easier way to change the font.

Thanks.

EDIT:  I forgot to mention that since the bitmap is larger than the screen, I am drawing first to the bitmap then BitBlt-ing the appropriate portion when the user scrolls.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

'font' is an unsigned int and 'lf' is a logfont structure correct?

If your using API drawing commands you can still use GetHDC and ReleaseHDC from the window class.  Which allows changing the font.

//from within a derived window class method ...
SetFont(...)
hdc = GetHDC();
...draw with the hdc
ReleaseHDC(hdc);

Ionic Wind Support Team

Bruce Peaslee

I can't get that to change the font going to the bitmap. Here is my approach:


myWindow::CreateMap2(), void
{
int i,j;
int x,y;
int width, height;
int nWidth, nHeight;
rect RectClient;
rect *myRect = new(RECT,1);
unsigned int hPen, hOldPen;
unsigned int hBrush, hOldBrush;
unsigned intÂÃ,  font, oldfont;
LOGFONT lf;

LoadData();

DrawMode(TRANSPARENT);

//Prepare logical font
ZeroMemory(lf,LEN(LOGFONT));
lstrcpynA(lf.lfFaceName,"Courier New",32);
lf.lfHeightÂÃ,  = -7;
lf.lfWeightÂÃ,  = 500;
lf.lfQuality = 1;
lf.lfPitchAndFamily = 0;
font = CreateFontIndirectA(lf); // compiler doesn't likeÂÃ,  :<(
oldfont = SelectObject(m_hdcCompatible,font);

//Fill background color of bitmap
myRect->leftÂÃ,  ÂÃ, = 1;
myRect->rightÂÃ,  = 4000;
myRect->topÂÃ,  ÂÃ,  = 1;
myRect->bottom = 4000;
FillRect(m_hdcCompatible,myRect,CreateSolidBrush(GetSysColor(15)));

hPenÂÃ,  ÂÃ,  = CreatePen(0,1,BLACK);
hOldPen = SelectObject(m_hdcCompatible,hPen);

hBrush = CreateSolidBrush(RGB(151,40,0));
hOldBrush = SelectObject(m_hdcCompatible,hBrush);

SetTextColor(m_hdcCompatible,RED);

for(i = 0; i < g_nLots; i++)
{
x = Lot[i].x;
y = Lot[i].y;
widthÂÃ,  = Lot[i].width;
height = Lot[i].height;
Rectangle(m_hdcCompatible,x,y,x+width,y+height);

//draw lot number
myRect->leftÂÃ,  ÂÃ, = x+10;
myRect->rightÂÃ,  = x+40;
myRect->topÂÃ,  ÂÃ,  = y+10;
myRect->bottom = y+40;
DrawTextA(m_hdcCompatible,Using("0###",Lot[i].nLotNo),-1,myRect,0);

}
rectClient = GetClientRect();
nWidth = rectClient.right - rectClient.left;
nHeight = rectClient.bottom - rectClient.top;
BitBlt(m_hdcThis,0,0,nWidth,nHeight,m_hdcCompatible,0,0,SRCCOPY);

//clean up
SelectObject(m_hdcCompatible, hOldPen);
DeleteObject(hPen);
SelectObject(m_hdcCompatible, hOldBrush);
DeleteObject(hBrush);
delete myRect;
return;
}


Note that font = CreateFontIndirectA(lf); fails.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

Should be

CreateFontIndirectA(&lf);

Depends on how you declared the import.  And what you have as it's return type. 

import unsigned int CreateFontIndirectA(LOGFONT *lf);

Would be the correct way.  A common mistake is to use UINT when you haven't #typdef'd it yet.
Ionic Wind Support Team

Bruce Peaslee

Got it! Thanks.

I'm getting quite the education with this project.ÂÃ,  :D
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Bruce Peaslee

OK, I'm done. To check and see if it was all worth it, go to the project forum and download.

http://www.ionicwind.com/forums/index.php?topic=476.msg4355#msg4355
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Ionic Wind Support Team

You'll be happy to know that I've changed OnPaint so that your program will use the DC returned by the internal BeginPaint().  This DC contains the update region so flicker free drawing is much easier to do. 

All of the built in drawing commands will use this DC when called from your OnPaint handler in a non-autodrawn window.  I'll have another gui.lib update soon and a new example. 
Ionic Wind Support Team