Skip to content

Commit d606945

Browse files
DidiDerDenkerVogelDaniel Vogel
authored
WPF - Fixed Html Dropdown positioning when near bottom of screen (#4639)
* Fixed positioning of dropdown. * Renamed mouse-teleport, adjusted code-style, moved logic to mouse-adjustor, applied codereview. * Added comments to new class. * Applied codereview, i.e. renamed teleporting-variables and -comments. * Added boolean for the state of the popup. * Set isOpen. * Optimized adjust-method. * Added interface for mouse-adjustor. --------- Co-authored-by: Vogel <vogel@atv-systems.de> Co-authored-by: Daniel Vogel <daniel.vogel@kinoda.de>
1 parent 898ef3d commit d606945

File tree

3 files changed

+236
-16
lines changed

3 files changed

+236
-16
lines changed

CefSharp.Wpf/ChromiumWebBrowser.cs

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ public partial class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBro
140140
/// </summary>
141141
private static bool DesignMode;
142142

143+
/// <summary>
144+
/// The class that coordinates the positioning of the dropdown if wanted.
145+
/// </summary>
146+
private IMouseAdjustor mouseAdjustor;
147+
143148
// https://github.com/chromiumembedded/cef/issues/3427
144149
private bool resizeHackIgnoreOnPaint;
145150
private Structs.Size? resizeHackSize;
@@ -484,6 +489,14 @@ public ChromiumWebBrowser()
484489
{
485490
NoInliningConstructor();
486491
}
492+
493+
bool adjust = true;
494+
495+
if (adjust)
496+
this.mouseAdjustor = new MouseAdjustor();
497+
498+
else
499+
this.mouseAdjustor = new NoMouseAdjustor();
487500
}
488501

489502
/// <summary>
@@ -1037,7 +1050,11 @@ void IRenderWebBrowser.OnPopupShow(bool isOpen)
10371050
/// <param name="isOpen">if set to <c>true</c> [is open].</param>
10381051
protected virtual void OnPopupShow(bool isOpen)
10391052
{
1040-
UiThreadRunAsync(() => { popupImage.Visibility = isOpen ? Visibility.Visible : Visibility.Hidden; });
1053+
UiThreadRunAsync(() =>
1054+
{
1055+
popupImage.Visibility = isOpen ? Visibility.Visible : Visibility.Hidden;
1056+
mouseAdjustor.OnPopupShow(isOpen);
1057+
});
10411058
}
10421059

10431060
/// <inheritdoc />
@@ -1747,10 +1764,10 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
17471764
{
17481765
CleanupElement = window;
17491766
}
1750-
else if(CleanupElement is Window parent)
1767+
else if (CleanupElement is Window parent)
17511768
{
1752-
//If the CleanupElement is a window then move it to the new Window
1753-
if(parent != window)
1769+
// If the CleanupElement is a window then move it to the new Window
1770+
if (parent != window)
17541771
{
17551772
CleanupElement = window;
17561773
}
@@ -2108,8 +2125,10 @@ private void SetPopupSizeAndPositionImpl(Rect rect)
21082125
popupImage.Width = rect.Width;
21092126
popupImage.Height = rect.Height;
21102127

2111-
Canvas.SetLeft(popupImage, rect.X);
2112-
Canvas.SetTop(popupImage, rect.Y);
2128+
Point point = this.mouseAdjustor.UpdatePopupSizeAndPosition(rect, this.viewRect);
2129+
2130+
Canvas.SetLeft(popupImage, point.X);
2131+
Canvas.SetTop(popupImage, point.Y);
21132132
}
21142133

21152134
/// <summary>
@@ -2276,7 +2295,8 @@ protected override void OnMouseMove(MouseEventArgs e)
22762295
var point = e.GetPosition(this);
22772296
var modifiers = e.GetModifiers();
22782297

2279-
browser.GetHost().SendMouseMoveEvent((int)point.X, (int)point.Y, false, modifiers);
2298+
var adjustedPoint = mouseAdjustor.GetAdjustedMouseCoords(point);
2299+
browser.GetHost().SendMouseMoveEvent(adjustedPoint.X, adjustedPoint.Y, false, modifiers);
22802300
}
22812301

22822302
base.OnMouseMove(e);
@@ -2293,16 +2313,15 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
22932313
var point = e.GetPosition(this);
22942314
var modifiers = e.GetModifiers();
22952315
var isShiftKeyDown = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
2296-
var pointX = (int)point.X;
2297-
var pointY = (int)point.Y;
2316+
var adjustedPoint = mouseAdjustor.GetAdjustedMouseCoords(point);
22982317

