Fl_Scroll initial scroll values - fltk

I'm using FLTK for a project (the version distributed with Debian/sid 1.3.2-6+b1) and I'm having some trouble initializing Fl_Scroll's content scroll values.
I create a Fl_Double_Window and on the right side a vertical panel using Fl_Scroll, it is positioned at left x 600 and top y 24.
Then I set Fl_Scroll's type to Fl_Scroll::VERTICAL and place a Fl_Button inside, everything works fine.
The problem? Fl_Scroll initialize already scrolled with xposition() = 600 and yposition() = 24 (X and Y of the Fl_Scroll's constructor?? Is supposed to work so?), instead I want it to initialize with content scrolled at left and top 0, 0, so I tried scroll_to() in different places, before and after window's show(), on FL_SHOW of the subclassed Fl_Scroll (I didn't really need to subclass Fl_Scroll, just trying to get scroll_to() working), also on overridden draw() after
the parent draw(), even creating Fl_Scroll at 0, 0 then position() at 600, 24, but nothing.
The only way I got scroll_to() working is if I call it from an async event after the application initialize (FL_KEYUP).
Here's the overridden Fl_Double_Window window constructor:
group = new Fl_Group(0, 0, WIN_W, WIN_H);
{
menu = new Fl_Menu_Bar(0, 0, WIN_W, MENU_H);
menu->add("File/Quit", FL_CTRL+'q', cbMenuQuit_i);
glview = new CGLView(0, MENU_H, WIN_W-PROPS_W+1, WIN_H-MENU_H);
group->resizable(glview);
scroll = new CScroll(WIN_W-PROPS_W, MENU_H, PROPS_W-1, WIN_H-MENU_H);
scroll->type(Fl_Scroll::VERTICAL);
{
Fl_Pack *pack = new Fl_Pack(0, 0, PROPS_W-1, WIN_H-1);
{
btnAddLayer = new Fl_Button(0, 0, PROPS_W, 32, "##+ Add Layer");
btnAddLayer->callback(btnAddLayerCb_i, (void *)this);
}
pack->end();
}
scroll->end();
}
group->end();
end();
size_range(WIN_MIN_W, WIN_MIN_H); /* Make the window resizable */
show();

Never mind, got it, two altenative:
create Fl_Scroll at 0, 0 then position() it after scroll->end()
create Fl_Scroll at the wanted position then after scroll->end() use negative values scroll_to(-X, -Y)
In both cases is important that the widgets contained are already added (scroll_to() alter cotained widgets coords)
Isn't clear to me why X and Y of Fl_Scroll's constructor are used.

Related

Using CMFCMenuButton::SizeToContent does not seem to work as I would like. Why?

I am perplexed about the SizeToContent method of the CMFCMenuButton control.
This is my dialog in the IDE:
As you can see, I have specifically made the button wider than the two on the far right.
I added the following code to OnInitDialog:
// Resize (if required)
const auto sizNewButton = m_btnReset.SizeToContent(true);
CRect rctButton;
m_btnReset.GetWindowRect(&rctButton);
if(sizNewButton.cx > rctButton.Width())
{
m_btnReset.SizeToContent();
}
Yet, when I run my application in English:
It has made it smaller. My application supports 50+ languages by using satellite DLLs and I was hoping to only resize to content if it was required. But it seems to resize it anyway. Have I missed a step here?
I have checked the properties for the control in the IDE and it is not set to auto resize:
I notice that the help documentation states:
The new size of the button is calculated to fit the button text, image, and arrow. The framework also adds in predefined margins of 10 pixels for the horizontal edge and 5 pixels for the vertical edge.
I had a look at my button:
Default size: 48 x 23 (the GeWindowRect result).
Calculated size: 57 x 23 (the SizeToContent result).
If I adjusted my code like this:
if((sizNewButton.cx - 10) > rctButton.Width())
That would bring it down to 47 and thus would not resize. I am assuming the code is not working right because of the padded margin that GetWindowRect knows nothing about.
Searched it, and found that the problem is MFC's CMFCMenuButton::SizeToContent() implementation in afxmenubutton.cpp:
CSize CMFCMenuButton::SizeToContent(BOOL bCalcOnly)
{
CSize size = CMFCButton::SizeToContent(FALSE); // <==== The culprit!!!
size.cx += CMenuImages::Size().cx;
if (!bCalcOnly)
{
SetWindowPos(NULL, -1, -1, size.cx, size.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}
return size;
}
That is, it calls the base implementation of SizeToContent() with the bCalcOnly parameter set to FALSE, which means it will also resize the control to just fit the text (without the drop-down arrow). This is less than required for the text plus the arrow, and of course the original size is lost.
A workaround can be get the (original) width, before the SizeToContent() call, and work with this instead of the new one:
CRect rctButton;
m_btnReset.GetWindowRect(&rctButton);
const auto nOrigWidth = rctButton.Width(); // Store the original width
const auto sizNewButton = m_btnReset.SizeToContent(true); // This resizes the control!!!
if (sizNewButton.cx > nOrigWidth) // Compare to the original width rather than the new one
m_btnReset.SizeToContent();
else // Restore original width
m_btnReset.SetWindowPos(NULL, -1, -1, nOrigWidth, sizNewButton.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
Alternative Workaround:
Define a new CMFCMenuButton-based class, overriding SizeToContent() - in the implementation call the base CMFCButton::SizeToContent() with the bCalcOnly parameter passed by the caller, not with FALSE. Map the control to this class instead of CMFCMenuButton. That is use a class that fixes it. Too much of an overkill for just a workaround though.

FabricJS: Issue when changing objects coordinates inside a Group after some Transformations

I am looking for a way to space (and move in general) objects inside a group regardless of their transformation.
In the small example i have made below, you will see 2 circles and I am attempting to space them by a certain factor.
It is ok until I rotate or resize the group and it is still ok the very first time the coordinates are changed after a transformation but calling the same function twice while the spacing is still correct, the group starts jumping all over the places..
I am using Fabric JS version: 1.7.7
Here is a sample:
https://jsfiddle.net/u0patgck/
console.clear();
var canvas = new fabric.Canvas('root');
var circles = [];
circles.push(new fabric.Circle({id: "circle1", radius: 20, fill: 'green', left: 150, top: 100}));
circles.push(new fabric.Circle({id: "circle2", radius: 20, fill: 'green', left: 200, top: 100}));
g = new fabric.Group(circles);
//once circles are added to group their coordinates change so I store their new original base coordinates in custom variables
g.forEachObject(function(obj){
obj.originalLeft = obj.getLeft();
obj.originalTop = obj.getTop();
}, g);
canvas.add(g);
canvas.renderAll();
function moveCircles(){
var space = parseFloat(document.getElementById("spacingPixels").value);
g.forEachObject(function(obj){
switch (obj.id){
case "circle1":
obj.setLeft(obj.originalLeft - space)
break;
case "circle2":
obj.setLeft(obj.originalLeft + space)
break;
default:
}
})
g.addWithUpdate();
canvas.renderAll()
}
Steps to Reproduce:
Test 1 (ok):
Click the "Move Circles" button as many times you want (circles coordinates are recalculated and the group is not "misbehaving".
Test 2 (ok first time but not second time):
Rotate the group first.
Click the move circles button ONE time (all is still good)
Click the move circles button another time (you will see the group starts moving by itself around the canvas).
Expected Behaviour:
The group should not move around or change the order of the objects inside.
The group moves erratically.
At the moment I am caching the transformations manually and restoring them after changing the coordinates (like below)
https://jsfiddle.net/0tdg1dof/
var a = {scaleX: g.getScaleX(), scaleY: g.getScaleY(), skewX: g.getSkewX(), skewY: g.getSkewY(), flipX: g.getFlipX(), flipY:g.getFlipX(), angle: g.getAngle()};
fabric.util.resetObjectTransform(g);
g.addWithUpdate();
g.setScaleX(a.scaleX);
g.setScaleY(a.scaleY);
g.setSkewX(a.skewX);
g.setSkewY(a.skewY);
g.setFlipX(a.flipX);
g.setFlipY(a.flipY);
g.setAngle(a.angle);
Is there a better way to do this?
Thank you.
AlessandroDM, I believe you did right solution, because you are manipulating just only group object. If you will not manipulate (restore) group objects (parent) you will need to do manipulation each child inside the group like angle, scale, etc. Also, you have one minor bug, if you will rotate group, and after that resize it (not diagonal controls, scaleX should not be equal to scaleY) and then click "Move Circles" your group will behave as you described in Test 2. In order to fix that you will need to set scaleX and scaleY values after all your values which you want to set:
var a = {scaleX: g.getScaleX(), scaleY: g.getScaleY(), skewX: g.getSkewX(), skewY: g.getSkewY(), flipX: g.getFlipX(), flipY:g.getFlipX(), angle: g.getAngle()};
fabric.util.resetObjectTransform(g);
g.addWithUpdate();
g.setSkewX(a.skewX);
g.setSkewY(a.skewY);
g.setFlipX(a.flipX);
g.setFlipY(a.flipY);
g.setAngle(a.angle);
g.setScaleX(a.scaleX);
g.setScaleY(a.scaleY);
It is not a fabricjs bug setting scale after all manipulations, even native HTML5 canvas required scaling after all manipulations.

Phaser inputEnabled Sprite has mouseover even if not on stage

I found that if I add a Sprite (with inputEnabled = true) to a group and don't add the group to the stage, I can still interact with the Sprite (although it's not shown).
var group = App.phaser.add.group(null, null, false, false);
var bmd = App.phaser.add.bitmapData(100, 100);
bmd.ctx.beginPath();
bmd.ctx.rect(0, 0, 100, 100);
bmd.ctx.fillStyle = '#ff9900';
bmd.ctx.fill();
var sprite = App.phaser.add.sprite(0, 0, bmd);
group.addChild(sprite);
// group.visible = false;
sprite.x = 100;
sprite.y = 100;
sprite.inputEnabled = true;
sprite.input.useHandCursor = true;
The stage is completely blank (which is good). But when I move my mouse over the top left corner I see a mousecursor (and any added event handlers would also respond).
Only way to prevent this form happening is to set the group's visibility to false, but this is clearly not the best solution.
I'm doing somthing wrong or this is a bug in Phaser?
Sprites don't have to be on the display list in order to be interactive, they just have to have 'inputEnabled' set on them. This is commonly used to allow you to create 'invisible' hit areas.
If you want the sprite to be ignored for input you can call sprite.input.stop() and toggle it back on as needed with start.
Also please use Group.add otherwise the sprite doesn't get assigned a z value, throwing it out of sequence in the Group.

Flot draw additional vertical grid line

I can't get rid of vertical line in the end of graph.
I have replicated problem here:
http://jsfiddle.net/63BPw/4/
Color of grid lines is darkred and it's setted only in this part of js code:
grid: {
aboveData: false,
hoverable: true,
clickable: true,
color: "darkred",
borderWidth: {
top: 0,
bottom: 1,
left: 0,
right: 0
}
}
Console do not throw any errors. I don't use any margins from inside javascript file or external css files.
I think this is probably a bug.
The problem isn't in your grid setup, but in the drawing of each axis. Specifically, you setup 2 y-axes, one on the left and one on the right. Deep in the flot code, it decides whether to draw a "bar" to attach the ticks to based on whether the axis is the "innermost" or not. When you have 2 yaxes, it is incorrectly deciding that your right-positioned yaxis is not innermost, and therefore drawing a tick bar.
Relevant code, both bits are called per-axis:
In allocateAxisBoxFirstPhase:
/*note this is only called for axes that have tickLength set
but later it is checked as true/false, not undefined
(which is what you'd get if you set your yaxis tickLength = 0) */
var sameDirection = $.grep(all, function (a) {
return a && a.reserveSpace;
});
innermost = $.inArray(axis, sameDirection) == 0;
In drawGrid:
if (!axis.innermost) {
//in here it draws the tick bar
}
Workaround:
Find that !axis.innermost bit and change it to axis.innermost == false. Set tickLength = 0 in your 2nd yaxis. That's it.
I've implemented those two changes here: http://jsfiddle.net/63BPw/5/
Also, FYI I filed a bug about this: https://github.com/flot/flot/issues/1056

SetWindowPos() function not moving window?

I have a dialog that I want to place within another dialog and position relative to one of the controls on the main dialog.
void CspAceDlg::DrawResultsArea()
{
CWnd* pTabCtl = GetDlgItem(IDC_BUILDTABS);
CRect rectTabCtl; // Allocate CRect for control's position.
pTabCtl->GetWindowRect(&rectTabCtl);
int resX = rectTabCtl.right + 15;
int resY = rectTabCtl.top;
//RESULTS AREA
results.Create(IDD_RESULTSDIALOG, this);
results.SetWindowPos(this, resX, resY, /*608, 19, */175, 135, SWP_SHOWWINDOW);
results.ShowWindow(SW_SHOW);
}
My problem is that my dialog resource (IDD_REULTSDIALOG) has properties called X Pos and Y Pos that seem to be overriding my SetWindowPos() (and the little property tab in the resource editor won't let me leave these blank). If I set these properties to 0, 0 my dialog appears in the top left corner of the main dialog. If I set them to a number I can guess-and-test place it roughly where I want, but then running the application on different resolutions causes the dialog to appear in different spots. What I really want to do anyway is place the dialog relative to another control on my main dialog (in this case my tab control). Why is my SetWindowPos() being ignored, and how do I fix this? Should I be using a different function?
According to the documentation for SetWindowPos, if you pass in SWP_SHOWWINDOW, the window will not be moved:
If the SWP_SHOWWINDOW or SWP_HIDEWINDOW flag is set, the window cannot be moved or sized.
Figured it out myself, largely due to this thread.
My code came out looking like this:
void CspAceDlg::DrawResultsArea()
{
CRect rectTabCtl; // CRect representing tab control's position.
POINT pResXY;
POINT pResWH;
CWnd* pTabCtl = GetDlgItem(IDC_BUILDTABS);
pTabCtl->GetWindowRect(&rectTabCtl);
pResXY.x = rectTabCtl.right + 15;
pResXY.y = rectTabCtl.top;
pResWH.x = pResXY.x + 175;
pResWH.y = pResXY.y + 135;
ScreenToClient(&pResXY);
ScreenToClient(&pResWH);
//RESULTS AREA
results.Create(IDD_RESULTSDIALOG, this);
//results.SetWindowPos(this, resX, resY, /*608, 19, */175, 135, SWP_SHOWWINDOW);
results.MoveWindow(pResXY.x, pResXY.y, pResWH.x, pResWH.y, TRUE);
results.ShowWindow(SW_SHOW);
}
What fixed this problem for me, was setting the program's compatibility properties to "run this program as an administrator".

Resources