Asynchronous afterBeingResized()?


#1

i wonder if there is a way to obtain something like resized(), which
is not called every pixel drag, but after the user have released the grab component (could be the border, or could be the ResizerComponent).
i don’t know to keep track of this by myself, start a thread that polls the size of the window… and such stuff…
anyone know some easy sulutions ?

lou


#2

you could subclass the resizableCornerComponent or whatever you’re using, and override the mouseUp function.


#3

Could use an AsyncUpdater that gets triggered by the resized() callback - that’d help consolidate the number of callbacks you process…


#4

or, you could alternatively check the state of the mouse buttons when the resize happens…

if (ModifierKeys::getCurrentModifiers().isLeftButtonDown())
{
   hasBeenResized (); //
}

not tried yet but it may work.

also, as that may be a bit inefficient, you may want to instead use a timer, and perform this check in the callback. just start the timer at the end of resized(), and it will only perform this check when the component hasn’t been resized for a short while.


#5

haydxn: i’m using the standard ResizerCornerComponent that comes with the DialogWindow. i have to investigate its mouseUp then…

jules: but how i can get rid of the component being repainted after being resized ?


#6

ResizableCornerComponent is a subclass of Component; the mouseUp(const MouseEvent& e) function is a virtual member function of Component, which you have access to if you subclass ResizableCornerComponent.

e.g.

class MyResizableCornerComponent : public ResizableCornerComponent
{
private:

   Component* owner;
public:

   MyResizableCornerComponent (Component* const target)
      : ResizableCornerComponent (target)
   {
      owner = target; // take the Component* so we can access it too
   }

   void mouseUp (const MouseEvent& e)
   {
      owner->postCommandMessage(1); // or any old id
   }
};

then in your resized component, you’d change your ResizableCornerComponent to MyResizableCornerComponent, and implement this function:

void handleCommandMessage (int id)
{
   if (id == 1) hasBeenResized ();
}

… and of course declare and define your function "void hasBeenResized ()"
again, i’ve not tried this, so sorry if it’s flawed. There are many ways you could achieve this.


#7

[quote=“haydxn”]
also, as that may be a bit inefficient, you may want to instead use a timer, and perform this check in the callback. just start the timer at the end of resized(), and it will only perform this check when the component hasn’t been resized for a short while.[/quote]

i’m triggering the timer for other stuff. maybe i have to use a background low priority thread. anyway your leftmousebutton hack don’t work well. i’ll have to investigate.


#8

if you did this, however, you’d have to do the following too:

  • put your ‘resized’ code into hasBeenResized
  • make sure call hasBeenResized() whenever you set the size from any other method…

it’s a bit of a tricky predicament. personally i’d just use a timer and resize the contents a short while after resizing has stopped. you are aware that you can add a timer as a member, not just as a base class? i use this method to make resizing more efficient in TrackPAD3.


#9

hey jules but when i have to trigger the AsyncUpdater ? i trigger from the resized() function, but how i know when the user finished to resize ? i have to track down myself ?


#10

what is it you need this function for? perhaps if you can explain the scenario we might be able to come up with a better solution.


#11

i have in the main component several cells filled with an image like this:

[code] if (bigImageOne != 0)
{
if( hqButton->getToggleState() )
g.setImageResamplingQuality(Image::ResamplingQuality::mediumResamplingQuality);
else
g.setImageResamplingQuality(Image::ResamplingQuality::lowResamplingQuality);

		g.drawImageWithin(bigImageOne,
			imageRectangleOne.getX(),
			imageRectangleOne.getY(),
			imageRectangleOne.getWidth(),
			imageRectangleOne.getHeight(),
			Justification::centred,
			false,
			false);
	}[/code]

if i resize the component, i adjust the size of the cells to fit in the main component area. is obvious that the images get resized too. but i don’t want to resize the images in the meanwhile the component get resized (cause slowing down the gui), i want the images get resized after the release of the resizer component border (or corner). all in one single paint call.
now i’m starting a timer in the resized function. take tracks to resize, then in the timer i check for it, and do the resize of the image, then repaint. this works but i think in this situation there are too much paint the same…

[code]
… timerCallback()…
if( hasResized )
{
const int margin = 5;
imageRectangle.setBounds(list->getRight()+ margin,
list->getY(),
getWidth() - list->getRight()-2*margin,
list->getHeight());

		hasResized = false;
		repaint();
	}

	stopTimer();[/code]

if i’m unable to control the repaint from the resized… shouldn’t a component
implement setRepaintsOnResized(true||false) like setRepaintsOnMouseActivity() ?

just thought…

lou


#12

what i don’t understand is the speed. maybe i have to build a Graphics object that gets speedup by GDI+ or AntiGrain (which are lot faster, with antigrain for example i can resize with medium quality 10 images 800x600 without noticeable gui slowdown ). Look at google Picasa for example, it resizes hundreds of images (with subsampling and antialiasing) at the speed of light… with juce if i resize one image with poor quality i loose the responsivity of the app (and also visual stuff like the dropshadows get broken on resize).


#13

