Skip to content
This repository has been archived by the owner on Dec 22, 2023. It is now read-only.

Experiment: Let wx.ScrolledView handle scrolling to enable horizontal scrolling #72

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions src/trelby.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,28 +156,29 @@ def save(self):
def saveScDict(self):
util.writeToFile(self.scDictFilename, self.scDict.save(), mainFrame)

class MyPanel(wx.Panel):
class MyPanel(wx.ScrolledWindow):

def __init__(self, parent, id):
wx.Panel.__init__(
wx.ScrolledWindow.__init__(
self, parent, id,
# wxMSW/Windows does not seem to support
# wx.NO_BORDER, which sucks
style = wx.WANTS_CHARS | wx.NO_BORDER)
style = wx.WANTS_CHARS | wx.NO_BORDER | wx.HSCROLL | wx.VSCROLL)

hsizer = wx.BoxSizer(wx.HORIZONTAL)

self.scrollBar = wx.ScrollBar(self, -1, style = wx.SB_VERTICAL)
self.scrollBarVertical = wx.ScrollBar(self, -1, style = wx.SB_VERTICAL)
self.ctrl = MyCtrl(self, -1)

hsizer.Add(self.ctrl, 1, wx.EXPAND)
hsizer.Add(self.scrollBar, 0, wx.EXPAND)

self.scrollBar.Bind(wx.EVT_COMMAND_SCROLL, self.ctrl.OnScroll)
self.scrollBarVertical.Bind(wx.EVT_COMMAND_SCROLL, self.ctrl.OnScroll)

self.scrollBar.Bind(wx.EVT_SET_FOCUS, self.OnScrollbarFocus)
self.scrollBarVertical.Bind(wx.EVT_SET_FOCUS, self.OnScrollbarFocus)

self.SetSizer(hsizer)
self.SetScrollRate(int(self.ctrl.chX), int(self.ctrl.chY))
self.EnableScrolling(True, True)

# we never want the scrollbar to get the keyboard focus, pass it on to
# the main widget
Expand All @@ -186,7 +187,7 @@ def OnScrollbarFocus(self, event):

class MyCtrl(wx.Control):

def __init__(self, parent, id):
def __init__(self, parent: MyPanel, id):
style = wx.WANTS_CHARS | wx.FULL_REPAINT_ON_RESIZE | wx.NO_BORDER
wx.Control.__init__(self, parent, id, style = style)

Expand Down Expand Up @@ -417,8 +418,8 @@ def adjustScrollBar(self):
# about draft / layout mode differences.
approx = int(((height / self.mm2p) / self.chY) / 1.3)

self.panel.scrollBar.SetScrollbar(self.sp.getTopLine(), approx,
len(self.sp.lines) + approx - 1, approx)
self.panel.scrollBarVertical.SetScrollbar(self.sp.getTopLine(), approx,
len(self.sp.lines) + approx - 1, approx)

def clearAutoComp(self):
if self.sp.clearAutoComp():
Expand All @@ -433,8 +434,14 @@ def isUntouched(self):
else:
return True

def getVisibleAreaSize(self):
''' Size of the area visble in the scrolled window'''
return self.panel.GetSize()

def updateScreen(self, redraw = True, setCommon = True):
self.adjustScrollBar()
self.SetMinSize(wx.Size(int(self.pageW), gd.vm.getDocumentHeight(self)))
self.PostSizeEventToParent()

if setCommon:
self.updateCommon()
Expand Down Expand Up @@ -611,7 +618,7 @@ def OnMouseWheel(self, event):
self.updateScreen()

def OnScroll(self, event):
pos = self.panel.scrollBar.GetThumbPosition()
pos = self.panel.scrollBarVertical.GetThumbPosition()
self.sp.setTopLine(pos)
self.sp.clearAutoComp()
self.updateScreen()
Expand Down
131 changes: 88 additions & 43 deletions src/viewmode.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# -*- coding: iso-8859-1 -*-
import math
from typing import Optional

import config
import mypager
import pml
import screenplay
import trelby
import util

# Number of lines the smooth scroll will try to search. 15-20 is a good
Expand Down Expand Up @@ -82,6 +86,9 @@ def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):
def getLineHeight(self, ctrl):
raise Exception("getLineHeight not implemented")

def getDocumentHeight(self, ctrl, untilLine: Optional[int] = None) -> int:
raise Exception("getLineHeight not implemented")

