mirror of
https://github.com/lalbornoz/roar.git
synced 2024-12-22 20:36:37 +00:00
Initial implementation of Arabic character reshaping & handling.
libgui/GuiCanvasWxBackend.py:{arabicShapes{},_reshapeArabic()}: initial implementation. libgui/GuiCanvasWxBackend.py:draw{CursorMaskWithJournal,Patch}(): update type signature. libgui/GuiCanvasWxBackend.py:drawPatch(): call _reshapeArabic() on Arabic character patches. libroar/Roar{Assets,Canvas}Window.py: pass updated set of arguments to backend.draw{CursorWithMask,Patch}(). libtools/ToolText.py:{arabicRegEx{},_checkRtl()}: initial implementation. libtools/ToolText.py:_processKeyChar(): call _checkRtl(). libtools/ToolText.py:onKeyboardEvent(): initial implementation of RTL backspace support. assets/text/TODO: updated.
This commit is contained in:
parent
02887e1542
commit
8ab52e7ff1
@ -23,8 +23,9 @@ High-priority list:
|
||||
Queue:
|
||||
1) scrolling bug: start @ top, down key til cursor below visible canvas, scroll down, cursor gone GRRRR
|
||||
2) scrolling bug: scroll down, apply operator to entire canvas, scroll up
|
||||
3) object tool: object size wrong occasionally w/ external objects
|
||||
4) scrolling bug: scroll down, un/redo, scroll up
|
||||
5) clone selection lag
|
||||
3) scrolling bug: scroll down, text tool, move cursor w/ arrow keys
|
||||
4) object tool: object size wrong occasionally w/ external objects
|
||||
5) scrolling bug: scroll down, un/redo, scroll up
|
||||
6) clone selection lag
|
||||
|
||||
vim:ff=dos tw=0
|
||||
|
@ -27,6 +27,47 @@ class GuiBufferedDC(wx.MemoryDC):
|
||||
# }}}
|
||||
|
||||
class GuiCanvasWxBackend():
|
||||
# {{{ arabicShapes{}
|
||||
arabicShapes = {
|
||||
u'\u0621': (u'\uFE80'),
|
||||
u'\u0622': (u'\uFE81', None, None, u'\uFE82'),
|
||||
u'\u0623': (u'\uFE83', None, None, u'\uFE84'),
|
||||
u'\u0624': (u'\uFE85', None, None, u'\uFE86'),
|
||||
u'\u0625': (u'\uFE87', None, None, u'\uFE88'),
|
||||
u'\u0626': (u'\uFE89', u'\uFE8B', u'\uFE8C', u'\uFE8A'),
|
||||
u'\u0627': (u'\uFE8D', None, None, u'\uFE8E'),
|
||||
u'\u0628': (u'\uFE8F', u'\uFE91', u'\uFE92', u'\uFE90'),
|
||||
u'\u0629': (u'\uFE93', None, None, u'\uFE94'),
|
||||
u'\u062A': (u'\uFE95', u'\uFE97', u'\uFE98', u'\uFE96'),
|
||||
u'\u062B': (u'\uFE99', u'\uFE9B', u'\uFE9C', u'\uFE9A'),
|
||||
u'\u062C': (u'\uFE9D', u'\uFE9F', u'\uFEA0', u'\uFE9E'),
|
||||
u'\u062D': (u'\uFEA1', u'\uFEA3', u'\uFEA4', u'\uFEA2'),
|
||||
u'\u062E': (u'\uFEA5', u'\uFEA7', u'\uFEA8', u'\uFEA6'),
|
||||
u'\u062F': (u'\uFEA9', None, None, u'\uFEAA'),
|
||||
u'\u0630': (u'\uFEAB', None, None, u'\uFEAC'),
|
||||
u'\u0631': (u'\uFEAD', None, None, u'\uFEAE'),
|
||||
u'\u0632': (u'\uFEAF', None, None, u'\uFEB0'),
|
||||
u'\u0633': (u'\uFEB1', u'\uFEB3', u'\uFEB4', u'\uFEB2'),
|
||||
u'\u0634': (u'\uFEB5', u'\uFEB7', u'\uFEB8', u'\uFEB6'),
|
||||
u'\u0635': (u'\uFEB9', u'\uFEBB', u'\uFEBC', u'\uFEBA'),
|
||||
u'\u0636': (u'\uFEBD', u'\uFEBF', u'\uFEC0', u'\uFEBE'),
|
||||
u'\u0637': (u'\uFEC1', u'\uFEC3', u'\uFEC4', u'\uFEC2'),
|
||||
u'\u0638': (u'\uFEC5', u'\uFEC7', u'\uFEC8', u'\uFEC6'),
|
||||
u'\u0639': (u'\uFEC9', u'\uFECB', u'\uFECC', u'\uFECA'),
|
||||
u'\u063A': (u'\uFECD', u'\uFECF', u'\uFED0', u'\uFECE'),
|
||||
u'\u0640': (u'\u0640', None, None, None),
|
||||
u'\u0641': (u'\uFED1', u'\uFED3', u'\uFED4', u'\uFED2'),
|
||||
u'\u0642': (u'\uFED5', u'\uFED7', u'\uFED8', u'\uFED6'),
|
||||
u'\u0643': (u'\uFED9', u'\uFEDB', u'\uFEDC', u'\uFEDA'),
|
||||
u'\u0644': (u'\uFEDD', u'\uFEDF', u'\uFEE0', u'\uFEDE'),
|
||||
u'\u0645': (u'\uFEE1', u'\uFEE3', u'\uFEE4', u'\uFEE2'),
|
||||
u'\u0646': (u'\uFEE5', u'\uFEE7', u'\uFEE8', u'\uFEE6'),
|
||||
u'\u0647': (u'\uFEE9', u'\uFEEB', u'\uFEEC', u'\uFEEA'),
|
||||
u'\u0648': (u'\uFEED', None, None, u'\uFEEE'),
|
||||
u'\u0649': (u'\uFEEF', None, None, u'\uFEF0'),
|
||||
u'\u064A': (u'\uFEF1', u'\uFEF3', u'\uFEF4', u'\uFEF2'),
|
||||
}
|
||||
# }}}
|
||||
# {{{ _CellState(): Cell state
|
||||
class _CellState():
|
||||
CS_NONE = 0x00
|
||||
@ -99,6 +140,40 @@ class GuiCanvasWxBackend():
|
||||
self._penAlpha = wx.Pen(wx.Colour(Colours[14][:4]), 1)
|
||||
self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None
|
||||
# }}}
|
||||
# {{{ _reshapeArabic(self, canvas, eventDc, patch, point)
|
||||
def _reshapeArabic(self, canvas, eventDc, patch, point):
|
||||
lastCell = point[0]
|
||||
while True:
|
||||
if ((lastCell + 1) >= (canvas.size[0] - 1)) \
|
||||
or (not canvas.map[point[1]][lastCell + 1][3] in self.arabicShapes):
|
||||
break
|
||||
else:
|
||||
lastCell += 1
|
||||
connect = False
|
||||
for runX in range(lastCell, point[0], -1):
|
||||
runCell = list(canvas.map[point[1]][runX])
|
||||
if runX == lastCell:
|
||||
if self.arabicShapes[runCell[3]][1] != None:
|
||||
runCell[3] = self.arabicShapes[runCell[3]][1]; connect = True;
|
||||
else:
|
||||
runCell[3] = self.arabicShapes[runCell[3]][0]; connect = False;
|
||||
else:
|
||||
if connect and (self.arabicShapes[runCell[3]][2] != None):
|
||||
runCell[3] = self.arabicShapes[runCell[3]][2]; connect = True;
|
||||
elif connect and (self.arabicShapes[runCell[3]][3] != None):
|
||||
runCell[3] = self.arabicShapes[runCell[3]][3]; connect = False;
|
||||
elif not connect and (self.arabicShapes[runCell[3]][1] != None):
|
||||
runCell[3] = self.arabicShapes[runCell[3]][1]; connect = True;
|
||||
else:
|
||||
runCell[3] = self.arabicShapes[runCell[3]][0]; connect = False;
|
||||
self._drawCharPatch(eventDc, runCell, [runX, point[1]])
|
||||
runCell = list(patch[2:])
|
||||
if connect and (self.arabicShapes[patch[5]][3] != None):
|
||||
runCell[3] = self.arabicShapes[patch[5]][3]
|
||||
else:
|
||||
runCell[3] = self.arabicShapes[patch[5]][0]
|
||||
self._drawCharPatch(eventDc, runCell, [point[0], point[1]])
|
||||
# }}}
|
||||
# {{{ _setBrushDc(self, brushBg, brushFg, dc, pen)
|
||||
def _setBrushDc(self, brushBg, brushFg, dc, pen):
|
||||
if self._lastBrushBg != brushBg:
|
||||
@ -113,12 +188,12 @@ class GuiCanvasWxBackend():
|
||||
return [a * b for a, b in zip(point, self.cellSize)]
|
||||
# }}}
|
||||
|
||||
# {{{ drawCursorMaskWithJournal(self, canvasJournal, eventDc, viewRect)
|
||||
def drawCursorMaskWithJournal(self, canvasJournal, eventDc, viewRect):
|
||||
[self.drawPatch(eventDc, patch, viewRect) for patch in canvasJournal.popCursor()]
|
||||
# {{{ drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc, viewRect)
|
||||
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc, viewRect):
|
||||
[self.drawPatch(canvas, eventDc, patch, viewRect) for patch in canvasJournal.popCursor()]
|
||||
# }}}
|
||||
# {{{ drawPatch(self, eventDc, patch, viewRect)
|
||||
def drawPatch(self, eventDc, patch, viewRect):
|
||||
# {{{ drawPatch(self, canvas, eventDc, patch, viewRect)
|
||||
def drawPatch(self, canvas, eventDc, patch, viewRect):
|
||||
point = [m - n for m, n in zip(patch[:2], viewRect)]
|
||||
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
|
||||
if patch[5] == " ":
|
||||
@ -128,6 +203,8 @@ class GuiCanvasWxBackend():
|
||||
self._drawCharPatch(eventDc, patch[2:], point)
|
||||
else:
|
||||
self._drawBrushPatch(eventDc, patch[2:], point)
|
||||
elif patch[5] in self.arabicShapes:
|
||||
self._reshapeArabic(canvas, eventDc, patch, point)
|
||||
else:
|
||||
self._drawCharPatch(eventDc, patch[2:], point)
|
||||
return True
|
||||
|
@ -10,10 +10,10 @@ from GuiWindow import GuiWindow
|
||||
import json, os, sys, wx
|
||||
|
||||
class RoarAssetsWindow(GuiMiniFrame):
|
||||
# {{{ _drawPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
def _drawPatch(self, eventDc, isCursor, patch, viewRect):
|
||||
# {{{ _drawPatch(self, canvas, eventDc, isCursor, patch, viewRect)
|
||||
def _drawPatch(self, canvas, eventDc, isCursor, patch, viewRect):
|
||||
if not isCursor:
|
||||
self.backend.drawPatch(eventDc, patch, viewRect)
|
||||
self.backend.drawPatch(canvas, eventDc, patch, viewRect)
|
||||
# }}}
|
||||
# {{{ _import(self, f, pathName)
|
||||
def _import(self, f, pathName):
|
||||
@ -113,7 +113,7 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect)
|
||||
for numRow in range(canvas.size[1]):
|
||||
for numCol in range(canvas.size[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
self.backend.drawPatch(canvas, eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
# }}}
|
||||
# {{{ onPaint(self, event)
|
||||
def onPaint(self, event):
|
||||
@ -153,11 +153,11 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
if deltaSize[0] > 0:
|
||||
for numRow in range(oldSize[1]):
|
||||
for numNewCol in range(oldSize[0], newSize[0]):
|
||||
self._drawPatch(eventDc, False, [numNewCol, numRow, 1, 1, 0, " "], viewRect)
|
||||
self._drawPatch(canvas, eventDc, False, [numNewCol, numRow, 1, 1, 0, " "], viewRect)
|
||||
if deltaSize[1] > 1:
|
||||
for numNewRow in range(oldSize[1], newSize[1]):
|
||||
for numNewCol in range(newSize[0]):
|
||||
self._drawPatch(eventDc, False, [numNewCol, numNewRow, 1, 1, 0, " "], viewRect)
|
||||
self._drawPatch(canvas, eventDc, False, [numNewCol, numNewRow, 1, 1, 0, " "], viewRect)
|
||||
# }}}
|
||||
# {{{ update(self, canvas, newSize, newCanvas=None)
|
||||
def update(self, canvas, newSize, newCanvas=None):
|
||||
@ -167,7 +167,7 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect)
|
||||
for numRow in range(canvas.size[1]):
|
||||
for numCol in range(canvas.size[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
self.backend.drawPatch(canvas, eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
# }}}
|
||||
|
||||
# {{{ onImportAnsi(self, event)
|
||||
|
@ -47,9 +47,9 @@ class RoarCanvasWindow(GuiWindow):
|
||||
# {{{ _drawPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
def _drawPatch(self, eventDc, isCursor, patch, viewRect):
|
||||
if not self.canvas.dirtyCursor:
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect)
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc, viewRect)
|
||||
self.canvas.dirtyCursor = True
|
||||
if self.backend.drawPatch(eventDc, patch, viewRect) and isCursor:
|
||||
if self.backend.drawPatch(self.canvas, eventDc, patch, viewRect) and isCursor:
|
||||
patchDeltaCell = self.canvas.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
|
||||
self.canvas.journal.pushCursor(patchDelta)
|
||||
# }}}
|
||||
@ -103,7 +103,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
viewRect = self.GetViewStart()
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect)
|
||||
if self.canvas.dirtyCursor:
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect)
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc, viewRect)
|
||||
self.canvas.dirtyCursor = False
|
||||
for patch in deltaPatches:
|
||||
if patch == None:
|
||||
@ -111,7 +111,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
elif patch[0] == "resize":
|
||||
del eventDc; self.resize(patch[1:], False); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart());
|
||||
else:
|
||||
self.canvas._commitPatch(patch); self.backend.drawPatch(eventDc, patch, self.GetViewStart());
|
||||
self.canvas._commitPatch(patch); self.backend.drawPatch(self.canvas, eventDc, patch, self.GetViewStart());
|
||||
# }}}
|
||||
# {{{ dispatchPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
def dispatchPatch(self, eventDc, isCursor, patch, viewRect):
|
||||
@ -148,7 +148,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
|
||||
for numRow in range(newSize[1]):
|
||||
for numCol in range(newSize[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]], self.GetViewStart())
|
||||
self.backend.drawPatch(self.canvas, eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]], self.GetViewStart())
|
||||
# }}}
|
||||
|
||||
# {{{ onKeyboardInput(self, event)
|
||||
@ -193,7 +193,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
def onLeaveWindow(self, event):
|
||||
if False:
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, self.GetViewStart())
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc, self.GetViewStart())
|
||||
self.lastCellState = None
|
||||
# }}}
|
||||
# {{{ onMouseInput(self, event)
|
||||
@ -228,7 +228,7 @@ class RoarCanvasWindow(GuiWindow):
|
||||
def onPaint(self, event):
|
||||
viewRect = self.GetViewStart()
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect)
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect)
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc, viewRect)
|
||||
self.backend.onPaint(self.GetClientSize(), self, self.GetViewStart())
|
||||
# }}}
|
||||
|
||||
|
@ -5,12 +5,42 @@
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
import re, string, wx
|
||||
import re, string, time, wx
|
||||
|
||||
class ToolText(Tool):
|
||||
name = "Text"
|
||||
arabicRegEx = r'^[\u0621-\u063A\u0640-\u064A]+$'
|
||||
rtlRegEx = r'^[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]+$'
|
||||
|
||||
# {{{ _checkRtl(self, canvas, brushPos, keyChar)
|
||||
def _checkRtl(self, canvas, brushPos, keyChar):
|
||||
rtlFlag = False
|
||||
if (keyChar != None) and re.match(self.rtlRegEx, keyChar):
|
||||
rtlFlag = True
|
||||
else:
|
||||
lastX, lastY = brushPos[0], brushPos[1]
|
||||
while True:
|
||||
if canvas.map[lastY][lastX][3] == " ":
|
||||
if (lastX + 1) >= canvas.size[0]:
|
||||
if lastY == 0:
|
||||
break
|
||||
else:
|
||||
lastX, lastY = 0, lastY - 1
|
||||
else:
|
||||
lastX += 1
|
||||
elif re.match(self.arabicRegEx, canvas.map[lastY][lastX][3]):
|
||||
rtlFlag = True
|
||||
if (lastX + 1) >= canvas.size[0]:
|
||||
if lastY == 0:
|
||||
break
|
||||
else:
|
||||
lastX, lastY = 0, lastY - 1
|
||||
else:
|
||||
lastX += 1
|
||||
else:
|
||||
break
|
||||
return rtlFlag
|
||||
# }}}
|
||||
# {{{ _processKeyChar(self, brushColours, brushPos, canvas, dispatchFn, eventDc, keyChar, keyModifiers, viewRect)
|
||||
def _processKeyChar(self, brushColours, brushPos, canvas, dispatchFn, eventDc, keyChar, keyModifiers, viewRect):
|
||||
if (ord(keyChar) != wx.WXK_NONE) \
|
||||
@ -18,7 +48,7 @@ class ToolText(Tool):
|
||||
and ((ord(keyChar) >= 32) if ord(keyChar) < 127 else True) \
|
||||
and (keyModifiers in (wx.MOD_NONE, wx.MOD_SHIFT)):
|
||||
dispatchFn(eventDc, False, [*brushPos, *brushColours, 0, keyChar], viewRect);
|
||||
if not re.match(self.rtlRegEx, keyChar):
|
||||
if not self._checkRtl(canvas, brushPos, keyChar):
|
||||
if brushPos[0] < (canvas.size[0] - 1):
|
||||
brushPos[0] += 1
|
||||
elif brushPos[1] < (canvas.size[1] - 1):
|
||||
@ -56,13 +86,29 @@ class ToolText(Tool):
|
||||
else:
|
||||
rc, error = False, "Clipboard does not contain text data and/or cannot be opened"
|
||||
elif keyCode == wx.WXK_BACK:
|
||||
if brushPos[0] > 0:
|
||||
brushPos[0] -= 1
|
||||
elif brushPos[1] > 0:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, brushPos[1] - 1
|
||||
if ((brushPos[0] + 1) >= canvas.size[0]):
|
||||
if brushPos[1] > 0:
|
||||
lastBrushPos = [0, brushPos[1] - 1]
|
||||
else:
|
||||
lastBrushPos = [0, 0]
|
||||
else:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, canvas.size[1] - 1
|
||||
rc, dirty = True, False; dispatchFn(eventDc, True, [*brushPos, *brushColours, 0, "_"], viewRect);
|
||||
lastBrushPos = [brushPos[0] + 1, brushPos[1]]
|
||||
if not self._checkRtl(canvas, lastBrushPos, None):
|
||||
if brushPos[0] > 0:
|
||||
brushPos[0] -= 1
|
||||
elif brushPos[1] > 0:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, brushPos[1] - 1
|
||||
else:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, canvas.size[1] - 1
|
||||
else:
|
||||
if brushPos[0] < (canvas.size[0] - 1):
|
||||
brushPos[0] += 1
|
||||
elif brushPos[1] > 0:
|
||||
brushPos[0], brushPos[1] = 0, brushPos[1] - 1
|
||||
else:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, 0
|
||||
rc, dirty = True, False; dispatchFn(eventDc, False, [*brushPos, *brushColours, 0, " "], viewRect);
|
||||
dispatchFn(eventDc, True, [*brushPos, *brushColours, 0, "_"], viewRect);
|
||||
elif keyCode == wx.WXK_RETURN:
|
||||
if brushPos[1] < (canvas.size[1] - 1):
|
||||
brushPos[0], brushPos[1] = 0, brushPos[1] + 1
|
||||
|
Loading…
Reference in New Issue
Block a user