552 views
in GUI Development by

Hello,

I currently have <1920,1080> in the profile settings for ScreenSize. Now I want the window to expand as I drag it larger and automatically fill the empty area with color. I get the pixels of width and height of the window with "GetActiveWindow" & "GetWindowRect" functions.

How can I now set that my window expands automatically when I drag it larger?

Some pictures:

 

Best regards
Justin

 

1 Answer

+1 vote
by
 
Best answer

Hello Justin,

I assume you refer to the Win32 Platform Package. In this case the size of the window is predetermined by the value set in ScreenSize attribute. It is fixed and not intended to be changed at the runtime since usually the design of the application imposes the size of the window. Anyway, to support the UI with dynamic size would require multiple modifications of the Platform Package code. For the recent version 12, these would be:

1. In the WindowProc() function look for the WM_SIZE Windows message handler. Delete the original code found there. The function is found in the module gfx_system_windows.c.

2. In the WM_SIZE message handler, recreate the DIB (Windows bitmap acting as frame buffer memory) with the new window size. (use the functions CreateDIB() and DestroyDIB()).

3. In the WM_SIZE message handler, recreate the Viewport. (use the functions EwInitViewport() and EwDoneViewport()).

4. In the WM_SIZE message handler, adjust the root object dimension. For this purpose invoke the method OnSetBounds() of the root object providing to it the new dimension.

5. In the WM_SIZE message handler, update the global variable ViewerSize.

Following could be the code (without having verified that it works):

case WM_SIZE :
{
  XObject rootObject = EwGetAppRootObject();
  int     width      = LOWORD( aLParam );
  int     height     = HIWORD( aLParam );
  XPoint  size       = { width, height };
  XRect   rect       = { 0, 0, width, height };

  /* Each time the window is resized -> free the old framebuffer */
  if ( Viewport    ) EwDoneViewport( Viewport );
  if ( Framebuffer ) DestroyDib( Framebuffer );

  /* Create new framebuffer */
  if (( Framebuffer = CreateDib( width, height )) == 0 )
  {
    ... failed -> end application ...
  }

  /* Also create the viewport as bridge between the Windows DIB and the
     Graphics Engine bitmap */
  if (( Viewport = EwInitViewport( size, rect, 0, 255, Framebuffer,
        0, 0, ViewportProc )) == 0 )
  {
    ... failed -> end application ...
  }

  /* Remember the new size in the global variable */
  ViewportSize = size;

  /* update the size of the root object */
  CoreRectView__OnSetBounds( rootObject, rect );
  CoreGroup__InvalidateArea( rootObject, rect );
  EwProcessSignals();
  EwUpdateViewer();
}
break;

Now the UI application should be resizable. For example, when the size changes, the position and the size of components existing in your application can be updated. When you have a Filled Rectangle view in the background of the Application component, just configures its Layout property to be as shown below. This will have the effect of the rectangle automatically adapting the new size and so filling the entire screen:

Well, I hope it helps you further.

Best regards

Paul Banach

by

Hello Paul,

 

First of all, thank you for your quick reply and your help.

I have now added your code to my system, but "EwUpdateViewer()" is not recognized and an error occurs during the build (see image with error LNK1120 & 2019).

Someone told me that the function is defined in "ewgfx.h", but there is nothing there.

Where can I find the function or how can I define it?

 

 

Best regards Justin

by

Hello Justin,

unfortunately the function in question is static and not accessible from this module. I have overseen this. Nevertheless, it should work even without the invocation of EwUpdateViewer(). Remove it. In such case also remove the invocation of EwProcessSignals(). The update related code will then be executed during the next iteration through the Windows message loop. Following would be the resulting code:

case WM_SIZE :
{
  XObject rootObject = EwGetAppRootObject();
  int     width      = LOWORD( aLParam );
  int     height     = HIWORD( aLParam );
  XPoint  size       = { width, height };
  XRect   rect       = { 0, 0, width, height };

  /* Each time the window is resized -> free the old framebuffer */
  if ( Viewport    ) EwDoneViewport( Viewport );
  if ( Framebuffer ) DestroyDib( Framebuffer );

  /* Create new framebuffer */
  if (( Framebuffer = CreateDib( width, height )) == 0 )
  {
    ... failed -> end application ...
  }

  /* Also create the viewport as bridge between the Windows DIB and the
     Graphics Engine bitmap */
  if (( Viewport = EwInitViewport( size, rect, 0, 255, Framebuffer,
        0, 0, ViewportProc )) == 0 )
  {
    ... failed -> end application ...
  }

  /* Remember the new size in the global variable */
  ViewportSize = size;

  /* update the size of the root object */
  CoreRectView__OnSetBounds( rootObject, rect );
  CoreGroup__InvalidateArea( rootObject, rect );
}
break;

