From 9ad1900437b5d93ef64516197cc4f379c21a4866 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 10 Apr 2026 09:47:36 +0200 Subject: [PATCH 01/10] [graphics] improve TArrow drawing Use PaintArrowNDC in the TArrow::Paint Use attributes ModifyOn methods Shorten options handling code --- graf2d/graf/src/TArrow.cxx | 56 +++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/graf2d/graf/src/TArrow.cxx b/graf2d/graf/src/TArrow.cxx index b9f2ff414943f..85dd8ad19236b 100644 --- a/graf2d/graf/src/TArrow.cxx +++ b/graf2d/graf/src/TArrow.cxx @@ -116,9 +116,7 @@ void TArrow::Copy(TObject &obj) const void TArrow::Draw(Option_t *option) { - Option_t *opt; - if (option && strlen(option)) opt = option; - else opt = (char*)GetOption(); + Option_t *opt = option && *option ? option : GetOption(); AppendPad(opt); } @@ -130,15 +128,15 @@ void TArrow::Draw(Option_t *option) /// - if `option=""`, `option` will be the current arrow option TArrow *TArrow::DrawArrow(Double_t x1, Double_t y1,Double_t x2, Double_t y2, - Float_t arrowsize ,Option_t *option) + Float_t arrowsize, Option_t *option) { Float_t size = arrowsize; if (size <= 0) size = fArrowSize; if (size <= 0) size = 0.05; - const char* opt = option; - if (!opt || !opt[0]) opt = fOption.Data(); - if (!opt || !opt[0]) opt = "|>"; + Option_t* opt = option; + if (!opt || !*opt) opt = GetOption(); + if (!opt || !*opt) opt = "|>"; TArrow *newarrow = new TArrow(x1,y1,x2,y2,size,opt); newarrow->SetAngle(fAngle); TAttLine::Copy(*newarrow); @@ -154,14 +152,9 @@ TArrow *TArrow::DrawArrow(Double_t x1, Double_t y1,Double_t x2, Double_t y2, void TArrow::Paint(Option_t *option) { if (!gPad) return; - Option_t *opt; - if (option && strlen(option)) opt = option; - else opt = (char*)GetOption(); + Option_t *opt = option && *option ? option : GetOption(); if (TestBit(kLineNDC)) - PaintArrow(gPad->GetX1() + fX1 * (gPad->GetX2() - gPad->GetX1()), - gPad->GetY1() + fY1 * (gPad->GetY2() - gPad->GetY1()), - gPad->GetX1() + fX2 * (gPad->GetX2() - gPad->GetX1()), - gPad->GetY1() + fY2 * (gPad->GetY2() - gPad->GetY1()), fArrowSize, opt); + PaintArrowNDC(fX1, fY1, fX2, fY2, fArrowSize, opt); else PaintArrow(gPad->XtoPad(fX1), gPad->YtoPad(fY1), gPad->XtoPad(fX2), gPad->YtoPad(fY2), fArrowSize, opt); } @@ -173,11 +166,12 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Float_t arrowsize, Option_t *option) { if (!gPad) return; + auto &parent = *gPad; // Compute the gPad coordinates in TRUE normalized space (NDC) - Int_t iw = gPad->GetWw(); - Int_t ih = gPad->GetWh(); + Int_t iw = parent.GetWw(); + Int_t ih = parent.GetWh(); Double_t x1p,y1p,x2p,y2p; - gPad->GetPadPar(x1p,y1p,x2p,y2p); + parent.GetPadPar(x1p,y1p,x2p,y2p); Int_t ix1 = (Int_t)(iw*x1p); Int_t iy1 = (Int_t)(ih*y1p); Int_t ix2 = (Int_t)(iw*x2p); @@ -187,8 +181,8 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, // Option and attributes TString opt = option; opt.ToLower(); - TAttLine::Modify(); - TAttFill::Modify(); + TAttLine::ModifyOn(parent); + TAttFill::ModifyOn(parent); Double_t wndc = TMath::Min(1.,(Double_t)iw/(Double_t)ih); @@ -202,7 +196,7 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, // Ratios to convert user space in TRUE normalized space (NDC) Double_t rx1,ry1,rx2,ry2; - gPad->GetRange(rx1,ry1,rx2,ry2); + parent.GetRange(rx1,ry1,rx2,ry2); Double_t rx = (x2ndc-x1ndc)/(rx2-rx1); Double_t ry = (y2ndc-y1ndc)/(ry2-ry1); @@ -274,7 +268,7 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, yarr[i] = (1/ry)*(yarr[i]-y1ndc)+ry1; } // paint arrow main line with start/stop segment together - gPad->PaintSegments(cnt/2, xarr, yarr); + parent.PaintSegments(cnt/2, xarr, yarr); // Draw the arrow's head(s) if (opt.Contains(">")) { @@ -295,15 +289,15 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, y2ar[i] = (1/ry)*(y2ar[i]-y1ndc)+ry1; } if (opt.Contains("|>")) { - gPad->GetPainter()->SetLineStyle(1); + parent.GetPainter()->SetLineStyle(1); if (GetFillColor()) { - gPad->PaintFillArea(3,x2ar,y2ar); - gPad->PaintPolyLine(4,x2ar,y2ar); + parent.PaintFillArea(3,x2ar,y2ar); + parent.PaintPolyLine(4,x2ar,y2ar); } else { - gPad->PaintPolyLine(4,x2ar,y2ar); + parent.PaintPolyLine(4,x2ar,y2ar); } } else { - gPad->PaintPolyLine(3,x2ar,y2ar); + parent.PaintPolyLine(3,x2ar,y2ar); } } @@ -324,15 +318,15 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, y1ar[i] = (1/ry)*(y1ar[i]-y1ndc)+ry1; } if (opt.Contains("<|")) { - gPad->GetPainter()->SetLineStyle(1); + parent.GetPainter()->SetLineStyle(1); if (GetFillColor()) { - gPad->PaintFillArea(3,x1ar,y1ar); - gPad->PaintPolyLine(4,x1ar,y1ar); + parent.PaintFillArea(3,x1ar,y1ar); + parent.PaintPolyLine(4,x1ar,y1ar); } else { - gPad->PaintPolyLine(4,x1ar,y1ar); + parent.PaintPolyLine(4,x1ar,y1ar); } } else { - gPad->PaintPolyLine(3,x1ar,y1ar); + parent.PaintPolyLine(3,x1ar,y1ar); } } } From 23b6b6c22861d1e91955eb1ce4e002da6dd4364c Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 10 Apr 2026 16:23:51 +0200 Subject: [PATCH 02/10] [padpainter] preserve double buffer mode When disabled, drawing performed on complete canvas. So one should use absolute coordinates for painting. While such situation appears in interactive painting, support for now only line and box drawing --- graf2d/gpad/inc/TPadPainter.h | 2 ++ graf2d/gpad/src/TPadPainter.cxx | 38 ++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/graf2d/gpad/inc/TPadPainter.h b/graf2d/gpad/inc/TPadPainter.h index 89ac417f9a57c..590d32068da25 100644 --- a/graf2d/gpad/inc/TPadPainter.h +++ b/graf2d/gpad/inc/TPadPainter.h @@ -24,6 +24,7 @@ class TVirtualPad; class TPadPainter : public TPadPainterBase { WinContext_t fWinContext; + Int_t fDoubleBuffer; public: TPadPainter(); @@ -46,6 +47,7 @@ class TPadPainter : public TPadPainterBase { void SelectDrawable(Int_t device) override; void UpdateDrawable(Int_t mode) override; void SetDrawMode(Int_t device, Int_t mode) override; + void SetDoubleBuffer(Int_t device, Int_t mode) override; //TASImage support (noop for a non-gl pad). diff --git a/graf2d/gpad/src/TPadPainter.cxx b/graf2d/gpad/src/TPadPainter.cxx index cfb96aa2d5785..491c00c4bd315 100644 --- a/graf2d/gpad/src/TPadPainter.cxx +++ b/graf2d/gpad/src/TPadPainter.cxx @@ -70,6 +70,8 @@ Implement TVirtualPadPainter which abstracts painting operations. TPadPainter::TPadPainter() { + fDoubleBuffer = 1; + fWinContext = (WinContext_t) 0; } /* @@ -184,6 +186,18 @@ void TPadPainter::SetDrawMode(Int_t device, Int_t mode) gVirtualX->SetDrawModeW(gVirtualX->GetWindowContext(device), (TVirtualX::EDrawMode) mode); } +//////////////////////////////////////////////////////////////////////////////// +/// Set double buffer mode for specified device + +void TPadPainter::SetDoubleBuffer(Int_t device, Int_t mode) +{ + // important flag - when disabled canvas pixmap used directly + // so one need to use absolute coordinates + fDoubleBuffer = mode; + + gVirtualX->SetDoubleBuffer(device, mode); +} + //////////////////////////////////////////////////////////////////////////////// ///Noop, for non-gl pad TASImage calls gVirtualX->CopyArea. @@ -240,10 +254,10 @@ void TPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2) if (fAttLine.GetLineWidth() <= 0) return; - const Int_t px1 = gPad->XtoPixel(x1); - const Int_t px2 = gPad->XtoPixel(x2); - const Int_t py1 = gPad->YtoPixel(y1); - const Int_t py2 = gPad->YtoPixel(y2); + const Int_t px1 = fDoubleBuffer ? gPad->XtoPixel(x1) : gPad->XtoAbsPixel(x1); + const Int_t px2 = fDoubleBuffer ? gPad->XtoPixel(x2) : gPad->XtoAbsPixel(x2); + const Int_t py1 = fDoubleBuffer ? gPad->YtoPixel(y1) : gPad->YtoAbsPixel(y1); + const Int_t py2 = fDoubleBuffer ? gPad->YtoPixel(y2) : gPad->YtoAbsPixel(y2); gVirtualX->DrawLineW(fWinContext, px1, py1, px2, py2); } @@ -256,10 +270,10 @@ void TPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t v2 if (fAttLine.GetLineWidth() <= 0) return; - const Int_t px1 = gPad->UtoPixel(u1); - const Int_t py1 = gPad->VtoPixel(v1); - const Int_t px2 = gPad->UtoPixel(u2); - const Int_t py2 = gPad->VtoPixel(v2); + const Int_t px1 = fDoubleBuffer ? gPad->UtoPixel(u1) : gPad->UtoAbsPixel(u1); + const Int_t py1 = fDoubleBuffer ? gPad->VtoPixel(v1) : gPad->VtoAbsPixel(v1); + const Int_t px2 = fDoubleBuffer ? gPad->UtoPixel(u2) : gPad->UtoAbsPixel(u2); + const Int_t py2 = fDoubleBuffer ? gPad->VtoPixel(v2) : gPad->VtoAbsPixel(v2); gVirtualX->DrawLineW(fWinContext, px1, py1, px2, py2); } @@ -272,10 +286,10 @@ void TPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, EB if (fAttLine.GetLineWidth() <= 0 && mode == TVirtualPadPainter::kHollow) return; - Int_t px1 = gPad->XtoPixel(x1); - Int_t px2 = gPad->XtoPixel(x2); - Int_t py1 = gPad->YtoPixel(y1); - Int_t py2 = gPad->YtoPixel(y2); + Int_t px1 = fDoubleBuffer ? gPad->XtoPixel(x1) : gPad->XtoAbsPixel(x1); + Int_t px2 = fDoubleBuffer ? gPad->XtoPixel(x2) : gPad->XtoAbsPixel(x2); + Int_t py1 = fDoubleBuffer ? gPad->YtoPixel(y1) : gPad->YtoAbsPixel(y1); + Int_t py2 = fDoubleBuffer ? gPad->YtoPixel(y2) : gPad->YtoAbsPixel(y2); // Box width must be at least one pixel (WTF is this code???) if (TMath::Abs(px2 - px1) < 1) From 43c7ac99c2bf880488d1a991b1343c2f2039b486 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 10 Apr 2026 16:35:40 +0200 Subject: [PATCH 03/10] [glpainter] support box drawing in locked state Let work resize/move of TBox in not-opaque mode --- graf3d/gl/src/TGLPadPainter.cxx | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 2236829a9eef8..3c55cef6354e1 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -397,12 +397,10 @@ void TGLPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t if (fLocked) { // this code used when crosshair cursor is drawn if (fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert)) { - const Int_t px1 = gPad->UtoPixel(u1); - const Int_t py1 = gPad->VtoPixel(v1); - const Int_t px2 = gPad->UtoPixel(u2); - const Int_t py2 = gPad->VtoPixel(v2); // TODO: only here set line attributes to virtual x - gVirtualX->DrawLineW(fWinContext, px1, py1, px2, py2); + gVirtualX->DrawLineW(fWinContext, + gPad->UtoAbsPixel(u1), gPad->VtoAbsPixel(v1), + gPad->UtoAbsPixel(u2), gPad->VtoAbsPixel(v2)); } return; } @@ -422,7 +420,21 @@ void TGLPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t void TGLPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, EBoxMode mode) { - if (fLocked) return; + if (fLocked) { + //GL pad painter can be called in non-standard situation: + //not from TPad::Paint, but + //from TView3D::ExecuteRotateView. This means in fact, + //that TView3D wants to draw itself in a XOR mode, via + //gVirtualX. + // TODO: only here set line attributes to virtual x + if (fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert)) { + gVirtualX->DrawBoxW(fWinContext, + gPad->XtoAbsPixel(x1), gPad->YtoAbsPixel(y1), + gPad->XtoAbsPixel(x2), gPad->YtoAbsPixel(y2), + (TVirtualX::EBoxMode) mode); + } + return; + } if (IsGradientFill(GetAttFill().GetFillColor())) { Double_t xs[] = {x1, x2, x2, x1}; From 3d8648d75f88bf2810a79a1b7473868cc0172235 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 10 Apr 2026 16:25:39 +0200 Subject: [PATCH 04/10] [tbox] fix moving in non-opaque mode While such mode is not default, it was not working since a long time. Now repair logic when and how coordiantes are set back --- graf2d/graf/src/TBox.cxx | 42 ++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/graf2d/graf/src/TBox.cxx b/graf2d/graf/src/TBox.cxx index efbdc510afd67..382e2f05e2e20 100644 --- a/graf2d/graf/src/TBox.cxx +++ b/graf2d/graf/src/TBox.cxx @@ -256,21 +256,21 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) auto y1 = parent->AbsPixeltoY(_y1); auto x2 = parent->AbsPixeltoX(_x2); auto y2 = parent->AbsPixeltoY(_y2); - if (isBox) { - if (parent->GetLogx()) { - x1 = TMath::Power(10, x1); - x2 = TMath::Power(10, x2); - } - if (parent->GetLogy()) { - y1 = TMath::Power(10, y1); - y2 = TMath::Power(10, y2); - } - } if (paint) { auto pp = parent->GetPainter(); pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); } else { + if (isBox) { + if (parent->GetLogx()) { + x1 = TMath::Power(10, x1); + x2 = TMath::Power(10, x2); + } + if (parent->GetLogy()) { + y1 = TMath::Power(10, y1); + y2 = TMath::Power(10, y2); + } + } SetX1(x1); SetY1(y1); SetX2(x2); @@ -513,10 +513,11 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) break; case kButton1Up: + if (opaque || ropaque) + parent->ShowGuidelines(this, event); + if (gROOT->IsEscaped()) { gROOT->SetEscape(kFALSE); - if (opaque || ropaque) - parent->ShowGuidelines(this, event); if (opaque) { SetX1(oldX1); SetY1(oldY1); @@ -528,11 +529,9 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) break; } - if (opaque || ropaque) { - parent->ShowGuidelines(this, event); - } else { - if (px1 < 0) - break; + if ((!opaque && pINSIDE) || (!ropaque && (pTop || pBot || pL || pR))) + action(kFALSE, px1, py1, px2, py2); + else if (!ropaque) { if (pA) action(kFALSE, pxold, pyt, pxt, pyold); if (pB) @@ -541,16 +540,9 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) action(kFALSE, pxl, pyold, pxold, pyl); if (pD) action(kFALSE, pxold, pyold, pxt, pyl); - if (pTop || pBot || pL || pR || pINSIDE) - action(kFALSE, px1, py1, px2, py2); - if (pINSIDE) { - // if it was not a pad that was moved then it must have been - // a box or something like that so we have to redraw the pad - if (parent == gPad) parent->Modified(kTRUE); - } } - if (pA || pB || pC || pD || pTop || pL || pR || pBot) + if (pA || pB || pC || pD || pTop || pL || pR || pBot || pINSIDE) parent->Modified(kTRUE); break; From 42cef972c396407949e78d866dd7cfa876edfbac Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 10 Apr 2026 17:13:20 +0200 Subject: [PATCH 05/10] [tline] fix moving in non-opaque mode Similar as TBox --- graf2d/graf/src/TLine.cxx | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/graf2d/graf/src/TLine.cxx b/graf2d/graf/src/TLine.cxx index fe21673a650da..8c1e791b25ede 100644 --- a/graf2d/graf/src/TLine.cxx +++ b/graf2d/graf/src/TLine.cxx @@ -145,7 +145,9 @@ void TLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) auto action = [this, parent](Int_t code, Int_t _x1, Int_t _y1, Int_t _x2 = 0, Int_t _y2 = 0) { Double_t x1, y1, x2, y2; - if (TestBit(kLineNDC)) { + Bool_t isndc = TestBit(kLineNDC); + + if (isndc) { x1 = (1. * _x1 / parent->GetWw() - parent->GetAbsXlowNDC()) / parent->GetAbsWNDC(); y1 = ((1 - 1. * _y1 / parent->GetWh()) - parent->GetAbsYlowNDC()) / parent->GetAbsHNDC(); x2 = (1. * _x2 / parent->GetWw() - parent->GetAbsXlowNDC()) / parent->GetAbsWNDC(); @@ -155,23 +157,24 @@ void TLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) y1 = parent->AbsPixeltoY(_y1); x2 = parent->AbsPixeltoX(_x2); y2 = parent->AbsPixeltoY(_y2); - if (parent->GetLogx()) { - x1 = TMath::Power(10, x1); - x2 = TMath::Power(10, x2); - } - if (parent->GetLogy()) { - y1 = TMath::Power(10, y1); - y2 = TMath::Power(10, y2); - } } if (code == 0) { auto pp = parent->GetPainter(); pp->SetAttLine(*this); - if (TestBit(kLineNDC)) + if (isndc) pp->DrawLineNDC(x1, y1, x2, y2); else pp->DrawLine(x1, y1, x2, y2); } else { + if (!isndc && parent->GetLogx()) { + x1 = TMath::Power(10, x1); + x2 = TMath::Power(10, x2); + } + if (!isndc && parent->GetLogy()) { + y1 = TMath::Power(10, y1); + y2 = TMath::Power(10, y2); + } + if (code & 1) { SetX1(x1); SetY1(y1); From 2e1fd7255ebbcb0dc544f9ded6f1530c1451dd56 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 10 Apr 2026 17:41:24 +0200 Subject: [PATCH 06/10] [tpad] fix moving in non-opaque mode Like for TBox or TLine mode was not tested for long time and therefore not working --- graf2d/gpad/src/TPad.cxx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index c1272454b597b..1a8d1bc002a4c 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -2025,14 +2025,14 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (newcode) return; - auto pp = GetPainter(); - - auto action = [this,pp,parent](Bool_t paint, Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { + auto action = [this,parent](Bool_t paint, Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { auto x1 = AbsPixeltoX(_x1); auto y1 = AbsPixeltoY(_y1); auto x2 = AbsPixeltoX(_x2); auto y2 = AbsPixeltoY(_y2); if (paint) { + auto pp = GetPainter(); + pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); } else { // Get parent corners pixels coordinates @@ -2067,7 +2067,6 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) fXUpNDC = fXlowNDC + fWNDC; fYUpNDC = fYlowNDC + fHNDC; - pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); // No break !!! @@ -2353,7 +2352,7 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) pxold = px; pyold = py; - if ((!fResizing && opaque) || (fResizing && ropaque)) { + if ((pINSIDE && opaque) || (fResizing && ropaque)) { if (px != pxorg || py != pyorg) { if (pA) action(kFALSE, pxold, pyt, pxt, pyold); @@ -2387,14 +2386,16 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kButton1Up: + if (opaque || ropaque) + ShowGuidelines(this, event); + if (gROOT->IsEscaped()) { gROOT->SetEscape(kFALSE); + fResizing = kFALSE; break; } - if (opaque||ropaque) { - ShowGuidelines(this, event); - } else { + if ((pINSIDE && !opaque) || (fResizing && !ropaque)) { if (pA) action(kFALSE, pxold, pyt, pxt, pyold); if (pB) @@ -2409,8 +2410,6 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (pA || pB || pC || pD || pTop || pL || pR || pBot) Modified(kTRUE); - pp->SetAttLine({-1, 1, -1}); - // Reset pad parameters and recompute conversion coefficients ResizePad(); @@ -2418,6 +2417,8 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) RangeChanged(); } + fResizing = kFALSE; + break; case kButton1Locate: From 23723c9530820e95b7220e413b1bae103ee074bd Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 10 Apr 2026 18:00:52 +0200 Subject: [PATCH 07/10] [tbox] simplify ExecuteEvent handling Handle all resize corners, resize sides and moving similar --- graf2d/graf/src/TBox.cxx | 421 +++++++++++++++++---------------------- 1 file changed, 178 insertions(+), 243 deletions(-) diff --git a/graf2d/graf/src/TBox.cxx b/graf2d/graf/src/TBox.cxx index 382e2f05e2e20..06577f2eb63b6 100644 --- a/graf2d/graf/src/TBox.cxx +++ b/graf2d/graf/src/TBox.cxx @@ -231,42 +231,42 @@ TBox *TBox::DrawBox(Double_t x1, Double_t y1,Double_t x2, Double_t y2) void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) { + if (TestBit(kCannotMove)) return; + if (!gPad) return; - if (!gPad->IsEditable() && event != kMouseEnter) return; - if (TestBit(kCannotMove)) return; + auto &parent = *gPad; + + if (!parent.IsEditable() && event != kMouseEnter) return; Bool_t isBox = !(InheritsFrom("TPave") || InheritsFrom("TWbox")); constexpr Int_t kMaxDiff = 7; constexpr Int_t kMinSize = 20; - static Int_t px1, px2, py1, py2, pxl, pyl, pxt, pyt, pxold, pyold; - static Int_t px1p, px2p, py1p, py2p, pxlp, pylp, pxtp, pytp; + static Int_t px1, px2, py1, py2, dpx1, dpy2; + static Int_t px1p, px2p, py1p, py2p; static Double_t oldX1, oldY1, oldX2, oldY2; - static Bool_t pA, pB, pC, pD, pTop, pL, pR, pBot, pINSIDE; - Int_t wx, wy; - auto parent = gPad; - Bool_t opaque = parent->OpaqueMoving(); - Bool_t ropaque = parent->OpaqueResizing(); + static Bool_t hasOld = kFALSE; + static enum { pNone, pA, pB, pC, pD, pTop, pL, pR, pBot, pINSIDE } mode = pNone; + static bool firstPaint = kFALSE; + Bool_t opaque = parent.OpaqueMoving(); + Bool_t ropaque = parent.OpaqueResizing(); // convert to user coordinates and either paint ot set back - auto action = [parent,isBox,this](Bool_t paint, Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { - auto x1 = parent->AbsPixeltoX(_x1); - auto y1 = parent->AbsPixeltoY(_y1); - auto x2 = parent->AbsPixeltoX(_x2); - auto y2 = parent->AbsPixeltoY(_y2); - if (paint) { - auto pp = parent->GetPainter(); - pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); - pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); - } else { + auto paint_or_set = [&parent,isBox,this](Bool_t paint) + { + auto x1 = parent.AbsPixeltoX(px1); + auto y1 = parent.AbsPixeltoY(py1); + auto x2 = parent.AbsPixeltoX(px2); + auto y2 = parent.AbsPixeltoY(py2); + if (!paint) { if (isBox) { - if (parent->GetLogx()) { + if (parent.GetLogx()) { x1 = TMath::Power(10, x1); x2 = TMath::Power(10, x2); } - if (parent->GetLogy()) { + if (parent.GetLogy()) { y1 = TMath::Power(10, y1); y2 = TMath::Power(10, y2); } @@ -275,6 +275,12 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) SetY1(y1); SetX2(x2); SetY2(y2); + } else if (firstPaint) { + firstPaint = kFALSE; + } else { + auto pp = parent.GetPainter(); + pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); + pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); } }; @@ -283,11 +289,7 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) switch (event) { case kMouseEnter: - if (fTip) parent->ResetToolTip(fTip); - break; - - case kButton1Double: - px1 = -1; //used by kButton1Up + if (fTip) parent.ResetToolTip(fTip); break; case kArrowKeyPress: @@ -297,253 +299,186 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) oldY1 = fY1; oldX2 = fX2; oldY2 = fY2; + hasOld = kTRUE; // No break !!! case kMouseMotion: - px1 = parent->XtoAbsPixel(isBox ? parent->XtoPad(GetX1()) : GetX1()); - py1 = parent->YtoAbsPixel(isBox ? parent->YtoPad(GetY1()) : GetY1()); - px2 = parent->XtoAbsPixel(isBox ? parent->XtoPad(GetX2()) : GetX2()); - py2 = parent->YtoAbsPixel(isBox ? parent->YtoPad(GetY2()) : GetY2()); - - if (px1 < px2) { - pxl = px1; - pxt = px2; - } else { - pxl = px2; - pxt = px1; - } - if (py1 < py2) { - pyl = py1; - pyt = py2; - } else { - pyl = py2; - pyt = py1; - } - - px1p = parent->XtoAbsPixel(parent->GetX1()) + parent->GetBorderSize(); - py1p = parent->YtoAbsPixel(parent->GetY1()) - parent->GetBorderSize(); - px2p = parent->XtoAbsPixel(parent->GetX2()) - parent->GetBorderSize(); - py2p = parent->YtoAbsPixel(parent->GetY2()) + parent->GetBorderSize(); - - if (px1p < px2p) { - pxlp = px1p; - pxtp = px2p; - } else { - pxlp = px2p; - pxtp = px1p; - } - if (py1p < py2p) { - pylp = py1p; - pytp = py2p; - } else { - pylp = py2p; - pytp = py1p; - } - - pA = pB = pC = pD = pTop = pL = pR = pBot = pINSIDE = kFALSE; - - // case pA - if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) { - pxold = pxl; pyold = pyl; pA = kTRUE; - parent->SetCursor(kTopLeft); - } - // case pB - if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) { - pxold = pxt; pyold = pyl; pB = kTRUE; - parent->SetCursor(kTopRight); - } - // case pC - if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) { - pxold = pxt; pyold = pyt; pC = kTRUE; - parent->SetCursor(kBottomRight); - } - // case pD - if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) { - pxold = pxl; pyold = pyt; pD = kTRUE; - parent->SetCursor(kBottomLeft); - } - // top edge - if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) && TMath::Abs(py - pyl) < kMaxDiff) { - pxold = pxl; pyold = pyl; pTop = kTRUE; - parent->SetCursor(kTopSide); - } - // bottom edge - if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) && TMath::Abs(py - pyt) < kMaxDiff) { - pxold = pxt; pyold = pyt; pBot = kTRUE; - parent->SetCursor(kBottomSide); - } - // left edge - if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) && TMath::Abs(px - pxl) < kMaxDiff) { - pxold = pxl; pyold = pyl; pL = kTRUE; - parent->SetCursor(kLeftSide); - } - // right edge - if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) && TMath::Abs(px - pxt) < kMaxDiff) { - pxold = pxt; pyold = pyt; pR = kTRUE; - parent->SetCursor(kRightSide); - } - // inside box - if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) && (py > pyl+kMaxDiff && py < pyt-kMaxDiff)) { - pxold = px; pyold = py; pINSIDE = kTRUE; + px1 = parent.XtoAbsPixel(isBox ? parent.XtoPad(GetX1()) : GetX1()); + py1 = parent.YtoAbsPixel(isBox ? parent.YtoPad(GetY1()) : GetY1()); + px2 = parent.XtoAbsPixel(isBox ? parent.XtoPad(GetX2()) : GetX2()); + py2 = parent.YtoAbsPixel(isBox ? parent.YtoPad(GetY2()) : GetY2()); + if (px1 > px2) + std::swap(px1, px2); + if (py1 < py2) + std::swap(py1, py2); + + px1p = parent.XtoAbsPixel(parent.GetX1()) + parent.GetBorderSize(); + py1p = parent.YtoAbsPixel(parent.GetY1()) - parent.GetBorderSize(); + px2p = parent.XtoAbsPixel(parent.GetX2()) - parent.GetBorderSize(); + py2p = parent.YtoAbsPixel(parent.GetY2()) + parent.GetBorderSize(); + if (px1p > px2p) + std::swap(px1p, px2p); + if (py1p < py2p) + std::swap(py1p, py2p); + + mode = pNone; + + if (TMath::Abs(px - px1) <= kMaxDiff && TMath::Abs(py - py2) <= kMaxDiff) { + mode = pA; + parent.SetCursor(kTopLeft); + } else if (TMath::Abs(px - px2) <= kMaxDiff && TMath::Abs(py - py2) <= kMaxDiff) { + mode = pB; + parent.SetCursor(kTopRight); + } else if (TMath::Abs(px - px2) <= kMaxDiff && TMath::Abs(py - py1) <= kMaxDiff) { + mode = pC; + parent.SetCursor(kBottomRight); + } else if (TMath::Abs(px - px1) <= kMaxDiff && TMath::Abs(py - py1) <= kMaxDiff) { + mode = pD; + parent.SetCursor(kBottomLeft); + } else if ((px > px1 + kMaxDiff && px < px2 - kMaxDiff) && TMath::Abs(py - py2) < kMaxDiff) { + mode = pTop; + parent.SetCursor(kTopSide); + } else if ((px > px1 + kMaxDiff && px < px2 - kMaxDiff) && TMath::Abs(py - py1) < kMaxDiff) { + mode = pBot; + parent.SetCursor(kBottomSide); + } else if ((py > py2 + kMaxDiff && py < py1 - kMaxDiff) && TMath::Abs(px - px1) < kMaxDiff) { + mode = pL; + parent.SetCursor(kLeftSide); + } else if ((py > py2 + kMaxDiff && py < py1 - kMaxDiff) && TMath::Abs(px - px2) < kMaxDiff) { + mode = pR; + parent.SetCursor(kRightSide); + } else if ((px > px1+kMaxDiff && px < px2-kMaxDiff) && (py > py2+kMaxDiff && py < py1-kMaxDiff)) { + dpx1 = px - px1; // cursor position relative to top-left corner + dpy2 = py - py2; + mode = pINSIDE; if (event == kButton1Down) - parent->SetCursor(kMove); + parent.SetCursor(kMove); else - parent->SetCursor(kCross); + parent.SetCursor(kCross); } - fResizing = pA || pB || pC || pD || pTop || pL || pR || pBot; - - if (!fResizing && !pINSIDE) - parent->SetCursor(kCross); + fResizing = (mode != pNone) && (mode != pINSIDE); + firstPaint = kTRUE; + if (mode == pNone) + parent.SetCursor(kCross); break; case kArrowKeyRelease: case kButton1Motion: - wx = wy = 0; - - if (pA) { - if (!ropaque) action(kTRUE, pxold, pyt, pxt, pyold); // draw the old box - if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } - if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } - if (px < pxlp) { px = pxlp; wx = px; } - if (py < pylp) { py = pylp; wy = py; } - if (!ropaque) action(kTRUE, px, pyt, pxt, py); // draw the new box - } - if (pB) { - if (!ropaque) action(kTRUE, pxl, pyt, pxold, pyold); - if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } - if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } - if (px > pxtp) { px = pxtp; wx = px; } - if (py < pylp) { py = pylp; wy = py; } - if (!ropaque) action(kTRUE, pxl, pyt, px, py); - } - if (pC) { - if (!ropaque) action(kTRUE, pxl, pyl, pxold, pyold); - if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } - if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } - if (px > pxtp) { px = pxtp; wx = px; } - if (py > pytp) { py = pytp; wy = py; } - if (!ropaque) action(kTRUE, pxl, pyl, px, py); - } - if (pD) { - if (!ropaque) action(kTRUE, pxold, pyold, pxt, pyl); - if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } - if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } - if (px < pxlp) { px = pxlp; wx = px; } - if (py > pytp) { py = pytp; wy = py; } - if (!ropaque) action(kTRUE, px, py, pxt, pyl); - } - if (pTop) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - py2 += py - pyold; - if (py2 > py1-kMinSize) { py2 = py1-kMinSize; wy = py2; } - if (py2 < py2p) { py2 = py2p; wy = py2; } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pBot) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - py1 += py - pyold; - if (py1 < py2+kMinSize) { py1 = py2+kMinSize; wy = py1; } - if (py1 > py1p) { py1 = py1p; wy = py1; } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pL) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - px1 += px - pxold; - if (px1 > px2-kMinSize) { px1 = px2-kMinSize; wx = px1; } - if (px1 < px1p) { px1 = px1p; wx = px1; } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pR) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - px2 += px - pxold; - if (px2 < px1+kMinSize) { px2 = px1+kMinSize; wx = px2; } - if (px2 > px2p) { px2 = px2p; wx = px2; } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pINSIDE) { - if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the old box - Int_t dx = px - pxold; - Int_t dy = py - pyold; - px1 += dx; py1 += dy; px2 += dx; py2 += dy; - if (px1 < px1p) { dx = px1p - px1; px1 += dx; px2 += dx; wx = px+dx; } - if (px2 > px2p) { dx = px2 - px2p; px1 -= dx; px2 -= dx; wx = px-dx; } - if (py1 > py1p) { dy = py1 - py1p; py1 -= dy; py2 -= dy; wy = py-dy; } - if (py2 < py2p) { dy = py2p - py2; py1 += dy; py2 += dy; wy = py+dy; } - if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the new box - } - - if (wx || wy) { - if (wx) px = wx; - if (wy) py = wy; - parent->GetCanvasImp()->Warp(px, py); + switch (mode) { + case pNone: + return; + case pA: + if (!ropaque) paint_or_set(kTRUE); + px1 = TMath::Max(px1p, TMath::Min(px, px2 - kMinSize)); + py2 = TMath::Max(py2p, TMath::Min(py, py1 - kMinSize)); + paint_or_set(!ropaque); + break; + case pB: + if (!ropaque) paint_or_set(kTRUE); + px2 = TMath::Min(px2p, TMath::Max(px, px1 + kMinSize)); + py2 = TMath::Max(py2p, TMath::Min(py, py1 - kMinSize)); + paint_or_set(!ropaque); + break; + case pC: + if (!ropaque) paint_or_set(kTRUE); + px2 = TMath::Min(px2p, TMath::Max(px, px1 + kMinSize)); + py1 = TMath::Min(py1p, TMath::Max(py, py2 + kMinSize)); + paint_or_set(!ropaque); + break; + case pD: + if (!ropaque) paint_or_set(kTRUE); + px1 = TMath::Max(px1p, TMath::Min(px, px2 - kMinSize)); + py1 = TMath::Min(py1p, TMath::Max(py, py2 + kMinSize)); + paint_or_set(!ropaque); + break; + case pTop: + if (!ropaque) paint_or_set(kTRUE); + py2 = TMath::Max(py2p, TMath::Min(py, py1 - kMinSize)); + paint_or_set(!ropaque); + break; + case pBot: + if (!ropaque) paint_or_set(kTRUE); + py1 = TMath::Min(py1p, TMath::Max(py, py2 + kMinSize)); + paint_or_set(!ropaque); + break; + case pL: + if (!ropaque) paint_or_set(kTRUE); + px1 = TMath::Max(px1p, TMath::Min(px, px2 - kMinSize)); + paint_or_set(!ropaque); + break; + case pR: + if (!ropaque) paint_or_set(kTRUE); + px2 = TMath::Min(px2p, TMath::Max(px, px1 + kMinSize)); + paint_or_set(!ropaque); + break; + case pINSIDE: + if (!opaque) paint_or_set(kTRUE); + px2 += px - dpx1 - px1; + px1 = px - dpx1; + py1 += py - dpy2 - py2; + py2 = py - dpy2; + if (px1 < px1p) { px2 += px1p - px1; px1 = px1p; } + if (px2 > px2p) { px1 -= px2 - px2p; px2 = px2p; } + if (py1 > py1p) { py2 -= py1 - py1p; py1 = py1p; } + if (py2 < py2p) { py1 += py2p - py2; py2 = py2p; } + paint_or_set(!opaque); + break; } - pxold = px; - pyold = py; - - if ((pINSIDE && opaque) || (fResizing && ropaque)) { - if (pA) - action(kFALSE, pxold, pyt, pxt, pyold); - if (pB) - action(kFALSE, pxl, pyt, pxold, pyold); - if (pC) - action(kFALSE, pxl, pyold, pxold, pyl); - if (pD) - action(kFALSE, pxold, pyold, pxt, pyl); - if (pTop || pBot || pL || pR || pINSIDE) - action(kFALSE, px1, py1, px2, py2); - - if (pINSIDE) parent->ShowGuidelines(this, event, 'i', true); - if (pTop) parent->ShowGuidelines(this, event, 't', true); - if (pBot) parent->ShowGuidelines(this, event, 'b', true); - if (pL) parent->ShowGuidelines(this, event, 'l', true); - if (pR) parent->ShowGuidelines(this, event, 'r', true); - if (pA) parent->ShowGuidelines(this, event, '1', true); - if (pB) parent->ShowGuidelines(this, event, '2', true); - if (pC) parent->ShowGuidelines(this, event, '3', true); - if (pD) parent->ShowGuidelines(this, event, '4', true); - parent->Modified(kTRUE); + if ((mode == pINSIDE && opaque) || (fResizing && ropaque)) { + switch(mode) { + case pINSIDE: parent.ShowGuidelines(this, event, 'i', true); break; + case pTop: parent.ShowGuidelines(this, event, 't', true); break; + case pBot: parent.ShowGuidelines(this, event, 'b', true); break; + case pL: parent.ShowGuidelines(this, event, 'l', true); break; + case pR: parent.ShowGuidelines(this, event, 'r', true); break; + case pA: parent.ShowGuidelines(this, event, '1', true); break; + case pB: parent.ShowGuidelines(this, event, '2', true); break; + case pC: parent.ShowGuidelines(this, event, '3', true); break; + case pD: parent.ShowGuidelines(this, event, '4', true); break; + default: break; // not involved + } + parent.Modified(kTRUE); } break; case kButton1Up: if (opaque || ropaque) - parent->ShowGuidelines(this, event); + parent.ShowGuidelines(this, event); if (gROOT->IsEscaped()) { gROOT->SetEscape(kFALSE); - if (opaque) { - SetX1(oldX1); - SetY1(oldY1); - SetX2(oldX2); - SetY2(oldY2); - parent->Modified(kTRUE); - parent->Update(); + if (opaque && (mode != pNone)) { + if (hasOld) { + SetX1(oldX1); + SetY1(oldY1); + SetX2(oldX2); + SetY2(oldY2); + } + hasOld = kFALSE; + mode = pNone; + fResizing = kFALSE; + parent.Modified(kTRUE); + parent.Update(); } break; } - if ((!opaque && pINSIDE) || (!ropaque && (pTop || pBot || pL || pR))) - action(kFALSE, px1, py1, px2, py2); - else if (!ropaque) { - if (pA) - action(kFALSE, pxold, pyt, pxt, pyold); - if (pB) - action(kFALSE, pxl, pyt, pxold, pyold); - if (pC) - action(kFALSE, pxl, pyold, pxold, pyl); - if (pD) - action(kFALSE, pxold, pyold, pxt, pyl); - } + if ((!opaque && mode == pINSIDE) || (!ropaque && fResizing)) + paint_or_set(kFALSE); + + if (mode != pNone) + parent.Modified(kTRUE); - if (pA || pB || pC || pD || pTop || pL || pR || pBot || pINSIDE) - parent->Modified(kTRUE); + mode = pNone; + fResizing = kFALSE; + hasOld = kFALSE; break; @@ -554,7 +489,7 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) while (true) { px = py = 0; - event = parent->GetCanvasImp()->RequestLocator(px, py); + event = parent.GetCanvasImp()->RequestLocator(px, py); ExecuteEvent(kButton1Motion, px, py); From d2440c389e0161a297ee5454525f6a5d039e7bcb Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 13 Apr 2026 13:18:12 +0200 Subject: [PATCH 08/10] [tpad] simplify ExecuteEvent handling Like in TBox, use enum and current coordinates Avoid first painting over original coordinates while this make invert drawing wrong --- graf2d/gpad/src/TPad.cxx | 508 +++++++++++++++------------------------ 1 file changed, 198 insertions(+), 310 deletions(-) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index 1a8d1bc002a4c..3a858128f869f 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -1950,35 +1950,27 @@ void TPad::DrawColorTable() void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) { - const Int_t kMaxDiff = 5; - const Int_t kMinSize = 20; - static Int_t pxorg, pyorg; - static Int_t px1, px2, py1, py2, pxl, pyl, pxt, pyt, pxold, pyold; - static Int_t px1p, px2p, py1p, py2p, pxlp, pylp, pxtp, pytp; - static Bool_t pA, pB, pC, pD, pTop, pL, pR, pBot, pINSIDE; - Int_t wx, wy; + constexpr Int_t kMaxDiff = 5; + constexpr Int_t kMinSize = 20; + static Int_t px1, px2, py1, py2, dpx1, dpy2; + static Int_t px1p, px2p, py1p, py2p; + static enum { pNone, pA, pB, pC, pD, pTop, pL, pR, pBot, pINSIDE } mode = pNone; + static Bool_t firstPaint = kFALSE; Bool_t opaque = OpaqueMoving(); Bool_t ropaque = OpaqueResizing(); - Bool_t fixedr = HasFixedAspectRatio(); if (!IsEditable() && event != kMouseEnter) return; - TVirtualPad *parent = GetMother(); - if (!parent->IsEditable()) return; + TVirtualPad &parent = *GetMother(); + if (!parent.IsEditable()) return; HideToolTip(event); if (fXlowNDC < 0 && event != kButton1Down) return; if (fYlowNDC < 0 && event != kButton1Down) return; - // keep old mouse position - if (event == kButton1Down) { - pxorg = px; - pyorg = py; - } - Int_t newcode = gROOT->GetEditorMode(); if (newcode) - pA = pB = pC = pD = pTop = pL = pR = pBot = pINSIDE = kFALSE; + mode = pNone; switch (newcode) { case kPad: TCreatePrimitives::Pad(event,px,py,0); @@ -2025,21 +2017,18 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (newcode) return; - auto action = [this,parent](Bool_t paint, Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { - auto x1 = AbsPixeltoX(_x1); - auto y1 = AbsPixeltoY(_y1); - auto x2 = AbsPixeltoX(_x2); - auto y2 = AbsPixeltoY(_y2); - if (paint) { - auto pp = GetPainter(); - pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); - pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); - } else { + auto paint_or_set = [this, &parent](Bool_t paint) + { + auto x1 = AbsPixeltoX(px1); + auto y1 = AbsPixeltoY(py1); + auto x2 = AbsPixeltoX(px2); + auto y2 = AbsPixeltoY(py2); + if (!paint) { // Get parent corners pixels coordinates - Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1()); - Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2()); - Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1()); - Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2()); + Int_t parentpx1 = fMother->XtoAbsPixel(parent.GetX1()); + Int_t parentpx2 = fMother->XtoAbsPixel(parent.GetX2()); + Int_t parentpy1 = fMother->YtoAbsPixel(parent.GetY1()); + Int_t parentpy2 = fMother->YtoAbsPixel(parent.GetY2()); // Get pad new corners pixels coordinates Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; } @@ -2052,7 +2041,48 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1); fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1); fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1); + } else if (firstPaint) { + // first paint with original coordinates not required + firstPaint = kFALSE; + } else { + auto pp = GetPainter(); + pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); + pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); + } + }; + + Int_t prevpx1 = px1, prevpx2 = px2, prevpy1 = py1, prevpy2 = py2; + + // function check how to restore pad ratio + auto adjustRatio = [this, &parent](int choise = 11) -> bool + { + if (!HasFixedAspectRatio()) + return true; // do nothing + + if (choise == 11) { + Int_t dx = parent.UtoPixel(fAspectRatio * (py1 - py2) / parent.VtoPixel(0)); + Int_t npx1 = (px1 + px2) / 2 - dx / 2; + Int_t npx2 = npx1 + dx; + if ((npx1 >= px1p) && (npx2 <= px2p)) { + px1 = npx1; px2 = npx2; + return true; + } + } else { + Int_t dy = parent.VtoPixel(1. - (0. + px2 - px1) / parent.UtoPixel(1.) / fAspectRatio); + Int_t npy1 = py1; + Int_t npy2 = py2; + switch (choise) { + case -1: npy2 = py1 - dy; break; + case 0: npy2 = (py1 + py2) / 2 - dy / 2; npy1 = npy2 + dy; break; + case 1: npy1 = py2 + dy; break; + } + if ((npy1 <= py1p) && (npy2 >= py2p)) { + py1 = npy1; py2 = npy2; + return true; + } } + + return false; // fail to adjust ratio, need to restore values }; switch (event) { @@ -2077,100 +2107,63 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) px2 = XtoAbsPixel(fX2); py2 = YtoAbsPixel(fY2); - if (px1 < px2) { - pxl = px1; - pxt = px2; - } else { - pxl = px2; - pxt = px1; - } - if (py1 < py2) { - pyl = py1; - pyt = py2; - } else { - pyl = py2; - pyt = py1; - } + if (px1 > px2) + std::swap(px1, px2); - px1p = parent->XtoAbsPixel(parent->GetX1()) + parent->GetBorderSize(); - py1p = parent->YtoAbsPixel(parent->GetY1()) - parent->GetBorderSize(); - px2p = parent->XtoAbsPixel(parent->GetX2()) - parent->GetBorderSize(); - py2p = parent->YtoAbsPixel(parent->GetY2()) + parent->GetBorderSize(); + if (py1 < py2) + std::swap(py1, py2); - if (px1p < px2p) { - pxlp = px1p; - pxtp = px2p; - } else { - pxlp = px2p; - pxtp = px1p; - } - if (py1p < py2p) { - pylp = py1p; - pytp = py2p; - } else { - pylp = py2p; - pytp = py1p; - } + px1p = parent.XtoAbsPixel(parent.GetX1()) + parent.GetBorderSize(); + py1p = parent.YtoAbsPixel(parent.GetY1()) - parent.GetBorderSize(); + px2p = parent.XtoAbsPixel(parent.GetX2()) - parent.GetBorderSize(); + py2p = parent.YtoAbsPixel(parent.GetY2()) + parent.GetBorderSize(); + + if (px1p > px2p) + std::swap(px1p, px2p); - pA = pB = pC = pD = pTop = pL = pR = pBot = pINSIDE = kFALSE; + if (py1p < py2p) + std::swap(py1p, py2p); - // case pA - if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) { - pxold = pxl; pyold = pyl; pA = kTRUE; + mode = pNone; + if (TMath::Abs(px - px1) <= kMaxDiff && TMath::Abs(py - py2) <= kMaxDiff) { + mode = pA; SetCursor(kTopLeft); - } - // case pB - if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) { - pxold = pxt; pyold = pyl; pB = kTRUE; + } else if (TMath::Abs(px - px2) <= kMaxDiff && TMath::Abs(py - py2) <= kMaxDiff) { + mode = pB; SetCursor(kTopRight); - } - // case pC - if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) { - pxold = pxt; pyold = pyt; pC = kTRUE; + } else if (TMath::Abs(px - px2) <= kMaxDiff && TMath::Abs(py - py1) <= kMaxDiff) { + mode = pC; SetCursor(kBottomRight); - } - // case pD - if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) { - pxold = pxl; pyold = pyt; pD = kTRUE; + } else if (TMath::Abs(px - px1) <= kMaxDiff && TMath::Abs(py - py1) <= kMaxDiff) { + mode = pD; SetCursor(kBottomLeft); - } - - if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) && - TMath::Abs(py - pyl) < kMaxDiff) { // top edge - pxold = pxl; pyold = pyl; pTop = kTRUE; + } else if ((px > px1 + kMaxDiff && px < px2 - kMaxDiff) && TMath::Abs(py - py2) < kMaxDiff) { + mode = pTop; SetCursor(kTopSide); - } - - if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) && - TMath::Abs(py - pyt) < kMaxDiff) { // bottom edge - pxold = pxt; pyold = pyt; pBot = kTRUE; + } else if ((px > px1 + kMaxDiff && px < px2 - kMaxDiff) && TMath::Abs(py - py1) < kMaxDiff) { + mode = pBot; SetCursor(kBottomSide); - } - - if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) && - TMath::Abs(px - pxl) < kMaxDiff) { // left edge - pxold = pxl; pyold = pyl; pL = kTRUE; + } else if ((py > py2 + kMaxDiff && py < py1 - kMaxDiff) && TMath::Abs(px - px1) < kMaxDiff) { + mode = pL; SetCursor(kLeftSide); - } - - if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) && - TMath::Abs(px - pxt) < kMaxDiff) { // right edge - pxold = pxt; pyold = pyt; pR = kTRUE; + } else if ((py > py2 + kMaxDiff && py < py1 - kMaxDiff) && TMath::Abs(px - px2) < kMaxDiff) { + mode = pR; SetCursor(kRightSide); - } - - if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) && - (py > pyl+kMaxDiff && py < pyt-kMaxDiff)) { // inside box - pxold = px; pyold = py; pINSIDE = kTRUE; + } else if ((px > px1+kMaxDiff && px < px2-kMaxDiff) && (py > py2+kMaxDiff && py < py1-kMaxDiff)) { + dpx1 = px - px1; // cursor position relative to top-left corner + dpy2 = py - py2; + mode = pINSIDE; if (event == kButton1Down) SetCursor(kMove); else SetCursor(kCross); } - fResizing = pA || pB || pC || pD || pTop || pL || pR || pBot; + fResizing = (mode != pNone) && (mode != pINSIDE); - if (!fResizing && !pINSIDE) + firstPaint = kTRUE; + + if (mode == pNone) SetCursor(kCross); break; @@ -2179,205 +2172,107 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) case kButton1Motion: if (TestBit(kCannotMove)) break; - wx = wy = 0; - - if (pA) { - if (!ropaque) action(kTRUE, pxold, pyt, pxt, pyold); - if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } - if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } - if (px < pxlp) { px = pxlp; wx = px; } - if (py < pylp) { py = pylp; wy = py; } - if (fixedr) { - Double_t dy = Double_t(TMath::Abs(pxt-px))/parent->UtoPixel(1.) / - fAspectRatio; - Int_t npy2 = pyt - TMath::Abs(parent->VtoAbsPixel(dy) - - parent->VtoAbsPixel(0)); - if (npy2 < pylp) { - px = pxold; - py = pyold; - } else - py = npy2; - - wx = wy = 0; - } - if (!ropaque) action(kTRUE, px, pyt, pxt, py); - } - if (pB) { - if (!ropaque) action(kTRUE, pxl, pyt, pxold, pyold); - if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } - if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } - if (px > pxtp) { px = pxtp; wx = px; } - if (py < pylp) { py = pylp; wy = py; } - if (fixedr) { - Double_t dy = Double_t(TMath::Abs(pxl-px))/parent->UtoPixel(1.) / - fAspectRatio; - Int_t npy2 = pyt - TMath::Abs(parent->VtoAbsPixel(dy) - - parent->VtoAbsPixel(0)); - if (npy2 < pylp) { - px = pxold; - py = pyold; - } else - py = npy2; - - wx = wy = 0; - } - if (!ropaque) action(kTRUE, pxl, pyt, px, py); - } - if (pC) { - if (!ropaque) action(kTRUE, pxl, pyl, pxold, pyold); - if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } - if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } - if (px > pxtp) { px = pxtp; wx = px; } - if (py > pytp) { py = pytp; wy = py; } - if (fixedr) { - Double_t dy = Double_t(TMath::Abs(pxl-px))/parent->UtoPixel(1.) / - fAspectRatio; - Int_t npy2 = pyl + TMath::Abs(parent->VtoAbsPixel(dy) - - parent->VtoAbsPixel(0)); - if (npy2 > pytp) { - px = pxold; - py = pyold; - } else - py = npy2; - - wx = wy = 0; - } - if (!ropaque) action(kTRUE, pxl, pyl, px, py); - } - if (pD) { - if (!ropaque) action(kTRUE, pxold, pyold, pxt, pyl); - if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } - if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } - if (px < pxlp) { px = pxlp; wx = px; } - if (py > pytp) { py = pytp; wy = py; } - if (fixedr) { - Double_t dy = Double_t(TMath::Abs(pxt-px))/parent->UtoPixel(1.) / - fAspectRatio; - Int_t npy2 = pyl + TMath::Abs(parent->VtoAbsPixel(dy) - - parent->VtoAbsPixel(0)); - if (npy2 > pytp) { - px = pxold; - py = pyold; - } else - py = npy2; - - wx = wy = 0; - } - if (!ropaque) action(kTRUE, px, py, pxt, pyl); - } - if (pTop) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - py2 += py - pyold; - if (py2 > py1-kMinSize) { py2 = py1-kMinSize; wy = py2; } - if (py2 < py2p) { py2 = py2p; wy = py2; } - if (fixedr) { - Double_t dx = Double_t(TMath::Abs(py2-py1))/parent->VtoPixel(0) * - fAspectRatio; - Int_t npx2 = px1 + parent->UtoPixel(dx); - if (npx2 > px2p) - py2 -= py - pyold; - else - px2 = npx2; - } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pBot) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - py1 += py - pyold; - if (py1 < py2+kMinSize) { py1 = py2+kMinSize; wy = py1; } - if (py1 > py1p) { py1 = py1p; wy = py1; } - if (fixedr) { - Double_t dx = Double_t(TMath::Abs(py2-py1))/parent->VtoPixel(0) * - fAspectRatio; - Int_t npx2 = px1 + parent->UtoPixel(dx); - if (npx2 > px2p) - py1 -= py - pyold; - else - px2 = npx2; + + switch (mode) { + case pNone: + return; + case pA: + if (!ropaque) paint_or_set(kTRUE); + px1 = TMath::Max(px1p, TMath::Min(px, px2 - kMinSize)); + py2 = TMath::Max(py2p, TMath::Min(py, py1 - kMinSize)); + if (!adjustRatio(-1)) { + px1 = prevpx1; + py2 = prevpy2; } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pL) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - px1 += px - pxold; - if (px1 > px2-kMinSize) { px1 = px2-kMinSize; wx = px1; } - if (px1 < px1p) { px1 = px1p; wx = px1; } - if (fixedr) { - Double_t dy = Double_t(TMath::Abs(px2-px1))/parent->UtoPixel(1.) / - fAspectRatio; - Int_t npy2 = py1 - TMath::Abs(parent->VtoAbsPixel(dy) - - parent->VtoAbsPixel(0)); - if (npy2 < py2p) - px1 -= px - pxold; - else - py2 = npy2; + paint_or_set(!ropaque); + break; + case pB: + if (!ropaque) paint_or_set(kTRUE); + px2 = TMath::Min(px2p, TMath::Max(px, px1 + kMinSize)); + py2 = TMath::Max(py2p, TMath::Min(py, py1 - kMinSize)); + if (!adjustRatio(-1)) { + px2 = prevpx2; + py2 = prevpy2; } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pR) { - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - px2 += px - pxold; - if (px2 < px1+kMinSize) { px2 = px1+kMinSize; wx = px2; } - if (px2 > px2p) { px2 = px2p; wx = px2; } - if (fixedr) { - Double_t dy = Double_t(TMath::Abs(px2-px1))/parent->UtoPixel(1.) / - fAspectRatio; - Int_t npy2 = py1 - TMath::Abs(parent->VtoAbsPixel(dy) - - parent->VtoAbsPixel(0)); - if (npy2 < py2p) - px2 -= px - pxold; - else - py2 = npy2; + paint_or_set(!ropaque); + break; + case pC: + if (!ropaque) paint_or_set(kTRUE); + px2 = TMath::Min(px2p, TMath::Max(px, px1 + kMinSize)); + py1 = TMath::Min(py1p, TMath::Max(py, py2 + kMinSize)); + if (!adjustRatio(1)) { + px2 = prevpx2; + py1 = prevpy1; } - if (!ropaque) action(kTRUE, px1, py1, px2, py2); - } - if (pINSIDE) { - if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the old box - Int_t dx = px - pxold; - Int_t dy = py - pyold; - px1 += dx; py1 += dy; px2 += dx; py2 += dy; - if (px1 < px1p) { dx = px1p - px1; px1 += dx; px2 += dx; wx = px+dx; } - if (px2 > px2p) { dx = px2 - px2p; px1 -= dx; px2 -= dx; wx = px-dx; } - if (py1 > py1p) { dy = py1 - py1p; py1 -= dy; py2 -= dy; wy = py-dy; } - if (py2 < py2p) { dy = py2p - py2; py1 += dy; py2 += dy; wy = py+dy; } - if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the new box - } - - if (wx || wy) { - if (wx) px = wx; - if (wy) py = wy; - GetCanvasImp()->Warp(px, py); - } - - pxold = px; - pyold = py; - - if ((pINSIDE && opaque) || (fResizing && ropaque)) { - if (px != pxorg || py != pyorg) { - if (pA) - action(kFALSE, pxold, pyt, pxt, pyold); - if (pB) - action(kFALSE, pxl, pyt, pxold, pyold); - if (pC) - action(kFALSE, pxl, pyold, pxold, pyl); - if (pD) - action(kFALSE, pxold, pyold, pxt, pyl); - if (pTop || pBot || pL || pR || pINSIDE) - action(kFALSE, px1, py1, px2, py2); + paint_or_set(!ropaque); + break; + case pD: + if (!ropaque) paint_or_set(kTRUE); + px1 = TMath::Max(px1p, TMath::Min(px, px2 - kMinSize)); + py1 = TMath::Min(py1p, TMath::Max(py, py2 + kMinSize)); + if (!adjustRatio(1)) { + px1 = prevpx1; + py1 = prevpy1; } + paint_or_set(!ropaque); + break; + case pTop: + if (!ropaque) paint_or_set(kTRUE); + py2 = TMath::Max(py2p, TMath::Min(py, py1 - kMinSize)); + if (!adjustRatio(11)) + py2 = prevpy2; + paint_or_set(!ropaque); + break; + case pBot: + if (!ropaque) paint_or_set(kTRUE); + py1 = TMath::Min(py1p, TMath::Max(py, py2 + kMinSize)); + if (!adjustRatio(11)) + py1 = prevpy1; + paint_or_set(!ropaque); + break; + case pL: + if (!ropaque) paint_or_set(kTRUE); + px1 = TMath::Max(px1p, TMath::Min(px, px2 - kMinSize)); + if (!adjustRatio(0)) + px1 = prevpx1; + paint_or_set(!ropaque); + break; + case pR: + if (!ropaque) paint_or_set(kTRUE); + px2 = TMath::Min(px2p, TMath::Max(px, px1 + kMinSize)); + if (!adjustRatio(0)) + px2 = prevpx2; + paint_or_set(!ropaque); + break; + case pINSIDE: + if (!opaque) paint_or_set(kTRUE); // draw the old box + px2 += px - dpx1 - px1; + px1 = px - dpx1; + py1 += py - dpy2 - py2; + py2 = py - dpy2; + if (px1 < px1p) { px2 += px1p - px1; px1 = px1p; } + if (px2 > px2p) { px1 -= px2 - px2p; px2 = px2p; } + if (py1 > py1p) { py2 -= py1 - py1p; py1 = py1p; } + if (py2 < py2p) { py1 += py2p - py2; py2 = py2p; } + paint_or_set(!opaque); // draw the new box + break; + } + if ((mode == pINSIDE && opaque) || (fResizing && ropaque)) { // Reset pad parameters and recompute conversion coefficients ResizePad(); - - if (pINSIDE) gPad->ShowGuidelines(this, event); - if (pTop) gPad->ShowGuidelines(this, event, 't', true); - if (pBot) gPad->ShowGuidelines(this, event, 'b', true); - if (pL) gPad->ShowGuidelines(this, event, 'l', true); - if (pR) gPad->ShowGuidelines(this, event, 'r', true); - if (pA) gPad->ShowGuidelines(this, event, '1', true); - if (pB) gPad->ShowGuidelines(this, event, '2', true); - if (pC) gPad->ShowGuidelines(this, event, '3', true); - if (pD) gPad->ShowGuidelines(this, event, '4', true); + switch(mode) { + case pINSIDE: gPad->ShowGuidelines(this, event); break; + case pTop: gPad->ShowGuidelines(this, event, 't', true); break; + case pBot: gPad->ShowGuidelines(this, event, 'b', true); break; + case pL: gPad->ShowGuidelines(this, event, 'l', true); break; + case pR: gPad->ShowGuidelines(this, event, 'r', true); break; + case pA: gPad->ShowGuidelines(this, event, '1', true); break; + case pB: gPad->ShowGuidelines(this, event, '2', true); break; + case pC: gPad->ShowGuidelines(this, event, '3', true); break; + case pD: gPad->ShowGuidelines(this, event, '4', true); break; + default: break; + } Modified(kTRUE); } @@ -2392,22 +2287,14 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (gROOT->IsEscaped()) { gROOT->SetEscape(kFALSE); fResizing = kFALSE; + mode = pNone; break; } - if ((pINSIDE && !opaque) || (fResizing && !ropaque)) { - if (pA) - action(kFALSE, pxold, pyt, pxt, pyold); - if (pB) - action(kFALSE, pxl, pyt, pxold, pyold); - if (pC) - action(kFALSE, pxl, pyold, pxold, pyl); - if (pD) - action(kFALSE, pxold, pyold, pxt, pyl); - if (pTop || pBot || pL || pR || pINSIDE) - action(kFALSE, px1, py1, px2, py2); + if ((mode == pINSIDE && !opaque) || (fResizing && !ropaque)) { + paint_or_set(kFALSE); - if (pA || pB || pC || pD || pTop || pL || pR || pBot) + if (fResizing) Modified(kTRUE); // Reset pad parameters and recompute conversion coefficients @@ -2417,6 +2304,7 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) RangeChanged(); } + mode = pNone; fResizing = kFALSE; break; From fc706985da25ca0bfbb11c52281cd353dee192d0 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 14 Apr 2026 12:38:16 +0200 Subject: [PATCH 09/10] [gui] provide kMouseEnter events for canvas Even it will not be used - for symmetry such events should be provided to TCanvas Co-authored-by: Bertrand Bellenot --- gui/gui/src/TRootCanvas.cxx | 8 ++++++-- gui/gui/src/TRootEmbeddedCanvas.cxx | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/gui/gui/src/TRootCanvas.cxx b/gui/gui/src/TRootCanvas.cxx index db1bcf4b7ae84..140d47fed4010 100644 --- a/gui/gui/src/TRootCanvas.cxx +++ b/gui/gui/src/TRootCanvas.cxx @@ -263,8 +263,9 @@ TRootContainer::TRootContainer(TRootCanvas *c, Window_t id, const TGWindow *p) kButtonPressMask | kButtonReleaseMask | kPointerMotionMask, kNone, kNone); - AddInput(kKeyPressMask | kKeyReleaseMask | kPointerMotionMask | - kExposureMask | kStructureNotifyMask | kLeaveWindowMask); + AddInput(kKeyPressMask | kKeyReleaseMask | kPointerMotionMask | kExposureMask | + kStructureNotifyMask | kEnterWindowMask | kLeaveWindowMask); + fEditDisabled = kEditDisable; } @@ -1968,6 +1969,9 @@ Bool_t TRootCanvas::HandleContainerCrossing(Event_t *event) if (event->fType == kLeaveNotify && event->fCode == kNotifyNormal) fCanvas->HandleInput(kMouseLeave, x, y); + if (event->fType == kEnterNotify && event->fCode == kNotifyNormal) + fCanvas->HandleInput(kMouseEnter, x, y); + return kTRUE; } diff --git a/gui/gui/src/TRootEmbeddedCanvas.cxx b/gui/gui/src/TRootEmbeddedCanvas.cxx index 4466c68a64cd9..bcd9588bbd862 100644 --- a/gui/gui/src/TRootEmbeddedCanvas.cxx +++ b/gui/gui/src/TRootEmbeddedCanvas.cxx @@ -84,7 +84,7 @@ TRootEmbeddedContainer::TRootEmbeddedContainer(TRootEmbeddedCanvas *c, Window_t kPointerMotionMask, kNone, kNone); AddInput(kKeyPressMask | kKeyReleaseMask | kPointerMotionMask | - kExposureMask | kStructureNotifyMask | kLeaveWindowMask); + kExposureMask | kStructureNotifyMask | kEnterWindowMask | kLeaveWindowMask); fEditDisabled = kEditDisableGrab; } @@ -419,6 +419,9 @@ Bool_t TRootEmbeddedCanvas::HandleContainerCrossing(Event_t *event) if (event->fType == kLeaveNotify && event->fCode == kNotifyNormal) fCanvas->HandleInput(kMouseLeave, x, y); + if (event->fType == kEnterNotify && event->fCode == kNotifyNormal) + fCanvas->HandleInput(kMouseEnter, x, y); + return kTRUE; } From 8b7a638c46264edcb557b90cf6b8f372cb06d2c7 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 14 Apr 2026 12:39:51 +0200 Subject: [PATCH 10/10] [tcanvas] Do not toggle feedback mode when enter/leave canvas Events handling can be continued also when mouse already outside canvas. Therefore keep feedback mode as is. It will be switched off/on when needed by starting objects dragging or by start painting --- graf2d/gpad/src/TCanvas.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graf2d/gpad/src/TCanvas.cxx b/graf2d/gpad/src/TCanvas.cxx index 25964427aaf91..3e6a550b2d705 100644 --- a/graf2d/gpad/src/TCanvas.cxx +++ b/graf2d/gpad/src/TCanvas.cxx @@ -1262,7 +1262,7 @@ void TCanvas::HandleInput(EEventType event, Int_t px, Int_t py) case kMouseEnter: // mouse enters canvas - if (!fDoubleBuffer) FeedbackMode(kTRUE); + //FeedbackMode(kTRUE); break; case kMouseLeave: @@ -1276,7 +1276,7 @@ void TCanvas::HandleInput(EEventType event, Int_t px, Int_t py) EnterLeave(prevSelPad, prevSelObj); fSelected = sobj; fSelectedPad = spad; - if (!fDoubleBuffer) FeedbackMode(kFALSE); + //FeedbackMode(kFALSE); } break;