Changeset - r28581:c07444122f9c
[Not reviewed]
master
0 2 0
Peter Nelson - 10 months ago 2024-01-27 18:44:27
peter1138@openttd.org
Fix #11894: Defer window OnResize event to avoid processing multiple times per input tick. (#11900)
2 files changed with 28 insertions and 4 deletions:
0 comments (0 inline, 0 general)
src/window.cpp
Show inline comments
 
@@ -1438,26 +1438,26 @@ void Window::FindWindowPlacementAndResiz
 
		int enlarge_x = std::max(std::min(def_width  - this->width,  _screen.width - this->width),  0);
 
		int enlarge_y = std::max(std::min(def_height - this->height, free_height   - this->height), 0);
 

	
 
		/* X and Y has to go by step.. calculate it.
 
		 * The cast to int is necessary else x/y are implicitly casted to
 
		 * unsigned int, which won't work. */
 
		if (this->resize.step_width  > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
 
		if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
 

	
 
		ResizeWindow(this, enlarge_x, enlarge_y);
 
		/* ResizeWindow() calls this->OnResize(). */
 
	} else {
 
		/* Always call OnResize; that way the scrollbars and matrices get initialized. */
 
		this->OnResize();
 
		/* Schedule OnResize; that way the scrollbars and matrices get initialized. */
 
		this->ScheduleResize();
 
	}
 

	
 
	int nx = this->left;
 
	int ny = this->top;
 

	
 
	if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
 

	
 
	const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
	ny = std::max(ny, (wt == nullptr || this == wt || this->top == 0) ? 0 : wt->height);
 
	nx = std::max(nx, 0);
 

	
 
	if (this->viewport != nullptr) {
 
@@ -2041,26 +2041,26 @@ void ResizeWindow(Window *w, int delta_x
 
		uint new_xinc = std::max(0, (w->nested_root->resize_x == 0) ? 0 : (int)(w->nested_root->current_x - w->nested_root->smallest_x) + delta_x);
 
		uint new_yinc = std::max(0, (w->nested_root->resize_y == 0) ? 0 : (int)(w->nested_root->current_y - w->nested_root->smallest_y) + delta_y);
 
		assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
 
		assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
 

	
 
		w->nested_root->AssignSizePosition(ST_RESIZE, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, _current_text_dir == TD_RTL);
 
		w->width  = w->nested_root->current_x;
 
		w->height = w->nested_root->current_y;
 
	}
 

	
 
	EnsureVisibleCaption(w, w->left, w->top);
 

	
 
	/* Always call OnResize to make sure everything is initialised correctly if it needs to be. */
 
	w->OnResize();
 
	/* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
 
	w->ScheduleResize();
 
	w->SetDirty();
 
}
 

	
 
/**
 
 * Return the top of the main view available for general use.
 
 * @return Uppermost vertical coordinate available.
 
 * @note Above the upper y coordinate is often the main toolbar.
 
 */
 
int GetMainViewTop()
 
{
 
	Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
	return (w == nullptr) ? 0 : w->top + w->height;
 
@@ -3045,24 +3045,25 @@ void UpdateWindows()
 
	last_time = now;
 

	
 
	PerformanceMeasurer framerate(PFE_DRAWING);
 
	PerformanceAccumulator::Reset(PFE_DRAWWORLD);
 

	
 
	ProcessPendingPerformanceMeasurements();
 

	
 
	TimerManager<TimerWindow>::Elapsed(delta_ms);
 
	CallWindowRealtimeTickEvent(delta_ms.count());
 

	
 
	/* Process invalidations before anything else. */
 
	for (Window *w : Window::Iterate()) {
 
		w->ProcessScheduledResize();
 
		w->ProcessScheduledInvalidations();
 
		w->ProcessHighlightedInvalidations();
 
	}
 

	
 
	/* Skip the actual drawing on dedicated servers without screen.
 
	 * But still empty the invalidation queues above. */
 
	if (_network_dedicated) return;
 

	
 
	DrawDirtyBlocks();
 

	
 
	for (Window *w : Window::Iterate()) {
 
		/* Update viewport only if window is not shaded. */
 
@@ -3103,24 +3104,44 @@ void SetWindowWidgetDirty(WindowClass cl
 
/**
 
 * Mark all windows of a particular class as dirty (in need of repainting)
 
 * @param cls Window class
 
 */
 
void SetWindowClassesDirty(WindowClass cls)
 
{
 
	for (const Window *w : Window::Iterate()) {
 
		if (w->window_class == cls) w->SetDirty();
 
	}
 
}
 

	
 
/**
 
 * Mark this window as resized and in need of OnResize() event.
 
 */
 
void Window::ScheduleResize()
 
{
 
	this->scheduled_resize = true;
 
}
 

	
 
/**
 
 * Process scheduled OnResize() event.
 
 */
 
void Window::ProcessScheduledResize()
 
{
 
	/* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
 
	while (this->scheduled_resize) {
 
		this->scheduled_resize = false;
 
		this->OnResize();
 
	}
 
}
 

	
 
/**
 
 * Mark this window's data as invalid (in need of re-computing)
 
 * @param data The data to invalidate with
 
 * @param gui_scope Whether the function is called from GUI scope.
 
 */
 
void Window::InvalidateData(int data, bool gui_scope)
 
{
 
	this->SetDirty();
 
	if (!gui_scope) {
 
		/* Schedule GUI-scope invalidation for next redraw. */
 
		this->scheduled_invalidation_data.push_back(data);
 
	}
 
	this->OnInvalidateData(data, gui_scope);
src/window_gui.h
Show inline comments
 
@@ -265,24 +265,25 @@ enum TooltipCloseCondition {
 
 * Data structure for an opened window
 
 */
 
struct Window : ZeroedMemoryAllocator {
 
private:
 
	static std::vector<Window *> closed_windows;
 

	
 
protected:
 
	void InitializeData(WindowNumber window_number);
 
	void InitializePositionSize(int x, int y, int min_width, int min_height);
 
	virtual void FindWindowPlacementAndResize(int def_width, int def_height);
 

	
 
	std::vector<int> scheduled_invalidation_data;  ///< Data of scheduled OnInvalidateData() calls.
 
	bool scheduled_resize; ///< Set if window has been resized.
 

	
 
	/* Protected to prevent deletion anywhere outside Window::DeleteClosedWindows(). */
 
	virtual ~Window();
 

	
 
public:
 
	Window(WindowDesc *desc);
 

	
 
	/**
 
	 * Helper allocation function to disallow something.
 
	 * Don't allow arrays; arrays of Windows are pointless as you need
 
	 * to destruct them all at the same time too, which is kinda hard.
 
	 * @param size the amount of space not to allocate
 
@@ -550,24 +551,26 @@ public:
 

	
 
	void SetDirty() const;
 
	void ReInit(int rx = 0, int ry = 0, bool reposition = false);
 

	
 
	/** Is window shaded currently? */
 
	inline bool IsShaded() const
 
	{
 
		return this->shade_select != nullptr && this->shade_select->shown_plane == SZSP_HORIZONTAL;
 
	}
 

	
 
	void SetShaded(bool make_shaded);
 

	
 
	void ScheduleResize();
 
	void ProcessScheduledResize();
 
	void InvalidateData(int data = 0, bool gui_scope = true);
 
	void ProcessScheduledInvalidations();
 
	void ProcessHighlightedInvalidations();
 

	
 
	/*** Event handling ***/
 

	
 
	/**
 
	 * Notification that the nested widget tree gets initialized. The event can be used to perform general computations.
 
	 * @note #nested_root and/or #widget_lookup (normally accessed via #GetWidget()) may not exist during this call.
 
	 */
 
	virtual void OnInit() { }
 

	
0 comments (0 inline, 0 general)