i found that using the drawImageWithin (…) i was always having trouble with the resizing being too greedy. I faced exactly this problem in TrackPAD3.

Instead of using drawImageWithin() and letting it do the resizing, i resized the image itself and just used drawImageAt (…).

these functions may not be directly useful to you, but they may give you some ideas.

NOTE: in my case, my component has a link to the file the image comes from, so it can get the original file. I am using the ImageCache to get the original image, however, so you should be able to come up with a similar solution where the File object is stored elsewhere (if you’re getting the image from a file in the first place… either way, you have access to the original image to rescale the new copy).

Image* loadImageFromFile ()
{
      if (fileToUse.existsAsFile())
      {
            return ImageCache::getFromFile (fileToUse);
      }
      else return 0;
}

/** This checks to see if the image is already at the correct
     size for the current dimensions of this widget. If the size
     is wrong, it will be resized. If it's already been set to
     the correct size, no resizing will take place, and the image
     will be used as is. */
bool imageNeedsResizing ()
{
     if (hasImage ())
     {
          if (
          // here we check the dimensions.
          // if the image has been resized, then:
          // - one dimension will match the corresponding panel dimension,
          // - the other will be less than or equal to its corresponding dimension.
               ( (imageToUse->getWidth () == getWidth ())
               && (imageToUse->getHeight () <= getHeight ()) )
                    ||									     
               ( (imageToUse->getHeight () == getHeight ())
               && (imageToUse->getWidth () <= getWidth ()) )
	)
               return false;	// we don't need to resize.
     }
     return true;
}



void resizeImage()
{
     if (imageNeedsResizing ())
     {
          clearImage ();
          Image* originalImage = loadImageFromFile();
          if (originalImage)
          {
               int imageW = originalImage->getWidth();
               int imageH = originalImage->getHeight();
               int newH, newW;
               const float imageRatio = imageH / (float)imageW;
               const float targetRatio = getHeight() / (float)getWidth();

               if (imageRatio <= targetRatio)
               {
                    newW = getWidth();
                    newH = roundFloatToInt (newW * imageRatio);
               }
               else
               {
                    newH = getHeight();
                    newW = roundFloatToInt (newH / imageRatio);
               }

               setImage (originalImage->createCopy ( newW, newH, imageQuality ));
               ImageCache::release (originalImage);
          }
          repaint();
     }
}

void resized()
{
     // The timer is used to make sure that the image is only resized
     // a few moments after resizing has stopped. If this was not used,
     // the image would be recalculated with every change in size, which
     // would be incredibly slow. This keeps things efficient, although it
     // does mean that the image resizes in steps.
     startTimer ( imageResizeDelay );
}

/** The resizing action stopped a moment ago, so now resize the image. */
void timerCallback () { stopTimer(); resizeImage(); repaint(); }

#14

hmmm. sounds a bit like how my GridLimitedResizerComponent works. It works as part of my LayerOuterer system. The resizes don’t fire unless certain criteria are met. christ knows right now cos I’m not at work. I’m sure I did it by overriding certain mouse events and not letting them pass unless I said so.

My LayerOuterer system is a component resize manager that you can invoke at runtime then move/resize/change properties of child components of the “managed” component (top level window usually)

Anyway it uses a snapping grid (optional) which means the resize of a component only happens near a snap point. works very well. my boss can alter the layout of an app at will, so exact layouts are no longer my responsibility.

as I say, cant remember how I did it. will look tommorow.


#15

haydxn: looking at the Component::setBounds code i see that the repaint() get called BEFORE actually triggering the resized() callback. so u actually get repaint() called every time you get resize AND also you repaint when calling the timerCallback. actually this works but not in the way i would.
any possibilities to control better the repaint of the component when resizing ?
hey jules i need some advice on AsyncUpdater… how this can consolidate the number of callbacks that get called if the repaint method is triggered before i got the resized() called ?


#16

It has to repaint every time a component gets resized, otherwise you’d see a big smeary mess while you’re resizing the component - not very professional.

Sounds like what you’re really asking for is one of those little outline bounding boxes like on old-fashioned Windows, that stretches while you’re dragging, and then when you let go, the window itself moves to the new position. If so, write one! Hacking the way components repaint wouldn’t be a good solution.

But if the problem is that your paint() takes too long, then that’s probably the thing you should be worrying about. Maybe use off-screen images to buffer the component’s content, and rebuild those images less often than the component gets painted.


#17

yeah i’ll do like this. transferring to a background thread the recalculation of what to paint and then i do only a blit in the main paint. this is a good approach… but still needs some advice on how to make consistent callback calls, and avoid too frequent consequent calls of the same functions…


#18

Read the help for AsyncUpdater, it’s explained in there. Also check out ChangeBroadcaster.


#19

ok i’ll do… > man juce
but i need some practical examples on how to use that classes, and in what cases to use’em…


#20

i’ve just tried the AsyncUpdater but the calls to triggerAsyncUpdate in the resize component are too frequent. I’ve adopted haydxn Timer solution and it works nice, currently resizing the image once before get it blitted.