Rework target profile editor input boxes for better editing experience
This commit is contained in:
@@ -27,7 +27,7 @@ from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenu
|
||||
from service.const import GraphCacheCleanupReason
|
||||
from service.fit import Fit
|
||||
from .input import ConstantBox, RangeBox
|
||||
from gui.utils.inputs import FloatBox, FloatRangeBox
|
||||
from .lists import FitList, TargetList
|
||||
from .vector import VectorPicker
|
||||
|
||||
@@ -213,9 +213,9 @@ class GraphControlPanel(wx.Panel):
|
||||
handledHandles.add(inputDef.handle)
|
||||
fieldSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
if mainInput:
|
||||
fieldTextBox = RangeBox(self, self._storedRanges.get((inputDef.handle, inputDef.unit), inputDef.defaultRange))
|
||||
fieldTextBox = FloatRangeBox(self, self._storedRanges.get((inputDef.handle, inputDef.unit), inputDef.defaultRange))
|
||||
else:
|
||||
fieldTextBox = ConstantBox(self, self._storedConsts.get((inputDef.handle, inputDef.unit), inputDef.defaultValue))
|
||||
fieldTextBox = FloatBox(self, self._storedConsts.get((inputDef.handle, inputDef.unit), inputDef.defaultValue))
|
||||
fieldTextBox.Bind(wx.EVT_TEXT, self.OnFieldChanged)
|
||||
fieldSizer.Add(fieldTextBox, 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
|
||||
fieldIcon = None
|
||||
|
||||
@@ -57,7 +57,7 @@ from gui.marketBrowser import MarketBrowser
|
||||
from gui.multiSwitch import MultiSwitch
|
||||
from gui.patternEditor import DmgPatternEditorDlg
|
||||
from gui.preferenceDialog import PreferenceDialog
|
||||
from gui.targetProfileEditor import ResistsEditorDlg
|
||||
from gui.targetProfileEditor import TargetProfileEditorDlg
|
||||
from gui.setEditor import ImplantSetEditorDlg
|
||||
from gui.shipBrowser import ShipBrowser
|
||||
from gui.statsPane import StatsPane
|
||||
@@ -398,7 +398,7 @@ class MainFrame(wx.Frame):
|
||||
dlg.Show()
|
||||
|
||||
def showTargetProfileEditor(self, event):
|
||||
ResistsEditorDlg(self)
|
||||
TargetProfileEditorDlg(self)
|
||||
|
||||
def showDamagePatternEditor(self, event):
|
||||
dlg = DmgPatternEditorDlg(self)
|
||||
|
||||
@@ -30,6 +30,7 @@ import gui.globalEvents as GE
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
|
||||
from gui.utils.clipboard import toClipboard, fromClipboard
|
||||
from gui.utils.inputs import FloatBox, InputValidator, strToFloat
|
||||
from service.fit import Fit
|
||||
from service.targetProfile import TargetProfile
|
||||
|
||||
@@ -37,13 +38,26 @@ from service.targetProfile import TargetProfile
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class TargetProfileTextValidator(BaseValidator):
|
||||
class ResistValidator(InputValidator):
|
||||
|
||||
def _validateWithReason(self, value):
|
||||
if not value:
|
||||
return True, ''
|
||||
value = strToFloat(value)
|
||||
if value is None:
|
||||
return False, 'Incorrect formatting (decimals only)'
|
||||
if value < 0 or value > 100:
|
||||
return False, 'Incorrect range (must be 0-100)'
|
||||
return True, ''
|
||||
|
||||
|
||||
class TargetProfileNameValidator(BaseValidator):
|
||||
|
||||
def __init__(self):
|
||||
BaseValidator.__init__(self)
|
||||
|
||||
def Clone(self):
|
||||
return TargetProfileTextValidator()
|
||||
return TargetProfileNameValidator()
|
||||
|
||||
def Validate(self, win):
|
||||
entityEditor = win.parent
|
||||
@@ -68,7 +82,7 @@ class TargetProfileEntityEditor(EntityEditor):
|
||||
|
||||
def __init__(self, parent):
|
||||
EntityEditor.__init__(self, parent, "Target Profile")
|
||||
self.SetEditorValidator(TargetProfileTextValidator)
|
||||
self.SetEditorValidator(TargetProfileNameValidator)
|
||||
|
||||
def getEntitiesFromContext(self):
|
||||
sTR = TargetProfile.getInstance()
|
||||
@@ -94,7 +108,7 @@ class TargetProfileEntityEditor(EntityEditor):
|
||||
sTR.deletePattern(entity)
|
||||
|
||||
|
||||
class ResistsEditorDlg(wx.Dialog):
|
||||
class TargetProfileEditorDlg(wx.Dialog):
|
||||
DAMAGE_TYPES = OrderedDict([
|
||||
("em", "EM resistance"),
|
||||
("thermal", "Thermal resistance"),
|
||||
@@ -146,11 +160,11 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
bmp.SetToolTip(wx.ToolTip(ttText))
|
||||
resistEditSizer.Add(bmp, 0, style, border)
|
||||
# set text edit
|
||||
setattr(self, "%sEdit" % type_, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize))
|
||||
editObj = getattr(self, "%sEdit" % type_)
|
||||
editObj.SetToolTip(wx.ToolTip(ttText))
|
||||
editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated)
|
||||
resistEditSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
setattr(self, "%sEdit" % type_, FloatBox(parent=self, id=wx.ID_ANY, value=None, pos=wx.DefaultPosition, size=defSize, validator=ResistValidator()))
|
||||
editBox = getattr(self, "%sEdit" % type_)
|
||||
editBox.SetToolTip(wx.ToolTip(ttText))
|
||||
self.Bind(event=wx.EVT_TEXT, handler=self.OnFieldChanged, source=editBox)
|
||||
resistEditSizer.Add(editBox, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
unit = wx.StaticText(self, wx.ID_ANY, "%", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
unit.SetToolTip(wx.ToolTip(ttText))
|
||||
resistEditSizer.Add(unit, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
@@ -171,20 +185,17 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
bmp.SetToolTip(wx.ToolTip(ttText))
|
||||
miscAttrSizer.Add(bmp, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 5)
|
||||
# set text edit
|
||||
setattr(self, "%sEdit" % attr, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize))
|
||||
editObj = getattr(self, "%sEdit" % attr)
|
||||
editObj.SetToolTip(wx.ToolTip(ttText))
|
||||
editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated)
|
||||
miscAttrSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
setattr(self, "%sEdit" % attr, FloatBox(parent=self, id=wx.ID_ANY, value=None, pos=wx.DefaultPosition, size=defSize))
|
||||
editBox = getattr(self, "%sEdit" % attr)
|
||||
editBox.SetToolTip(wx.ToolTip(ttText))
|
||||
self.Bind(event=wx.EVT_TEXT, handler=self.OnFieldChanged, source=editBox)
|
||||
miscAttrSizer.Add(editBox, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
unit = wx.StaticText(self, wx.ID_ANY, unitText, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
unit.SetToolTip(wx.ToolTip(ttText))
|
||||
miscAttrSizer.Add(unit, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
contentSizer.Add(miscAttrSizer, 1, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
# Color we use to reset invalid value color
|
||||
self.colorReset = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
|
||||
|
||||
self.slfooter = wx.StaticLine(self)
|
||||
contentSizer.Add(self.slfooter, 0, wx.EXPAND | wx.TOP, 5)
|
||||
|
||||
@@ -235,58 +246,47 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
self.Bind(wx.EVT_CLOSE, self.onClose)
|
||||
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
|
||||
|
||||
self.inputTimer = wx.Timer(self)
|
||||
self.Bind(wx.EVT_TIMER, self.OnInputTimer, self.inputTimer)
|
||||
|
||||
self.patternChanged()
|
||||
|
||||
self.ShowModal()
|
||||
|
||||
def ValuesUpdated(self, event=None):
|
||||
"""
|
||||
Event that is fired when resists values change. Iterates through all
|
||||
resist edit fields. If blank, sets it to 0.0. If it is not a proper
|
||||
decimal value, sets text color to red and refuses to save changes until
|
||||
issue is resolved
|
||||
"""
|
||||
def OnFieldChanged(self, event=None):
|
||||
if event is not None:
|
||||
event.Skip()
|
||||
self.inputTimer.Stop()
|
||||
self.inputTimer.Start(Fit.getInstance().serviceFittingOptions['marketSearchDelay'], True)
|
||||
|
||||
def OnInputTimer(self, event):
|
||||
event.Skip()
|
||||
|
||||
if self.block:
|
||||
return
|
||||
|
||||
editObj = None
|
||||
|
||||
try:
|
||||
p = self.entityEditor.getActiveEntity()
|
||||
|
||||
for type_ in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit" % type_)
|
||||
editBox = getattr(self, "%sEdit" % type_)
|
||||
# Raise exception if value is not valid
|
||||
if not editBox.isValid():
|
||||
reason = editBox.getInvalidationReason()
|
||||
raise ValueError(reason)
|
||||
|
||||
if editObj.GetValue() == "":
|
||||
# if we are blank, overwrite with 0
|
||||
editObj.ChangeValue("0.0")
|
||||
editObj.SetInsertionPointEnd()
|
||||
|
||||
value = float(editObj.GetValue())
|
||||
|
||||
# assertion, because they're easy
|
||||
assert 0 <= value <= 100
|
||||
|
||||
# if everything checks out, set resist attribute
|
||||
value = editBox.GetValueFloat() or 0
|
||||
setattr(p, "%sAmount" % type_, value / 100)
|
||||
editObj.SetForegroundColour(self.colorReset)
|
||||
|
||||
for attr in self.ATTRIBUTES:
|
||||
editObj = getattr(self, "%sEdit" % attr)
|
||||
editBox = getattr(self, "%sEdit" % attr)
|
||||
# Raise exception if value is not valid
|
||||
if not editBox.isValid():
|
||||
reason = editBox.getInvalidationReason()
|
||||
raise ValueError(reason)
|
||||
|
||||
if editObj.GetValue() == "" and attr != "signatureRadius":
|
||||
# if we are blank, overwrite with 0 except for signatureRadius
|
||||
editObj.ChangeValue("0.0")
|
||||
editObj.SetInsertionPointEnd()
|
||||
|
||||
# if everything checks out, set attribute
|
||||
value = editObj.GetValue()
|
||||
if value == '':
|
||||
value = None
|
||||
else:
|
||||
value = float(value)
|
||||
value = editBox.GetValueFloat()
|
||||
setattr(p, attr, value)
|
||||
editObj.SetForegroundColour(self.colorReset)
|
||||
|
||||
self.stNotice.SetLabel("")
|
||||
self.totSizer.Layout()
|
||||
@@ -297,16 +297,8 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
TargetProfile.getInstance().saveChanges(p)
|
||||
wx.PostEvent(self.mainFrame, GE.TargetProfileChanged(profileID=p.ID))
|
||||
|
||||
except ValueError:
|
||||
editObj.SetForegroundColour(wx.RED)
|
||||
msg = "Incorrect Formatting (decimals only)"
|
||||
pyfalog.warning(msg)
|
||||
self.stNotice.SetLabel(msg)
|
||||
except AssertionError:
|
||||
editObj.SetForegroundColour(wx.RED)
|
||||
msg = "Incorrect Range (must be 0-100)"
|
||||
pyfalog.warning(msg)
|
||||
self.stNotice.SetLabel(msg)
|
||||
except ValueError as e:
|
||||
self.stNotice.SetLabel(e.args[0])
|
||||
finally: # Refresh for color changes to take effect immediately
|
||||
self.Refresh()
|
||||
|
||||
@@ -326,18 +318,18 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
for field in self.DAMAGE_TYPES:
|
||||
edit = getattr(self, "%sEdit" % field)
|
||||
amount = getattr(p, "%sAmount" % field) * 100
|
||||
edit.ChangeValue(str(amount))
|
||||
edit.ChangeValueFloat(amount)
|
||||
|
||||
for attr in self.ATTRIBUTES:
|
||||
edit = getattr(self, "%sEdit" % attr)
|
||||
amount = getattr(p, attr)
|
||||
if amount == math.inf:
|
||||
edit.ChangeValue('')
|
||||
edit.ChangeValueFloat(None)
|
||||
else:
|
||||
edit.ChangeValue(str(amount))
|
||||
edit.ChangeValueFloat(amount)
|
||||
|
||||
self.block = False
|
||||
self.ValuesUpdated()
|
||||
self.OnFieldChanged()
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
import re
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import wx
|
||||
|
||||
@@ -38,18 +39,51 @@ def strToFloat(val):
|
||||
return None
|
||||
|
||||
|
||||
class ConstantBox(wx.TextCtrl):
|
||||
class InputValidator(metaclass=ABCMeta):
|
||||
|
||||
def __init__(self, parent, initial):
|
||||
super().__init__(parent, wx.ID_ANY, style=0)
|
||||
def validate(self, value):
|
||||
return self._validateWithReason(value)[0]
|
||||
|
||||
def getReason(self, value):
|
||||
return self._validateWithReason(value)[1]
|
||||
|
||||
@abstractmethod
|
||||
def _validateWithReason(self, value):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class FloatBox(wx.TextCtrl):
|
||||
|
||||
def __init__(self, parent, value, id=wx.ID_ANY, style=0, validator=None, **kwargs):
|
||||
super().__init__(parent=parent, id=id, style=style, **kwargs)
|
||||
self.Bind(wx.EVT_TEXT, self.OnText)
|
||||
self._storedValue = ''
|
||||
self.ChangeValue(valToStr(initial))
|
||||
|
||||
self._validator = validator
|
||||
self.ChangeValue(valToStr(value))
|
||||
|
||||
def ChangeValue(self, value):
|
||||
self._storedValue = value
|
||||
super().ChangeValue(value)
|
||||
self.updateColor()
|
||||
|
||||
def ChangeValueFloat(self, value):
|
||||
self.ChangeValue(valToStr(value))
|
||||
|
||||
def updateColor(self):
|
||||
if self.isValid():
|
||||
self.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
|
||||
else:
|
||||
self.SetForegroundColour(wx.RED)
|
||||
|
||||
def isValid(self):
|
||||
if self._validator is None:
|
||||
return True
|
||||
return self._validator.validate(self.GetValue())
|
||||
|
||||
def getInvalidationReason(self):
|
||||
if self._validator is None:
|
||||
return None
|
||||
return self._validator.getReason(self.GetValue())
|
||||
|
||||
def OnText(self, event):
|
||||
currentValue = self.GetValue()
|
||||
@@ -58,6 +92,7 @@ class ConstantBox(wx.TextCtrl):
|
||||
return
|
||||
if currentValue == '' or re.match('^\d*\.?\d*$', currentValue):
|
||||
self._storedValue = currentValue
|
||||
self.updateColor()
|
||||
event.Skip()
|
||||
else:
|
||||
self.ChangeValue(self._storedValue)
|
||||
@@ -66,13 +101,13 @@ class ConstantBox(wx.TextCtrl):
|
||||
return strToFloat(self.GetValue())
|
||||
|
||||
|
||||
class RangeBox(wx.TextCtrl):
|
||||
class FloatRangeBox(wx.TextCtrl):
|
||||
|
||||
def __init__(self, parent, initRange):
|
||||
super().__init__(parent, wx.ID_ANY, style=0)
|
||||
def __init__(self, parent, value, id=wx.ID_ANY, style=0, **kwargs):
|
||||
super().__init__(parent=parent, id=id, style=style, **kwargs)
|
||||
self.Bind(wx.EVT_TEXT, self.OnText)
|
||||
self._storedValue = ''
|
||||
self.ChangeValue('{}-{}'.format(valToStr(min(initRange)), valToStr(max(initRange))))
|
||||
self.ChangeValue('{}-{}'.format(valToStr(min(value)), valToStr(max(value))))
|
||||
|
||||
def ChangeValue(self, value):
|
||||
self._storedValue = value
|
||||
Reference in New Issue
Block a user