# return width of one page in (floating point) pixels
def getPageWidth(self, ctrl):
raise Exception("getPageWidth not implemented")
Expand Down Expand Up @@ -147,6 +154,10 @@ def makeLineVisibleGeneric(self, ctrl, line, texts, direction, jumpAhead):

# helper function for makeLineVisibleGeneric
def _makeLineVisibleHelper(self, ctrl, line, direction, jumpAhead):
currentLine = ctrl.sp.line
linePosition = self.getDocumentHeight(ctrl, currentLine)


startLine = ctrl.sp.getTopLine()
sign = 1 if (direction == config.SCROLL_DOWN) else -1
i = 1
Expand Down Expand Up @@ -207,13 +218,13 @@ def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):

marginLeft = int(ctrl.mm2p * cfg.marginLeft)
cox = util.clamp((width - ctrl.pageW) // 2, 0)
fyd = ctrl.sp.cfgGl.fontYdelta
fyd = self.getLineHeight(ctrl)
length = len(ls)

texts = []

while (y < height) and (i < length):
y += int((ctrl.sp.getSpacingBefore(i) / 10.0) * fyd)
y += self.__getLineHeightWithSpacing(ctrl, i)

if y >= height:
break
Expand Down Expand Up @@ -245,6 +256,22 @@ def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):
def getLineHeight(self, ctrl):
return ctrl.sp.cfgGl.fontYdelta

def __getLineHeightWithSpacing(self, ctrl, lineNumber: int):
lineHeightWithoutSpacing = self.getLineHeight(ctrl)
return lineHeightWithoutSpacing + int((ctrl.sp.getSpacingBefore(lineNumber) / 10.0) * lineHeightWithoutSpacing)

def getDocumentHeight(self, ctrl, untilLine: Optional[int] = None) -> int:
if untilLine is None:
untilLine = len(ctrl.sp.lines)
height = 0
for i in range(untilLine):
height += self.__getLineHeightWithSpacing(ctrl, i)

height += ctrl.getVisibleAreaSize().GetHeight() # One should always be able to scroll at least until the last line is on top

return height


def getPageWidth(self, ctrl):
# this is not really used for much in draft mode, as it has no
# concept of page width, but it's safer to return something
Expand All @@ -264,7 +291,7 @@ def pageCmd(self, ctrl, cs, dir, texts, dpages):
# Layout view mode. Pages are shown with the actual layout they would
# have.
class ViewModeLayout(ViewMode):

PAGE_GAP = 10 # gap between pages (pixels)
def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):
cfgGui = ctrl.getCfgGui()
textOp = pml.TextOp
Expand All @@ -274,8 +301,6 @@ def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):

width, height = ctrl.GetClientSize()

# gap between pages (pixels)
pageGap = 10
pager = mypager.Pager(ctrl.sp.cfg)

mm2p = ctrl.mm2p
Expand Down Expand Up @@ -307,7 +332,7 @@ def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):
if not topOfPage:
y = -int(op.y * mm2p)
else:
y = pageGap
y = self.PAGE_GAP

break
else:
Expand Down Expand Up @@ -366,7 +391,7 @@ def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):
cfgGui.fonts[op.flags & 3],
op.flags & pml.UNDERLINED))

y = pageY + ctrl.pageH + pageGap
y = pageY + ctrl.pageH + self.PAGE_GAP
pg = None

# if user has inserted new text causing the script to overflow
Expand All @@ -385,6 +410,23 @@ def getLineHeight(self, ctrl):
# lines.
return int(ctrl.chY * ctrl.mm2p + 1.0)

def getDocumentHeight(self, ctrl, untilLine: Optional[int] = None) -> int:
'''
:type ctrl: trelby.MyCtrl
'''
if untilLine is None:
untilPage = len(ctrl.sp.pages)
else:
untilPage = ctrl.sp.line2page(untilLine) - 1

height = (untilPage - 1) * (ctrl.pageH + self.PAGE_GAP)
height += 2 * self.PAGE_GAP # gap on top and on the bottom

if untilLine is not None:
height += ctrl.chY * ctrl.mm2p # in case a specific line is requested, we need to be line exact

return height

