VCL style affects non-VCL window - vcl-styles

I have a problem with VCL styles in Embarcadero C++ Berlin 10.1.
I have an application written in BCB C++ and it calls pspiHost.dll (written in VS 2017), that executes Photoshop 8bf plugin filters (https://github.com/spetric/Photoshop-Plugin-Host).
Everything works OK until I change project application appearance to some VCL Style. When plugin is called from styled application, it's window is also styled!? Also, plugin window is constantly repainted and any action in window (like panning image) is slowed down.
I have also noticed this strange behavior on one scanner preview dialog which was also styled and it was definitely not a VCL application.
How can I disable VCL styling for non-VCL windows called from DLL?
Here is an example with Charcoal Dark Slate style:
Please disregard image orientation (before correction for TBitmap style containers).
Here is an example of the same application without styling (windows default):
Styled plugin is completely useless (slow when zooming, panning or any image change). Last example is with image orientation correction, but it's not relevant for this problem (just for info).
Here is a simple application. A form that has TImage, one TPanel and two speedbuttons on panel ("Load image" and "Execute plugin").
Here is an OnClick event for "Load image" button:
//---------------------------------------------------------------------------
void __fastcall TfrmSimple::SpeedButton1Click(TObject *Sender)
{
// load BMP in Image1
Image1->Picture->LoadFromFile("974-1.bmp");
// set our bitmap to pspiHost - we know that it's 24 bit bgr, no alpha, so we know the TImgType
int w, h;
srcImage = Image1->Picture->Bitmap;
TImgType type = PSPI_IMG_TYPE_BGR;
w = srcImage->Width;
h = srcImage->Height;
// let's say we don't know if TBitmap has contiguous buffer, so we'll add scanlines one by one
pspiStartImageSL(type, w, h);
for (int i = 0; i < h; i++)
pspiAddImageSL(srcImage->ScanLine[i]);
pspiFinishImageSL(); // done
}
In this example, scanlines are "send" to pspiHost.dll one by one, so there are three API calls.
Here is OnClick event for "Execute plugin" button:
//---------------------------------------------------------------------------
void __fastcall TfrmSimple::SpeedButton2Click(TObject *Sender)
{
// let's deal with filter
if (srcImage->Width < 2 || srcImage->Height < 2)
return;
// some 8bf filter for testing
String filter = "curves3 (32 bits).8bf";
// load filter
if (pspiPlugInLoad(filter.c_str()) == 0)
{
void *FP = DisableTaskWindows((HWND)(this->Handle)); // so that plugin window stays on top like modal (required for some filters)
int rc;
// execute filter
try {
rc = pspiPlugInExecute((HWND)(this->Handle));
} catch (...) {
rc = -1;
}
EnableTaskWindows(FP); // back to normal
Image1->Refresh();
if (rc != 0)
{
// error executing
}
}
else
{
// error loading -> plugin not loaded
}
}
There are two API calls, load filter and execute filter. That's all.
The architecture is quite simple: Application -> pspiHost.dll -> 8bf filter (which is again DLL). Application appearance affects plugin window. Question is how to disable such behaviour?

Related

How to migrate a clickable Frame with ripple touch to Xamarin.Forms 2.5?

I have implemented a clickable Frame in Xamarin.Forms 2.3.4 with a custom FrameRenderer that set Clickable (and LongPressable FWIW) to true, subscribed the respective events and set the FrameRenderers foreground
TypedValue typedValue = new TypedValue();
this.Context.Theme.ResolveAttribute(Android.Resource.Attribute.SelectableItemBackground, typedValue, true);
this.Foreground = this.Resources.GetDrawable(typedValue.ResourceId, this.Context.Theme);
to achieve Material motion (ripple touch).
After updating to XF 2.5 (most likely as of 2.3.5, since fast renderers have been introduced with that release) my touch events have ceased to work. My custom renderer is assigned correctly, so are the Clickable and LongPressable properties, but nothing happens. Partially I have been able to work around the issue - at least for the moment - by subscribing to FrameRenderer.Touch and call OnClick from that event handler. This renders the app usable, but unfortunately lacks the visual feedback in form of the ripple touch effect.
Is there any way I can restore the original behavior? Is there any way to implement a clickable frame with ripple touch in XF 2.5?
With the help of this answer I have figured out a solution. Here's a draft of it:
First of all I store a local reference to my RippleDrawable (see documentation)
private void SetMaterialMotion()
{
TypedValue typedValue = new TypedValue();
this.Context.Theme.ResolveAttribute(Android.Resource.Attribute.SelectableItemBackground, typedValue, true);
this.Foreground = this.Resources.GetDrawable(typedValue.ResourceId, this.Context.Theme);
this._layerDrawable = this.Foreground as RippleDrawable;
}
then I have to subscribe to the Touch event
private void SubscribeClickEvent()
{
if (this.ClickableFrame.IsClickable)
{
this.Touch += ClickableFrameRenderer_Touch;
}
}
In my ClickableFrameRenderer_Touch I set the Pressed property and the hotspot of the RippleDrawable
private void ClickableFrameRenderer_Touch(object sender, TouchEventArgs e)
{
if(e.Event.Action == Android.Views.MotionEventActions.Down)
{
this.Pressed = true;
}
if(e.Event.Action == Android.Views.MotionEventActions.Up)
{
this._layerDrawable.SetHotspot(e.Event.GetX(), e.Event.GetY());
this.Pressed = false;
}
}
This will show the ripple touch motion (more or less) as expected. Of course this does not handle long presses, yet, but I'm on a way.
Anyway, this is not a solution I like very much. I still think that there has to be a supported way on making FastRenderers.FrameRenderer clickable. If anyone knows it: Please share your knowledge.

How can add action with QWidget.addAction()?

In Qt, I want to add some actions in a widget using QWidget.addAction().
I can easily do it with QToolBar.addAction(), but when I use QWidget.addAction(), it doesn't work.
How can I use QWidget.addAction()?
Here is my function:
void Reb::addActionToBar(QString *tabName, QAction *action)
{
//if tab exist, just add the action, else:
tab_widget->addTab(new QWidget(), *tabName);
for(int i = 0 ; i <= tab_widget->count() ; i++) {
if(tab_widget->tabText(i) == tabName) {
action.setParent(tab_widget->widget(i));
tab_widget->widget(i)->addAction(action);
}
}
}
And as you know tab_widget is a QTabWidget...
I have no error but i can't see my action in tab.
QWidget::addAction() does not do add the action to the UI - the only place where the widget's actions are shown is in the widget's context menu, given the right context menu policy.
QTabWidget has no means to display actions in its UI. Actions are usually displayed in toolbars or menubars, so you would need to add the action there.
As a side note, there is no need to pass QStrings by pointer, simply pass the QString by const reference:
void Reb::addActionToBar(const QString &tabName, QAction *action)
Also, your code has an off-by-one error, use i < tab_widget->count() instead of i <= tab_widget->count() to fix that.

How to detect QTableWidget scroll source (code itself/user (wheel)/user (scrollbar))?

I'm writing a program using Qt 4.8 that displays a table (QTableWidget) filled with filenames and file's params. First an user adds files to the list and then clicks process. The code itself updates the contents of the table with simple progress description. I want the table by default to be scrolled automatically to show the last processed file and that code is ready.
If I want to scroll it by hand the widget is being scrolled automatically as soon as something changes moving the viewport to the last element. I want to be able to override the automated scroll if I detect that it was the user who wanted to change view.
This behavior can be seen in many terminal emulator programs. When there's a new line added the view is scrolled but when user forces the terminal to see some previous lines the terminal does not try to scroll down.
How could I do that?
Solution:
I created an object which filters event processed by my QTableWidget and QScrollBar embedded inside. If I spot the event that should turn off automatic scrolling I just set a flag and stop scrolling view if that flag is set.
Everything is implemented inside tableController class. Here are parts of three crucial methods.
bool tableController::eventFilter(QObject* object, QEvent* event)
{
switch (event->type())
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::Wheel:
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
_autoScrollEnabled = false;
default:
break;
}
return QObject::eventFilter(object, event);
}
void tableController::changeFile(int idx)
{
[...]
if (_autoScrollEnabled)
{
QTableWidgetItem* s = _table.item(_engine.getLastProcessed(), 1);
_table.scrollToItem(s);
}
[...]
}
void tableController::tableController()
{
[...]
_autoScrollEnabled = true;
_table.installEventFilter(this);
_table.verticalTableScrollbar()->installEventFilter(this);
[...]
}
Thanks for all the help. I hope somebody will find it useful :)
Subclass QTableWidget and overload its wheelEvent. You can use the parameters of the supplied QWheelEvent object in order to determine if the user scrolled up or down.
Then use a simple boolean flag which is set (or reset) in your wheelEvent override. The method which is responsible for calling scrollToBottom() should then consider this boolean flag.
You will have to find a way to figure out when to set or reset that flag, e.g. always set it when the user scrolls up and reset it when the user scrolls down and the currently displayed area is at the bottom.
connect(_table->view()->verticalScrollBar(), &QAbstractSlider::actionTriggered, this, [this](int) {
_autoScrollEnabled = false;
});

