From bf1b09699543fa11113e834ec4f796e115cf5adb Mon Sep 17 00:00:00 2001
From: DarkGod <darkgod@net-core.org>
Date: Mon, 4 Nov 2013 00:47:22 +0100
Subject: [PATCH] Long chat dialogs will scroll

---
 game/engines/default/engine/dialogs/Chat.lua  |  2 +-
 .../default/engine/ui/VariableList.lua        | 85 ++++++++++++++++---
 2 files changed, 76 insertions(+), 11 deletions(-)

diff --git a/game/engines/default/engine/dialogs/Chat.lua b/game/engines/default/engine/dialogs/Chat.lua
index 8282601052..cb50438a1e 100644
--- a/game/engines/default/engine/dialogs/Chat.lua
+++ b/game/engines/default/engine/dialogs/Chat.lua
@@ -43,7 +43,7 @@ function _M:init(chat, id)
 	self:generateList()
 
 	self.c_desc = Textzone.new{width=self.iw - 10 - xoff, height=1, auto_height=true, text=self.text.."\n"}
-	self.c_list = VariableList.new{width=self.iw - 10 - xoff, list=self.list, fct=function(item) self:use(item) end, select=function(item) self:select(item) end}
+	self.c_list = VariableList.new{width=self.iw - 10 - xoff, max_height=game.h * 0.70 - self.c_desc.h, list=self.list, fct=function(item) self:use(item) end, select=function(item) self:select(item) end}
 
 	local uis = {
 		{left=0, top=0, ui=self.c_desc},
diff --git a/game/engines/default/engine/ui/VariableList.lua b/game/engines/default/engine/ui/VariableList.lua
index 4615864784..e9fb954910 100644
--- a/game/engines/default/engine/ui/VariableList.lua
+++ b/game/engines/default/engine/ui/VariableList.lua
@@ -20,6 +20,7 @@
 require "engine.class"
 local Base = require "engine.ui.Base"
 local Focusable = require "engine.ui.Focusable"
+local Slider = require "engine.ui.Slider"
 
 --- A generic UI list
 module(..., package.seeall, class.inherit(Base, Focusable))
@@ -27,8 +28,11 @@ module(..., package.seeall, class.inherit(Base, Focusable))
 function _M:init(t)
 	self.list = assert(t.list, "no list list")
 	self.w = assert(t.width, "no list width")
+	self.max_h = t.max_height
 	self.fct = t.fct
 	self.select = t.select
+	self.scrollbar = t.scrollbar
+	self.min_items_shown = t.min_items_shown or 3
 	self.display_prop = t.display_prop or "name"
 
 	Base.init(self, t)
@@ -39,6 +43,7 @@ function _M:generate()
 	self.key:reset()
 
 	self.sel = 1
+	self.scroll = 1
 	self.max = #self.list
 
 	local fw, fh = self.w, self.font_h
@@ -49,7 +54,8 @@ function _M:generate()
 	self.frame_usel = self:makeFrame("ui/selector", fw, fh)
 
 	-- Draw the list items
-	self.h = 0
+	local sh = 0
+	local minh = 0
 	for i, item in ipairs(self.list) do
 		local color = item.color or {255,255,255}
 		local text = item[self.display_prop]:splitLines(fw - self.frame_sel.b4.w - self.frame_sel.b6.w, self.font)
@@ -62,18 +68,38 @@ function _M:generate()
 			color_r, color_g, color_b = s:drawColorStringBlended(self.font, text[z], self.frame_sel.b4.w, self.frame_sel.b8.w / 3 + self.font_h * (z-1), color_r, color_g, color_b, true)
 		end
 
+		item.start_h = sh
 		item.fh = fh
 		item._tex = {s:glTexture()}
 
-		self.mouse:registerZone(0, self.h, self.w, fh, function(button, x, y, xrel, yrel, bx, by, event)
-			if self.sel and self.list[self.sel] then self.list[self.sel].focus_decay = self.focus_decay_max end
-			self.sel = i
-			self:onSelect()
-			if button == "left" and event == "button" then self:onUse() end
-		end)
-
-		self.h = self.h + fh
+		sh = sh + fh
+		if i <= self.min_items_shown then minh = sh end
 	end
+	self.h = math.max(minh, math.min(self.max_h or 1000000, sh))
+	if sh > self.h then self.scrollbar = true end
+
+	self.scroll_inertia = 0
+	self.scroll = 0
+	if self.scrollbar then self.scrollbar = Slider.new{size=self.h, max=sh} end
+
+	self.mouse:registerZone(0, 0, self.w, self.h, function(button, x, y, xrel, yrel, bx, by, event)
+		self.last_input_was_keyboard = false
+
+		if event == "button" and button == "wheelup" then if self.scrollbar then self.scroll_inertia = math.min(self.scroll_inertia, 0) - 5 end
+		elseif event == "button" and button == "wheeldown" then if self.scrollbar then self.scroll_inertia = math.max(self.scroll_inertia, 0) + 5 end
+		end
+
+		for i = 1, #self.list do
+			local item = self.list[i]
+			if by + self.scroll >= item.start_h and by + self.scroll < item.start_h + item.fh then
+				if self.sel and self.list[self.sel] then self.list[self.sel].focus_decay = self.focus_decay_max end
+				self.sel = i
+				self:onSelect()
+				if button == "left" and event == "button" then self:onUse() end
+				break
+			end
+		end
+	end)
 
 	-- Add UI controls
 	self.key:addBinds{
@@ -101,10 +127,41 @@ function _M:onSelect()
 	local item = self.list[self.sel]
 	if not item then return end
 
+	if self.scrollbar then
+		local pos = 0
+		for i = 1, #self.list do
+			local itm = self.list[i]
+			pos = pos + itm.fh
+			-- we've reached selected row
+			if self.sel == i then
+				-- check if it was visible if not go scroll over there
+				if pos - itm.fh < self.scrollbar.pos then self.scrollbar.pos = util.minBound(pos - itm.fh, 0, self.scrollbar.max)
+				elseif pos > self.scrollbar.pos + self.h then self.scrollbar.pos = util.minBound(pos - self.h, 0, self.scrollbar.max)
+				end
+				break
+			end
+		end
+	end
+
 	if rawget(self, "select") then self.select(item, self.sel) end
 end
 
-function _M:display(x, y, nb_keyframes)
+function _M:display(x, y, nb_keyframes, screen_x, screen_y)
+	local by = y
+	core.display.glScissor(true, screen_x, screen_y, self.w, self.h)
+
+	if self.scrollbar then
+		local tmp_pos = self.scrollbar.pos
+		self.scrollbar.pos = util.minBound(self.scrollbar.pos + self.scroll_inertia, 0, self.scrollbar.max)
+		if self.scroll_inertia > 0 then self.scroll_inertia = math.max(self.scroll_inertia - 1, 0)
+		elseif self.scroll_inertia < 0 then self.scroll_inertia = math.min(self.scroll_inertia + 1, 0)
+		end
+		if self.scrollbar.pos == 0 or self.scrollbar.pos == self.scrollbar.max then self.scroll_inertia = 0 end
+
+		y = y + (self.scrollbar and -self.scrollbar.pos or 0)
+		self.scroll = self.scrollbar.pos
+	end
+
 	for i = 1, self.max do
 		local item = self.list[i]
 		if not item then break end
@@ -129,4 +186,12 @@ function _M:display(x, y, nb_keyframes)
 		item._tex[1]:toScreenFull(x + self.frame_sel.b4.w, y, self.fw, item.fh, item._tex[2], item._tex[3])
 		y = y + item.fh
 	end
+
+	core.display.glScissor(false)
+
+	if self.focused and self.scrollbar then
+		self.scrollbar:display(x + self.w - self.scrollbar.w, by)
+
+		self.last_scroll = self.scrollbar.pos
+	end
 end
-- 
GitLab