def getPageWidth(self, ctrl):
return (ctrl.sp.cfg.paperWidth / ctrl.chX) *\
ctrl.getCfgGui().fonts[pml.NORMAL].fx
Expand All @@ -402,7 +444,7 @@ def pageCmd(self, ctrl, cs, dir, texts, dpages):
# would have, as many pages at a time as fit on the screen, complete pages
# only, in a single row.
class ViewModeSideBySide(ViewMode):

PAGE_GAP = 10 # gap between pages (+ screen left edge)
def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):
cfgGui = ctrl.getCfgGui()
textOp = pml.TextOp
Expand All @@ -414,64 +456,67 @@ def getScreen(self, ctrl, doExtra, partials = False, pageCache = None):

mm2p = ctrl.mm2p

# gap between pages (+ screen left edge)
pageGap = 10

# how many pages fit on screen
pageCnt = max(1, (width - pageGap) // (ctrl.pageW + pageGap))
pagesPerRow = max(1, (width - self.PAGE_GAP) // (ctrl.pageW + self.PAGE_GAP))

pager = mypager.Pager(ctrl.sp.cfg)

topLine = ctrl.sp.getTopLine()
pageNr = ctrl.sp.line2page(topLine)
pageNr = 1 # for now, we just render the whole document

if doExtra and ctrl.sp.cfg.pdfShowSceneNumbers:
pager.scene = ctrl.sp.getSceneNumber(
ctrl.sp.page2lines(pageNr)[0] - 1)

pagesDone = 0

while 1:
if (pagesDone >= pageCnt) or (pageNr >= len(ctrl.sp.pages)):
break
sy = self.PAGE_GAP
while (pageNr < len(ctrl.sp.pages)):
pagesDonePerRow = 0
sx = self.PAGE_GAP
while 1:
if (pagesDonePerRow >= pagesPerRow) or (pageNr >= len(ctrl.sp.pages)):
break

# we'd have to go back an arbitrary number of pages to get an
# accurate number for this in the worst case, so disable it
# altogether.
pager.sceneContNr = 0
# we'd have to go back an arbitrary number of pages to get an
# accurate number for this in the worst case, so disable it
# altogether.
pager.sceneContNr = 0

if pageCache:
pg = pageCache.getPage(pager, pageNr)
else:
pg = ctrl.sp.generatePMLPage(pager, pageNr, False,
doExtra)
if not pg:
break
if pageCache:
pg = pageCache.getPage(pager, pageNr)
else:
pg = ctrl.sp.generatePMLPage(pager, pageNr, False,
doExtra)
if not pg:
break

sx = pageGap + pagesDone * (ctrl.pageW + pageGap)
sy = pageGap
sx += pagesDonePerRow * (ctrl.pageW + self.PAGE_GAP)

dp = DisplayPage(pageNr, sx, sy, sx + ctrl.pageW,
sy + ctrl.pageH)
dpages.append(dp)
dp = DisplayPage(pageNr, sx, sy, sx + ctrl.pageW,
sy + ctrl.pageH)
dpages.append(dp)

for op in pg.ops:
if not isinstance(op, textOp):
continue
for op in pg.ops:
if not isinstance(op, textOp):
continue

texts.append(TextString(op.line, op.text,
int(sx + op.x * mm2p), int(sy + op.y * mm2p),
cfgGui.fonts[op.flags & 3], op.flags & pml.UNDERLINED))
texts.append(TextString(op.line, op.text,
int(sx + op.x * mm2p), int(sy + op.y * mm2p),
cfgGui.fonts[op.flags & 3], op.flags & pml.UNDERLINED))

pageNr += 1
pagesDone += 1
pageNr += 1
pagesDonePerRow += 1
sy += ctrl.pageH + self.PAGE_GAP

return (texts, dpages)

def getLineHeight(self, ctrl):
# the + 1.0 avoids occasional non-consecutive backgrounds for
# lines.
return int(ctrl.chY * ctrl.mm2p + 1.0)
def getDocumentHeight(self, ctrl) -> int:
pagesPerRow = max(1, (ctrl.GetClientSize().GetWidth() - self.PAGE_GAP) // (ctrl.pageW + self.PAGE_GAP))
rows = math.ceil((len(ctrl.sp.pages) - 1) / pagesPerRow)

return int(rows * (ctrl.pageH + self.PAGE_GAP) + 2 * self.PAGE_GAP)

def getPageWidth(self, ctrl):
return (ctrl.sp.cfg.paperWidth / ctrl.chX) *\
Expand Down
Loading