Design pattern for mouse interaction

I need some opinions on what is the "ideal" design pattern for a general mouse
interaction.
Here the simplified problem. I have a small 3d program (QT and openGL) and
I use the mouse for interaction. Every interaction is normally not only a
single function call, it is mostly performed by up to 3 function calls (initiate, perform, finalize).
For example, camera rotation: here the initial function call will deliver the current first mouse position,
whereas the performing function calls will update the camera etc.
However, for only a couple of interactions, hardcoding these (inside MousePressEvent, MouseReleaseEvent MouseMoveEvent or MouseWheelEvent etc)
is not a big deal, but if I think about a more advanced program (e.g 20 or more interactions) then a proper design is needed.
Therefore, how would you design such a interactions inside QT.
I hope I made my problem clear enough, otherwise don't bother complain :-)
Thanks
I suggest using polymorphism and the factory method pattern. Here's an example:
In my Qt program I have QGraphicsScenes and QGraphicsItems with mousePressEvent, mouseMoveEvent, and mouseReleaseEvent, which look like this:
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// call factory method, which returns a subclass depending on where click occurred
dragHandler = DragHandler::createDragHandler(event /* and other relevant stuff */);
}
void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
dragHandler->onMouseMove(event);
}
void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
dragHandler->onMouseRelease(event);
delete dragHandler;
}
The idea in this particular case is that depending on where I click on CustomItem, mouse pressing, moving, and releasing will have different functionality. For example, if I click on the edge of the item, dragging will resize it, but if I click in the middle of the item, dragging will move it. DragHandler::onMouseMove and DragHandler::onMouseRelease are virtual functions that are reimplemented by subclasses to provide the specific functionality I want depending on where the mouse press occurred. There's no need for DragHandler::onMousePress because that's basically the constructor.
This is of course a rather specific example, and probably not exactly what you want, but it gives you an idea of how you can use polymorphism to clean up your mouse handling.
Qt makes this beautifully simple.
Instead of all the switch mouse_mode: stuff you used to write, simply have each mouse event handler function emit a signal ie. mouseDown/mouseUp/mousePosition and use signals/slots to route those to the appropriate model functions.
Then you can accommodate different uses of the mouse (selecting, rotating, editing etc) by connect/disconnect different SLOTS to the signal sent in the Mouse...Event()
I find Apple's UIGestureRecognizer design quite nice and extendable.
The idea is to decouple the recognition of the gesture (or interaction) and the action that will be triggered.
You need to implement a basic or abstract GestureRecognizer class that is able to recognize a certain interaction or gesture based on events MousePressEvent, MouseReleaseEvent MouseMoveEvent or MouseWheelEvent etc. GestureRecongnizers have a target to report changes periodically.
For example your very basic class would be like: (sorry my poor semi c++ pseudo-code ... recently I don't use it that much)
class Recognizer {
int state; // ex: 0:possible, 1:began, 2:changed, 3:ended/recognized 4:cancelled
protected:
void setTarget(void &theTarget); // or even better a touple, target/method. In this case target is assumed to have a method gestureHandle(Recognizer *r);
virtual void mouserPress() = 0;
virtual void mouserRelease() = 0;
virtual void mouserMove() = 0;
virtual void mouserWheel() = 0;
...
}
And if you want to detect a swipe with the mouse
class SwipeRecognizer : Recognizer {
int direction; // ex: 0:left2right 1:bottom2top 2:...
private:
void mouserPress() {
state = 0; // possible. You don't know yet is the mouse is going to swipe, simple click, long press, etc.
// save some values so you can calculate the direction of the swipe later
target.gestureHandle(this);
};
void mouserMove() {
if (state == 0) {
state = 1; // it was possible now you know the swipe began!
direction = ... // calculate the swipe direction here
} else if (state == 1 || state == 2) {// state is began or changed
state = 2; // changed ... which means is still mouse dragging
// probably you want to make more checks here like you are still swiping in the same direction you started, maybe velocity thresholds, if any of your conditions are not met you should cancel the gesture recognizer by setting its state to 4
}
target.gestureHandler(this);
};
void mouserRelease() {
if (state == 2) { // is swipping
state = 3; // swipe ended
} else {
state = 4; // it was not swiping so simple cancel the tracking
}
target.gestureHandler(this);
};
void mouserWheel() {
// if this method is called then this is definitely not a swipe right?
state = 4; // cancelled
target.gestureHandler(this);
}
Just make sure above methods are called when the events are happening and they should call the target when needed.
This is how the target will look to me:
class Target {
...
void gestureHandler(Recognizer *r) {
if (r->state == 2) {
// Is swipping: move the opengl camera using some parameter your recognizer class brings
} else if (r->state == 3) {
// ended: stop moving the opengl camera
} else if (r->state == 4) {
// Cancelled, maybe restore camera to original position?
}
}
Implementation of UIGestureRecognizer is quite nice and will allow to register several targets /method for the same recognizer and several recognizers to the same view.
UIGestureRecognizers have a delegate object that is used to get information about other gesture recognizers, for example, if two gestures can be detected at the same time, or should one must fail as soon as the other is detected, etc.
Some gesture recognizer will require more overrides than others but the big PRO of this is that their output is the same: a handler method that informs about the current state (and other info).
I think is worth taking a look at it
Hope it helps :)

How can I add controls to the title bar of a flex panel?

I'm trying to implement a collapsible TitleWindow popup by adding a handle to the top right corner of a Panel control. Unfortunately, the image that I add as the handle doesn't show up when the control is drawn. I can trigger a redraw by changing the source of the image, so apparently it's there, but it's somehow hidden.
Is there a way to make this Image control visible to the user?
Here's the code for the window:
package
{
import mx.containers.Panel;
import mx.controls.Image;
public class CollapsableWindow extends Panel
{
public function CollapsableWindow()
{
super();
}
public var close:Image;
protected override function createChildren():void
{
super.createChildren();
close = new Image();
close.source = "/assets/close.png";
close.x = this.width - 20;
close.y = 8;
this.titleBar.addChildAt(close, 0);
}
}
}
You need to create your button in the createChildren method, then position it in the updateDisplayList method:
/**
* Creates the button and adds it to the titlebar
*/
override protected function createChildren():void
{
super.createChildren();
this.myButton = new Button();
this.myButton.addEventListener(MouseEvent.CLICK, onButtonClick);
this.titleBar.addChild(this.myButton);
}
/**
* Sizes and positions the button on the titlebar
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
this.myButton.setActualSize(myButton.getExplicitOrMeasuredWidth(),
myButton.getExplicitOrMeasuredHeight());
// Position the button
var y:int = 4;
var x:int = this.width - this.myButton.width - 2;
this.myButton.move(x, y);
}
I've had issues with PNG's not showing up in our app. Every once in a while, all our PNG's disappear. To test whether you're code suffers the same problem, try using a test image that's a GIF or other format. If it works then you're having the same issue I've run into dozens of times.
On our project, we solve this in two ways:
A workaround. Toggle your project's SDK version and, for some odd reason, that fixes it
So, for example, we use SDK 3.6
I right click on the project, chose properties and go to the Flex SDK section
From there I switch to ANY OTHER SDK version (say Flex 3.2) and choose ok
This triggers an automatic build (with some kind of mysterious PNG-fixing power)
Then I go back into the project properties and return it to our version.
8 times out of 10 this restores the PNG images
This worked even when we were using Flex SDK 3.4 and 3.5
A fix. Change your PNG format to PNG-24
It seems the root problem is related to using the PNG-8 format
For our images, I open the original in Photoshop press CMD-Shft-Ctrl-S, invoking "Save For Web..."
Then I choose PNG-24 for the format, instead of PNG-8
A similar process should work in any other image editing program
I hope that helps in some way,
-- gMale

Resources