22992318
browser.SendMouseWheelEvent(
2300-
pointX,
2301-
pointY,
2319+
adjustedPoint.X,
2320+
adjustedPoint.Y,
23022321
deltaX: isShiftKeyDown ? e.Delta : 0,
23032322
deltaY: !isShiftKeyDown ? e.Delta : 0,
23042323
modifiers: modifiers);
2305-
2324+
23062325
e.Handled = true;
23072326
}
23082327

@@ -2444,12 +2463,15 @@ private void OnMouseButton(MouseButtonEventArgs e)
24442463
//Anything greater than 3 then we send click count of 1
24452464
var clickCount = e.ClickCount;
24462465

2447-
if(clickCount > 3)
2466+
if (clickCount > 3)
24482467
{
24492468
clickCount = 1;
24502469
}
24512470

2452-
browser.GetHost().SendMouseClickEvent((int)point.X, (int)point.Y, (MouseButtonType)e.ChangedButton, mouseUp, clickCount, modifiers);
2471+
2472+
var adjustedPoint = mouseAdjustor.GetAdjustedMouseCoords(point);
2473+
browser.GetHost().SendMouseClickEvent(adjustedPoint.X, adjustedPoint.Y, (MouseButtonType)e.ChangedButton, mouseUp, clickCount, modifiers);
2474+
// browser.GetHost().SendMouseClickEvent(mouseTeleport.originalRect.X + mouseTeleport.originalRect.Width, (int)point.Y, (MouseButtonType)e.ChangedButton, mouseUp, clickCount, modifiers);
24532475
}
24542476