I hope it works now.

Best regards

Paul Banach

by

Hello Paul,

 

we have progress!

However, now I get an error from RootObject when building.

Everything goes up to line 636, in line 637 where "CoreGroup__OnSetBounds()" is called, rootObject is at NULL.

Then an error message appears:

 

 

Best regards

Justin

by

Ok. I suppose this is because of the first WM_SIZE message being sent still before the root object has been instantiated. Add an if-condition to perform the entire WM_SIZE message related code only when the rootObject is not NULL. For example:

case WM_SIZE :
{
  XObject rootObject = EwGetAppRootObject();

  if ( rootObject )
  {
    int     width      = LOWORD( aLParam );
    int     height     = HIWORD( aLParam );
    XPoint  size       = { width, height };
    XRect   rect       = { 0, 0, width, height };

    /* Each time the window is resized -> free the old framebuffer */
    if ( Viewport    ) EwDoneViewport( Viewport );
    if ( Framebuffer ) DestroyDib( Framebuffer );

    ...

    /* update the size of the root object */
    CoreRectView__OnSetBounds( rootObject, rect );
    CoreGroup__InvalidateArea( rootObject, rect );
  }
}
break;

Best regards

Paul

by
Thank you, I'll check on Monday to see if it works.

Have a nice weekend!

Best regards

Justin
by

Good morning Paul,

If I let the program build now, it starts. But as soon as I want to move the window, the picture goes black and the program hangs.

First it showed Error 149 [Invalid parameters to create viewport.] in the console, now Error 308 [postsignal is failed. The signal is just delivered.].

I also get an access error with rootObject.

 

Error 149:

Error 308:

Access Error:

rootObject:

 

Best regards

Justin

by

Hello Justin,

error 149 means that the first or the second parameter of EwInitViewport() is wrong resulting in an area with zero or negative size. Why this is the case, is difficult to say at the distance. Apparently the local variable width or height is <= 0. I'm not sure whether this information helps you further. If not, I can try to implement and test this application case myself. I hope to address it in the next few days. To avoid mistakes, you are working with the actual version 12?

Best regards

Paul Banach

by

Hello Paul,

Yes, i work with Version 12.

I run the lines again with breakpoints.

On the first pass, everything works fine, on the second pass, however, the width and height have values >0, but the program then hangs at

EndPaint(aWindow, &paintStruct);

up, the picture goes black and it stops responding.

I have also observed that the WM_PAINT function is not called again, or does not get any left and top values in the second pass.

Could WM_GETMINMAXINFO play a role there? This is also called before the program starts.

 

WM_PAINT:

case WM_PAINT:
    {
        PAINTSTRUCT paintStruct;
        HDC         dc = BeginPaint(aWindow, &paintStruct);
        POINT       pos;

        /* Get the current scroll position and adjust the drawing context */
        pos.x = GetScrollPos(Viewer, SB_HORZ);
        pos.y = GetScrollPos(Viewer, SB_VERT);
        OffsetWindowOrgEx(dc, pos.x, pos.y, 0);

        /* Map the framebuffer pixel from the DIB into the window context */
        if (FrameBuffer)
            SetDIBitsToDevice(dc, 0, 0, FrameBuffer->Width, FrameBuffer->Height,
                0, 0, 0, FrameBuffer->Height, FrameBuffer->Pixel,
                (BITMAPINFO*)FrameBuffer->Header, DIB_RGB_COLORS);

        EndPaint(aWindow, &paintStruct);
    }
    break;

WM_GETMINMAXINFO:

case WM_GETMINMAXINFO:
    {
        DWORD       style = WS_OVERLAPPEDWINDOW;
        DWORD       exStyle = WS_EX_CLIENTEDGE;
        MINMAXINFO* mmi = (MINMAXINFO*)aLParam;
        RECT        rect;
        SIZE        size;

        /* Which is the desired size of the window in order to show the entire
           framebuffer? */
        rect.left = 0;
        rect.top = 0;
        rect.right = ViewerSize.X;
        rect.bottom = ViewerSize.Y;
        AdjustWindowRectEx(&rect, style, 0, exStyle);
        size.cx = rect.right - rect.left;
        size.cy = rect.bottom - rect.top;
    }
    break;

Some pictures:

Left/Top = 0:

Error after second "EndPaint()":

 

Best regards Justin

by

Hello Justin,

I found the cause of the problem. It is related to the restructuring of the Win32 build environment we made same time ago. So there are more variables to take in account. Additionally the scrollbar support has to be disabled. Please follow the steps below:

1. Revert all your modifications in the Build Environment. The best is, you download a complete new, fresh version of the Windows Build Environment.

2. In the Build Environment locate the file Application\Source\ewmain.c.

3. Add following new function to the end of the file:

