ScrollBox automatisch scrollen
-
Im freien Clientbereich der Form liegt eine ScrollBox, darin ist ein TImage zentriert. Bei großen Bildern werden Scrollbalken gebildet (wenn das Image entsprechend groß ist). Jetzt such ich nach einer Möglichkeit, automatisch scrolen zu lassen, wenn ich die Maus beim Malen über die ScrolBox hinaus bewege.
ScrollInBy ist feuerschnell, untauglich. Dann hätte ich noch Position "anzubieten" :p :
if (Bedingung)
ScrollBox->HorzScrollBar->Position += 1;Ich kann keine brauchbare Bedingung definieren. Weiß jemand Rat oder die Lösung?
-
hallo,
das selbe problem hatte ich auch schon. konnte es aber nur stümperhaft lösen.
es gibt da ne message:SendMessage(ClientScrollBox.Handle, WM_VSCROLL, SB_LINEUP, 0);
WM_HSCROLL
SB_LINEDOWN
wie gesagt es hat funktioniert, aber nicht in jeder situation bestens...mfg
murph
-
*edel*, @murphy. Das wären schon mal die benötigten API's. Wenn ich nur eine Bedingung finden könnte, die Sinn macht. Erst will gar nichts gehen, dann sausen wieder die Scrollbalken durch bis zum Anschlag. Wenn die Bewegung nur nicht so schnell wär. Mit der Maus wieder zurück, und die Bewegung stoppt. Aber dafür ist gar keine Zeit mehr, so schnell geht alles...
-
Hi Omega
Was haste Dir denn nun schon wieder einfallen lassen?
Wenn ich das richtig seh', willst Du beimm Malen ueber Anzeigegrenzen hinaus automatisch Scrollen (wenn moeglich)?
Beim FREIHAND- Zeichnen wuerde mich das stoeren1(?) :p
Hmmmm
Eine nicht elegante Form koennte das sein:
void __fastcall TForm1::IPSBoxMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { int DXR,DXL; //Abstand zum Rand rects/links int DYU,DYO; //oben/unten if (IPSBox->HorzScrollBar->Position<IPSBox->HorzScrollBar->Range) { //falls Maus am rechten Rand DXR=IPSBox->ClientWidth-X; DXL=X; if (DXR<20) //naeher als 20 Pixel am Rand { IPSBox->HorzScrollBar->Position+=(20-DXR); } } if (IPSBox->HorzScrollBar->Position>0) { //falls Maus am linken Rand DXL=X; if (DXL<20) //naeher als 20 Pixel am Rand { IPSBox->HorzScrollBar->Position-=(20-DXL); } } if (IPSBox->VertScrollBar->Position<IPSBox->VertScrollBar->Range) { //falls Maus am unteren Rand DYU=IPSBox->ClientHeight-Y; DYO=Y; if (DYU<20) //naeher als 20 Pixel am Rand { IPSBox->VertScrollBar->Position+=(20-DYU); } } if (IPSBox->VertScrollBar->Position>0) { //falls Maus am oberen Rand DYU=IPSBox->ClientHeight-Y; DYO=Y; if (DYO<20) //naeher als 20 Pixel am Rand { IPSBox->VertScrollBar->Position-=(20-DYO); } } }
Damit klinkt man sich in OnMouseMove der Scrollbox ein.
!!! 'Ne Funktion schreiben, und die zuweisen zu OnMouseMove der ScrollBox!!!Kommt die Maus naeher als 20 Pixel an einen Rand, rollt das los! :p
Je naeher dem rand desto schneller!Haken: Nur wenn Maus bewegt wird!
Koennte noch mit 'ner Property wie 'AutoScroll' gesteuert werden!(manchmal stoert das vielleicht
)
PS: Guenstiger waere es ev. zeitgesteuert mit steuerbarem Tempo???
[ Dieser Beitrag wurde am 12.03.2003 um 19:49 Uhr von DerAltenburger editiert. ]
[ Dieser Beitrag wurde am 12.03.2003 um 20:14 Uhr von DerAltenburger editiert. ]
-
hi,
ja das mit der bedingung ist nicht einfach. ich habe dazu die mausposition genommen, ich glaube im onmousemove-handler ala "if (Y < Height)" ich weiß aber nicht mehr genau. vielleicht auch mit getcursorpos die cursorposition feststellen und wenn diese eine gewisse größe über/unterschreitet dann handeln...
wie gesagt, es hat dann funktioniert, wie du schon sagtest die bewegungen sind ziemlich ruckartig, naja, vielleicht findest du einen weg...mfg
murph
-
Vorsicht, es gab nur 3 Genies: Caesar, Da Vinchi, Einstein.
Jau, @DerAltenburger, das sieht nach Volltreffer aus. Mir gelang es nicht, die Position der Maus korrekt zu erfassen. Deswegen die Zufälligkeiten.
Muß es noch umsetzen, aber an dem Weg dürfte gar nichts stören. -20 ist IMHO das Problem. Ich will nicht schon scrollen, wenn ich noch so weit vom Rand weg bin. ZB. IrfanView macht es so, das stört beim Arbeiten. Erst wenn die Maus außerhalb der ScrollBox ist, ist der richtige Zeitpunkt zum Scrollen. Aber Scrollen ist Pflicht, sonst kann man IMHO nie Ausschnitte markieren, die größer sind als der sichtbare Bildausschnitt.
Daß die Funktion nichts tut, wenn nicht gescrollt wird, ist genau das, was gebraucht wird. Einfach mit der Maus hin- und herfahren, dann scrollt es weiter. Dann wieder zurück ins Image und die Draw-Form exakt positionieren. So ist es professionell.
(Und was anderes wird hier gar nicht gebaut :p )!
Also ab in die Werkstatt und umsetzen. Gibt's hier kein Smielie für eine Flasche Sekt? Volltreffer müssen gefeiert werden. - Deine Hilfe ist schon jetzt unbezahlbar. Das ist nicht meine App, das ist deine.
-
Hm... in eine Reihe mit Caesar gestellt zu werden würde mir nicht gefallen.
-
Das saust noch blitzmäßig durch.
DXR=ScrollBox->ClientWidth-X+10; DXL=X; if (DXR<1) ScrollBox->HorzScrollBar->Position+=1;
läuft schon langsamer und beginnt erst nach dem Rand mit Scrollen.
So scrollt es rasterpixelweise bei Zoomfaktoren:
DXR=ScrollBox->ClientWidth-X+10; DXL=X; if (DXR<1 && fmod(DXR,VFac) != 0) ScrollBox->HorzScrollBar->Position+=1;
Ich kann das Scrollen stoppen und kontrollieren. Aber pixelweise Scrollen ist quälend langsam. Bei höheren Arbeitsvergrößerungen komm ich nur etwa 4 px/sec weiter. Dazu das starke Rastergeruckel.
Aber immerhin, erst mal besser als nix. Das muß noch stark optimiert werden. Noch fällt mir aber nix besseres ein (vielleicht noch mit den Werten Spielen). Meine letzten 30 Kamele und meine antike Ölquelle für eine gute Idee. :p
@murph, kram das alte Grafiktool wiederaus der Schublade. Mit etwas Ideenglück bringt man die Geschichte sicher noch nach Wunsch auf die Reihe.
Hähä...@WebFritzi, etwas Gag und Schmunzelfaktor muß dabei sein. Caesar? Der Junge paß nicht in die Reihe.
-
Und mit dem API geht es in unkontrolierten größeren Rucken. Die auskommentierte Zeile finde ich als momentan besten Kompromiß. Wenn nur die Ruckartigen Bewegungen nicht wären.
if (ScrollBox->HorzScrollBar->Position<ScrollBox->HorzScrollBar->Range) { //falls Maus am rechten Rand DXR=ScrollBox->ClientWidth-X+10; DXL=X; if (DXR<1 && fmod(DXR,VFac) != 0) SendMessage(ScrollBox->Handle, WM_HSCROLL, SB_LINEDOWN, 0); //ScrollBox->HorzScrollBar->Position+=8; }
-
SendMessage(ScrollBox->Handle, WM_HSCROLL, SB_LINEDOWN, 0);
Da passen Message und WPARAM nicht zusammen!
-
@Omega: Vergiss besser die SendMessage-Sache. Die wird im VCL-Code sowieso intern aufgerufen. Spiel mal lieber ein bisschen mit den Eigenschaften Range und Increment des ScrollBar-Elements rum.
-
Die API-Zeile ist auch unspezifisch. Sowas ist gut, um zB. im RichEdit FindText ind den Sichtbereich zu bringen oder eben die ScrollBox schnell zu bewegen.
Mit Increment hab ich noch gat nicht expirimentiert. Könnte besser sein als Position+=8, auschecken. Range? Hmmm... mit größerem Range könnte der Ablauf eleganter werden.
Das wichtigste war sicher erst mal ein Stop-Faktor. OnMouseMove wirkt ja bei solchen Operationen quasi wie eine schleife. Das muß unterbrechbar sein, sonst bekommt man dieses Durchlaufverhalten...
Uff... ich seh nix mehr. Ist schon Nacht? Ich sollte weitermachen, wenn's draußen wieder hell ist. :p
-
Hi Omega-X
Du solltest mit Lob warten bis es optimal geht!
Und diese Uebertreibung!!!
(Waeren statt Cäsarnicht Bill Gates und Gott auch genug :p )Bei Dir fetzt das durch wie'n Puma???
(Da kommt wahrscheinlich MouseMove zu schnell in rekursion?)
Da hilft nur eins:
Das Ganze NICHT in MouseMove packen, sondern in eine NORMALE public- Funktion z.B. TimeScroll(...)
Da drin:
-- Mausposition abfragen
-- UMRECHNEN auf ScrollBox- Koordinaten!!!
-- Test aehnlich wie gepostet machen und Scrollen!!!Damit kannste auch auf Maus- Positionen reagieren, die AUSSERHALB der Box liegen
Die Funktion kann von 'nem ein/aus- schaltbaren Timer der Form gerufen werden.
(Kannst auch 'n Timer in die Klasse einbauen; ist aber Verschwendung, wenn Du das in mehreren Kompos machst)Ich mach in Form ein Timer, kleines Intervall (10..100 ms)
-- Timer- Event ruft fuer alle zu steuernden Kompos die dort eingebauten
-- Routinen TimeXXX() auf.-- in den Routinen wird dann mit Teiler (Als Property steuerbar?) zum Speibiel
-- nur jeder Zehnte Aufruf wirksam!-- Ich geb' den Routinen noch das Timerintrvall als Parameter mit, dann kann
-- sich jede Kompo individuell auf beliebiges (steuerbares) Vielfache des
-- Timerintervalls synchronisieren. :p(So in etwa mach ich das fuer eine Thumbnail- Anzeige. Die rauscht sonst auch durch!)
Ich hoffe, das hilft weiter!
-
Klar, Lob erst nach getaner Arbeit. Aber wann wird das sein? Bis dahin ist die Kehle längst ausgetrocknet. :p
Deine Ideen helfen auf jeden Fall. Timer? Die Maus soll doch selbst "Timer" spielen. Ansonsten würden sich beide wohl ins Handwerk pfuschen?. Nur jeder 10. Aufruf, das geht auch in einer Schleife. Muß ich auschecken, da die Maus zum Scrollen ja hin- und herfährt.
Variablen setzen, um später Properties draus machen zu können, stößt erst mal an Grenzen:
int Dfmod = fmod(DXR,VFac);
ScrollBox->HorzScrollBar->Position+=Dfmod;Eigentlich sollte jetzt bei jeder Mausbewegung bis zum nächsten Bildpixelraster durchgescrollt werden. Es erfolgt aber gar keine Reaktion. Der Modulo besteht aber, sonst würde es auch mit der if-Bedingung nicht klappen. Es sieht so aus, als würde Position nur einen Wert aber keine Variable akzeptieren. Das macht mir einen dicken Strich durch eine ansich vielleicht gute Rechnung. Das Verhalten irritiert mich.
Mit der Umrechnung von X auf ScrollBox-Maße hab ich Probs. X=0 ist außerhalb der ScrollBox-Position, wenn schon gescrollt wurde. Ich bräuchte einen Bezug für die Umrechnung. Vielleicht sind Screen-Koordinaten die Lösung? Damit werd ich mich mal auseinandersetzen...
-
Hi
ich glaub' jetzt haben wir uns mistverstanden!
Ich geh' davon aus, dass mit OnMouseMove das Teil zu schnell scrollt. (Scrollen loest wieder OnMouseMove aus-> Scrollen > OnMouseMove..... ==Rekursion ohne Ende!!!)
Deshalb 'Timing' vorgeben (wenn Maus am Rand der Scrollbox ist!)
-- eigene 'Timer'- Funktion
-- darin Mouse->CursorPos abfragen (Punkt in Screenkoordinaten!)
-- mit Umrechnen war nicht Skalieren gemeint! Sondern Abstand DeltaX und DeltaY
-- zu ScrollBox- Umgrenzung - Alle 4 Seiten kontrollieren
-- Dazu ev. MousePosition in ClientKoordinaten DER FORM umrechnen und mit
-- Left/Top/Right/Bottom der ScrollBox in Bezug bringen!!!-- Wenn Maus an einer Kante der ScrollBox ist, dann Scrollen um ScreenPixel!
?? Scrollweite Skalieren auf Anzeige- Bild- Pixel nur bei Vergroesserter
?? Darstellun sinnvoll!?? Skalierung mit fmod == RESTWERTDIV ??? (Ich denk 'ne normale Division
?? ist besser ???*****************************************************************************
DAS GILT ALLES NUR BEI EIGENER FUNKTION, NICHT BEI ONMOUSEMOVE DER SCROLLBOX!
*****************************************************************************
-
Hi Omega-X (Die Zweite)
Bau 'mal folgendes in eine Timerroutine Deiner Form ein und kuck Dir an was passiert, wenn die Maus aussen in die Naehe Deiner ScrollBox (hier IPSBox) kommt! :p
void __fastcall TForm1::Timer1Timer(TObject *Sender) { int DXl,DXr,DYo,DYu; int Rand=16; TRect ARect,IRect; TPoint Pt; ARect.Left=IPSBox->Left-Rand; ARect.Top=IPSBox->Top-Rand; ARect.Right=ARect.Left+IPSBox->Width +2*Rand; ARect.Bottom=ARect.Top+IPSBox->Height +2*Rand; IRect.Left=IPSBox->Left; IRect.Top=IPSBox->Top; IRect.Right=IRect.Left+IPSBox->Width; IRect.Bottom=IRect.Top+IPSBox->Height; Pt=ScreenToClient(Mouse->CursorPos); if ((Pt.x>ARect.Left)&&(Pt.x<ARect.Right)&&(Pt.y>ARect.Top)&&(Pt.y<ARect.Bottom)) { if ((Pt.x>IRect.Left)&&(Pt.x<IRect.Right)&&(Pt.y>IRect.Top)&&(Pt.y<IRect.Bottom)) { Form1->Color=clBtnFace; } else { //Maus im Randbereich (Ausserhalb)!!! DXl=IRect.Left-Pt.x; DXr=Pt.x-IRect.Right; DYo=IRect.Top-Pt.y; DYu=Pt.y-IRect.Bottom; if (DXl>0) Form1->Color=DXl*16; if (DXr>0) Form1->Color=DXr*16; if (DYo>0) Form1->Color=DYo*16; if (DYu>0) Form1->Color=DYu*16; } } else { Form1->Color=clBtnFace; } } //---------------------------------------------------------------------------
Hilft das weiter?
-
*Meuter*! Was bringen mir Screen-Koordinaten?
TPoint MPt = ClientToScreen(MovePt);
ShowMessage(IntToStr(MPt.x));Image->Left ist mindestens 200 vom Bildschirmrand entfernt. Ich geh auf ein Randpixel im Image nahe Image->Left, die Message sagt "3". Ist das nicht herrlich?
TPoint ScrollBScr = ScrollBox->ClientToScreen(ScrollBScr);
So sollte es nach der Hilfe gehen, die ScrollBox mit Bildschirmkoordinaten zu berechnen. Aber was hab ich jetzt definiert? Womit kann ich was berechnen? *Oh_ich_liebe_mein_Hirn_denn_es_ist_so_erfrischend_leer* :o
Wer kann einen gepeinigten Omega-X retten? Ich brauch ein gemeinsames Bezugssystem für die Scrollbox und das darin enthaltene TImage.
Beispiel: Wenn ScrollBox->Left=52; ist, soll die Message "53" ausgeben, wenn die Maus im ersten sichtbaren Image-Pixel ist. - Ansonsten vergleich ich Elefanten mit Mäusen.
-
Sorry, war so konzentriert, hab deinen beitrag erst jetzt gesehen. Ich check das mal. Auf den ersten Blick scheint es darum zu gehen, optisch darzustellen, wann Aktionen stattfinden würden. Recht aufwändig aber primstens testgeeignet. Und wieder ab in die Werkstatt. :p
-
Hmmm... nachdem nichts geschah, hab ich Color im if-Zweig auf clRed gesetzt, im else-Zweig auf clBlue. Egal, wohin ich mit der Maus geh, else ist immer true.
Konnte den Code nicht linear übernehmen.
// läuft bei mir nicht
Pt=ScreenToClient(Mouse->CursorPos);// So geht es
TPoint CursorPos;
Pt=ScreenToClient(CursorPos);Ob das den Unterschied macht?...
-
Hi
Komisch, das muesste geh'n.
Einfache Funktion:
1.ARect festlegen = Umriss der ScrollBox um Rand groesser
2.IRect festlegen = Umriss der ScrollBox
3.Pt=ScreenToClient(Mouse->CursorPos);
--MouseKoordinaten holen (= ScreenKoordinaten)
--Umrechnen in Form- Koordinaten.
??Falls das nicht geht nimm: Form1->ScreenToClient(...)??
4.Test, ob Maus im AUSSENRECHTECK (ScrollBox + Rand!!!)
-- Wenn ja, Test ob Maus im INNENRECHTECK (ScrollBox)
---- Wenn ja, Normale Farbe
---- sonst Farbe = Abstand der Maus von Kontur
-- sonst Normale FarbeD.h.:
Wenn Maus zu weit von Scroller weg ist oder innerhalb -> normale Farbe
Wenn Maus dicht an der Box (aussen) Farbe abhaengig von Abstand!Ist nur 'n Demo: Soll nur zeigen, wie Randberuehrung zeitgesteuert moeglich ist!
Bei mir wechselt die Farbe von dunkel bis Hellrot, wenn Maus im Randbereich ist.
Je weiter weg desto Heller Rot!(bis ARect verlassen wird) :pDie Breite des Randes ist einstellbar. Ob der sensitive Bereich ausserhalb an der Box oder innerhalb liegt, haengt von Berechnung AREct / IRect ab. Mich wuerde die Mausreaktion innen stoeren. Mir ist's lieber, wenn Reaktion nur in kleinem Bereich ausserhalb erfolgt - ist aber ANSICHTSSACHE!
-
Alles hängt daran, daß ich den die Mausposition nicht in Screenkoordinaten umwandeln kann. Damit wär ich nämlich aus dem Schneider. Hab es nach deinem Beispiel auch noch mal mit der Form als Basis versucht, bringt auch kein anderes Ergebnis.
MovePt = Form1->ClientToScreen(MovePt);
Bei der erst verwendeten Methode hab ich gemerxt, daß sich der Punkt des Scrollbeginns immer weiter ims Image hinein zurückverlagert, je größer Position ist. Das war bei dem Ansatz das Haupthandicap.
Karamba, wenn die Mausposition als Screen-Kordinaten ermittelbar wäre, könnte man exakt vergleichen, ohne die Rechtecke benutzen zu müssen. Und dann die Position der ScrollBar nur über den Timer verschieben. Das wär 'ne klare Sache.
Also, ich brauch die Mausposition als Screenkoordinaten! Was mach ich falsch? Es muß gehen, wenn es in der Klasse so vorgesehen ist.
Zum letzten Absatz: Mich würde auch stören, wenn die Mausreaktion innen ist. Die muß außen liegen, dann ist alles paletti. Als Bedingung setz ich auch, daß Drawing oder Dragging true sein muß. Sonst würde jede Mausbewegung im Bildschirm was bewegen.