24552477
e.Handled = true;
@@ -2558,7 +2580,7 @@ protected override AutomationPeer OnCreateAutomationPeer()
25582580
/// <inheritdoc/>
25592581
public void Load(string url)
25602582
{
2561-
if(IsDisposed)
2583+
if (IsDisposed)
25622584
{
25632585
return;
25642586
}
@@ -2776,7 +2798,7 @@ public IBrowser GetBrowser()
27762798

27772799
return browser;
27782800
}
2779-
2801+
27802802
private async Task CefUiThreadRunAsync(Action action)
27812803
{
27822804
if (!IsDisposed && InternalIsBrowserInitialized())

CefSharp.Wpf/IMouseAdjustor.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using CefSharp.Structs;
3+
4+
namespace CefSharp.Wpf
5+
{
6+
/// <summary>
7+
/// Implement this interface to control how the mouse-adjustor is used.
8+
/// </summary>
9+
public interface IMouseAdjustor : IDisposable
10+
{
11+
System.Windows.Point UpdatePopupSizeAndPosition(Rect originalRect, Rect viewRect);
12+
void OnPopupShow(bool isOpen);
13+
Point GetAdjustedMouseCoords(System.Windows.Point point);
14+
}
15+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
using CefSharp.Structs;
2+
3+
namespace CefSharp.Wpf.Internals
4+
{
5+
public class NoMouseAdjustor : IMouseAdjustor
6+
{
7+
public virtual void Dispose()
8+
{
9+
}
10+
11+
public System.Windows.Point UpdatePopupSizeAndPosition(Rect originalRect, Rect viewRect)
12+
{
13+
return new System.Windows.Point(originalRect.X, originalRect.Y);
14+
}
15+
16+
public void OnPopupShow(bool isOpen)
17+
{
18+
}
19+
20+
public Point GetAdjustedMouseCoords(System.Windows.Point point)
21+
{
22+
return new Point((int)point.X, (int)point.Y);
23+
}
24+
}
25+
26+
public class MouseAdjustor : IMouseAdjustor
27+
{
28+
/// <summary>
29+
/// The x-offset.
30+
/// </summary>
31+
private int xOffset;
32+
33+
/// <summary>
34+
/// The y-offset.
35+
/// </summary>
36+
private int yOffset;
37+
38+
/// <summary>
39+
/// The original rect.
40+
/// </summary>
41+
private Rect originalRect;
42+
43+
/// <summary>
44+
/// The adjusted rect.
45+
/// </summary>
46+
private Rect adjustedRect;
47+
48+
/// <summary>
49+
/// If the popup is open or not.
50+
/// </summary>
51+
private bool isOpen;
52+
53+
/// <summary>
54+
/// This method is required for the interface.
55+
/// </summary>
56+
public virtual void Dispose()
57+
{
58+
}
59+
60+
/// <summary>
61+
/// Updates the size and the position of the popup.
62+
/// </summary>
63+
/// <param name="originalRect"></param>
64+
/// <param name="viewRect"></param>
65+
/// <returns>The adjusted point.</returns>
66+
public System.Windows.Point UpdatePopupSizeAndPosition(Rect originalRect, Rect viewRect)
67+
{
68+
int x = originalRect.X,
69+
prevX = originalRect.X,
70+
y = originalRect.Y,
71+
prevY = originalRect.Y,
72+
xOffset = 0,
73+
yOffset = 0;
74+
75+
// If popup goes outside the view, try to reposition origin
76+
if (originalRect.X + originalRect.Width > viewRect.Width)
77+
{
78+
x = viewRect.Width - originalRect.Width;
79+
xOffset = prevX - x;
80+
}
81+
82+
if (originalRect.Y + originalRect.Height > viewRect.Height)
83+
{
84+
y = y - originalRect.Height - 20;
85+
yOffset = prevY - y;
86+
}
87+
88+
// If x or y became negative, move them to 0 again
89+
if (x < 0)
90+
{
91+
x = 0;
92+
xOffset = prevX;
93+
}
94+
95+
if (y < 0)
96+
{
97+
y = 0;
98+
yOffset = prevY;
99+
}
100+
101+
if (x != prevX || y != prevY)
102+
{
103+
this.isOpen = true;
104+
105+
this.xOffset = xOffset;
106+
this.yOffset = yOffset;
107+
108+
Rect adjustedRect = new Rect(x, y, x + originalRect.Width, y + originalRect.Height);
109+
110+
this.originalRect = originalRect;
111+
this.adjustedRect = adjustedRect;
112+
113+
if (this.originalRect.Y < this.adjustedRect.Y + this.adjustedRect.Height)
114+
{
115+
var newY = this.adjustedRect.Y + this.adjustedRect.Height;
116+
this.originalRect = new Rect(originalRect.X, newY, originalRect.Width, originalRect.Y + originalRect.Height - newY);
117+
}
118+
}
119+
120+
return new System.Windows.Point(x, y);
121+
}
122+
123+
/// <summary>
124+
/// Resets the offsets and original-rect.
125+
/// <param name="isOpen">If the popup is open or not.</param>
126+
/// </summary>
127+
public void OnPopupShow(bool isOpen)
128+
{
129+
if (!isOpen)
130+
{
131+
this.isOpen = false;
132+
133+
this.xOffset = 0;
134+
this.yOffset = 0;
135+
136+
this.originalRect = new Rect();
137+
this.originalRect = new Rect();
138+
}
139+
}
140+
141+
/// <summary>
142+
/// Adjusts the mouse-coordinates when the popup is visible.
143+
/// </summary>
144+
/// <param name="point">The original point.</param>
145+
/// <returns>The adjusted point if needed, else the original point.</returns>
146+
public Point GetAdjustedMouseCoords(System.Windows.Point point)
147+
{
148+
if (!this.isOpen)
149+
return new Point((int)point.X, (int)point.Y);
150+
151+
if (!this.IsInsideOriginalRect(point) && IsInsideAdjustedRect(point))
152+
return new Point((int)point.X + this.xOffset, (int)point.Y + this.yOffset);
153+
154+
return new Point((int)point.X, (int)point.Y);
155+
}
156+
157+
/// <summary>
158+
/// Checks if the given point is inside the original-rect.
159+
/// </summary>
160+
/// <param name="point">The point.</param>
161+
/// <returns>Returns true if the point is inside the original rect, else return false.</returns>
162+
private bool IsInsideOriginalRect(System.Windows.Point point)
163+
{
164+
return point.X >= this.originalRect.X &&
165+
point.X < this.originalRect.X + this.originalRect.Width &&
166+
point.Y >= this.originalRect.Y &&
167+
point.Y < this.originalRect.Y + this.originalRect.Height;
168+
}
169+
170+
/// <summary>
171+
/// Checks if the given point is inside the adjusted rect.
172+
/// </summary>
173+
/// <param name="point">The point.</param>
174+
/// <returns>Returns true if the point is inside the adjusted rect, else return false.</returns>
175+
private bool IsInsideAdjustedRect(System.Windows.Point point)
176+
{
177+
return point.X >= this.adjustedRect.X &&
178+
point.X < this.adjustedRect.X + this.adjustedRect.Width &&
179+
point.Y >= this.adjustedRect.Y &&
180+
point.Y < this.adjustedRect.Y + this.adjustedRect.Height;
181+
}
182+
}
183+
}

0 commit comments

Comments
 (0)