/* Following function has the job to adjust the size of the viewport and the
   of the root object to the values provided in the variables aWidth/aHeight. */
void EwResizeScreen( int aWidth, int aHeight, void* aFrameBuffer )
{
  XPoint size = { aWidth, aHeight };
  XRect  rect = { 0, 0, aWidth, aHeight };

  /* Each time the window is resized -> discard the old viewport ... */
  if ( Viewport ) EwDoneViewport( Viewport );
  Viewport = 0;

  /* ... and create a new viewport as bridge between the Windows DIB and the
     Graphics Engine bitmap represented by aFrameBuffer */
  if (( Viewport = EwInitViewport( size, rect, 0, 255, aFrameBuffer,
        0, 0, EwViewportProc )) == 0 )
    EwPanic();

  /* Update all relevant variables */
  DisplayInfo.FrameBuffer    = aFrameBuffer;
  DisplayInfo.DisplayWidth   = aWidth;
  DisplayInfo.DisplayHeight  = aHeight;
  DisplayInfo.ViewportX      = 0;
  DisplayInfo.ViewportY      = 0;
  DisplayInfo.ViewportWidth  = aWidth;
  DisplayInfo.ViewportHeight = aHeight;
  DisplayInfo.Context        = 0;

  /* update the size of the root object */
  CoreRectView__OnSetBounds( RootObject, rect );
  CoreGroup__InvalidateArea( RootObject, rect );
}

4. In the Build Environment locate the file Application\Source\gfx_system_windows.c.

5. In this file search for case WM_HSCROLL. Remove the entire case code block, since we don't need scrollbars anymore.

6. In this file search for case WM_VSCROLL. Remove the entire case code block, since we don't need scrollbars anymore.

7. In this file search for case WM_GETMINMAXINFO. Replace the code block by following implementation. In this implementation specify your desired min. size of the window (in the variables minWidth and minHeight). At the runtime, the window will be restricted to not become smaller:

/* Ensure, the size of the viewer window doesn't exceed the given min. value */
case WM_GETMINMAXINFO :
{
  MINMAXINFO* mmi  = (MINMAXINFO*)aLParam;
  RECT        rect = { 0, 0, 200, 200 };

  /* The min. size of the GUI application */
  int         minWidth  = 200;
  int         minHeight = 100;

  /* Estimate, how thick are the border and the title bar of the window? */
  AdjustWindowRectEx( &rect, WS_OVERLAPPEDWINDOW, 0, WS_EX_CLIENTEDGE );

  /* Limit the min. window size accordingly. Take in account the thickness of
     the border and of the title bar */
  mmi->ptMinTrackSize.x = minWidth  + (( rect.right  - rect.left ) - 200 );
  mmi->ptMinTrackSize.y = minHeight + (( rect.bottom - rect.top  ) - 200 );
}
break;

7. Search for case WM_SIZE. Replace the code block by following implementation:

/* In response to WM_SIZE adjust the size of the GUI application */
case WM_SIZE :
{
  extern void EwResizeScreen( int aWidth, int aHeight, void* aFrameBuffer );
  int         width  = LOWORD( aLParam );
  int         height = HIWORD( aLParam );

  /* Any size changes? */
  if (( width != ViewerSize.X ) || ( height != ViewerSize.Y ))
  {
    /* Each time the window is resized -> free the old framebuffer */
    if ( FrameBuffer ) DestroyDib( FrameBuffer );
    FrameBuffer = 0;

    /* Create new framebuffer */
    if (( FrameBuffer = CreateDib( width, height )) == 0 )
    {
      EwPrint( "ERROR: Failed to create a framebuffer.\n" );
      EwPanic();
    }

    /* Remember the new size in the global variables */
    ViewerSize.X = width;
    ViewerSize.Y = height;

    /* Finally re-create the viewport and update the bounds of the root object. */
    EwResizeScreen( width, height, FrameBuffer );
  }
}
break;

I hope this helps you further. We will discuss internally to eventually add this 'dynamic resizing' feature to the Windows Build Environment in the next version.

Best regards

Paul Banach

 

by

Hello Paul,

I followed your steps but I get 2 error codes in the console and the program window stays white.

 

 

I looked over the code and used breakpoints again, and I noticed that DisplayInfo." " is not used at all, but the program "starts" directly.

 

Best regards

Justin

by
I created a new project and it has the same effect as in my current project.

Best regards

Justin
by

Hi Justin,

you are right. The problem is that Windows may send WM_SIZE message just at creation time of the window, if the originally specified size of the window has been changed (e.g. due to display resolution or constraints queried via WM_GETMINMAXINFO message). The consequence, the size of the window and framebuffer changes while the initialization has not been finalized. To correctly deal with this peculiarity we need to modify the Build Environment.

As workaround, you can defer the WM_SIZE related processing. For this purpose:

Step 1: First follow the steps described above.

Step 2: Now, modify the case WM_SIZE: to be case WM_USER + 1: (instead of WM_SIZE use a message number from the user defined range) as shown below:

/* Handling the deferred WM_SIZE message */
case WM_USER + 1 :
{
  extern void EwResizeScreen( int aWidth, int aHeight, void* aFrameBuffer );
  int         width  = LOWORD( aLParam );
  int         height = HIWORD( aLParam );

  ...

Step 3: Add new case WM_SIZE: with following code:

/* In response to WM_SIZE adjust the size of the GUI application */
case WM_SIZE :
{
  /* At the initialization time, Windows may send a WM_SIZE message. However,
     at this time the application has not finalized its initialization. Therefore
     defer the size adaptation. */
  PostMessage( aWindow, WM_USER + 1, aWParam, aLParam );
}
break;

Hopefully this is the solution.

Best regards

Paul

by

The above workaround has a side effect of thin stripes appearing at the edges of the window while the window is resized. If you want to suppress it, you can modify the case WM_USER + 1 : code as shown below. This defers the first WM_SIZE message. All following messages are processed immediately:

/* In response to WM_SIZE adjust the size of the GUI application */
case WM_SIZE :
{
  static int initialized = 0;

  /* At the initialization time, Windows may send a WM_SIZE message. However,
     the application has not finalized its initialization. Therefore defer
     the size adaptation.  */
  if ( !initialized )
    PostMessage( aWindow, WM_USER + 1, aWParam, aLParam );
  else
    SendMessage( aWindow, WM_USER + 1, aWParam, aLParam );

  initialized = 1;
}
break;

Just a workaround for a workaround. As mentioned above to handle the dynamic size adaptation correctly we need to rework the Build Environment.

Best regards

Paul

by

Hello Paul,

Thank you very much for your great help!

It now works without any problems and without a single error.

I do have a question though:

How do I get rid of the small black border at the bottom and right? Or is that normal?

 

Best regards

Justin

by

Let me first address another problem I have just detected. While resizing the window, the content is switched temporarily to black. This is because Windows message loop is stopped as long as you click on the border. So please add following modification to fix it:

Step 1: First follow the above described steps.

Step 2: In the module ewmain.c add two line to the end of the function EwResizeScreen(). The modification forces an immediate screen update:

/* Following function has the job to adjust the size of the viewport and the
   of the root object to the values provided in the variables aWidth/aHeight. */
void EwResizeScreen( int aWidth, int aHeight, void* aFrameBuffer )
{
  XPoint size = { aWidth, aHeight };
  XRect  rect = { 0, 0, aWidth, aHeight };

  ...

  /* update the size of the root object */
  CoreRectView__OnSetBounds( RootObject, rect );
  CoreGroup__InvalidateArea( RootObject, rect );

  /* Force immediate screen update */
  EwProcessSignals();
  EwUpdate( Viewport, RootObject );
}

Now can you test the version of your application? Do you still observe the black stripes? If yes, have you adapted the Application component in Embedded Wizard to adjust its content so it fills the new area? For test purpose I have created a simple application with a gray rectangle in the background filling the entire Application component. In the Layout property of the Rectangle I have configured it to resize itself according to size changes of the application. In my test I don't observe any stripes anymore ... Can you check it?

by

Hello Paul,

 

I changed the code, but the black stripes are already there.

 

 

I'll create a new project right away and see if it's the same there.

Best regards
Justin

by

Okay, it was my fault, I had set something wrong in Embedded Wizard, so the black line was there.

Now it's gone and the resizing works without any problems.

Thanks Paul for your help/support!

Best regards

Justin

by

Now I found another problem.

If you minimize the program, the program crashes and the error "Failed to create framebuffer."

 

by

That's true. The problem here is that when the window is minimized, Windows sends a WM_SIZE message with size 0,0. Modifying the if-condition in case WM_USER +1: seems to help. The modification takes care to perform the operation only when the window size is valid ( width > 0 ) && ( height > 0 ):

/* Any size changes? */
if (( width > 0 ) && ( height > 0 ) &&
   (( width != ViewerSize.X ) || ( height != ViewerSize.Y )))
  {
    /* Each time the window is resized -> free the old framebuffer */
    if ( FrameBuffer ) DestroyDib( FrameBuffer );
    FrameBuffer = 0;
    ....

I keep my fingers crossed, that it is the solution ...

by
Thank you, it works!

 

Many thanks and best regards

Justin

Ask Embedded Wizard

Welcome to the question and answer site for Embedded Wizard users and UI developers.

Ask your question and receive answers from the Embedded Wizard support team or from other members of the community!

Embedded Wizard Website | Privacy Policy | Imprint

...