import
sfml, sfml_colors,
input_helpers, sg_packets
from strutils import countlines
{.deadCodeElim: on.}
type
PGuiContainer* = ref TGuiContainer
TGuiContainer* = object of TObject
position: TVector2f
activeEntry: PTextEntry
widgets: seq[PGuiObject]
buttons: seq[PButton]
PGuiObject* = ref TGuiObject
TGuiObject* = object of TObject
PButton* = ref TButton
TButton* = object of TGuiObject
enabled: bool
bg*: sfml.PRectangleShape
text*: PText
onClick*: TButtonClicked
bounds: TFloatRect
PButtonCollection* = ref TButtonCollection
TButtonCollection* = object of TGuiContainer
PTextEntry* = ref TTextEntry
TTextEntry* = object of TButton
inputClient: input_helpers.PTextInput
PMessageArea* = ref TMessageArea
TMessageArea* = object of TGuiObject
pos: TVector2f
messages: seq[TMessage]
texts: seq[PText]
scrollBack*: int
sizeVisible*: int
direction*: int
TMessage = object
color: TColor
text: string
lines: int
TButtonClicked = proc(button: PButton)
var
guiFont* = newFont("data/fnt/LiberationMono-Regular.ttf")
messageProto* = newText("", guiFont, 16)
let
vectorZeroF* = vec2f(0.0, 0.0)
proc newGuiContainer*(): PGuiContainer
proc newGuiContainer*(pos: TVector2f): PGuiContainer {.inline.}
proc free*(container: PGuiContainer)
proc add*(container: PGuiContainer; widget: PGuiObject)
proc clearButtons*(container: PGuiContainer)
proc click*(container: PGuiContainer; position: TVector2f)
proc setActive*(container: PGuiContainer; entry: PTextEntry)
proc setPosition*(container: PGuiContainer; position: TVector2f)
proc update*(container: PGuiContainer; dt: float)
proc draw*(window: PRenderWindow; container: PGuiContainer) {.inline.}
proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea {.discardable.}
proc add*(m: PMessageArea; msg: ScChat)
proc draw*(window: PRenderWindow; b: PButton) {.inline.}
proc click*(b: PButton; p: TVector2f)
proc setPosition*(b: PButton; p: TVector2f)
proc setString*(b: PButton; s: string) {.inline.}
proc newButton*(container: PGuiContainer; text: string; position: TVector2f;
onClick: TButtonClicked; startEnabled: bool = true): PButton {.discardable.}
proc init(b: PButton; text: string; position: TVector2f; onClick: TButtonClicked)
proc setEnabled*(b: PButton; enabled: bool)
proc disable*(b: PButton) {.inline.}
proc enable*(b: PButton) {.inline.}
proc newTextEntry*(container: PGuiContainer; text: string;
position: TVector2f; onEnter: TInputFinishedProc = nil): PTextEntry {.discardable.}
proc init(t: PTextEntry; text: string; onEnter: TInputFinishedProc)
proc draw*(window: PRenderWindow, t: PTextEntry) {.inline.}
proc setActive*(t: PTextEntry) {.inline.}
proc clearText*(t: PTextEntry) {.inline.}
proc getText*(t: PTextEntry): string {.inline.}
proc update*(m: PMessageArea)
if guiFont == nil:
echo("Could not load font, crying softly to myself.")
quit(1)
proc newGuiContainer*(): PGuiContainer =
new(result, free)
result.widgets = @[]
result.buttons = @[]
proc newGuiContainer*(pos: TVector2f): PGuiContainer =
result = newGuiContainer()
result.setPosition pos
proc free*(container: PGuiContainer) =
container.widgets = nil
container.buttons = nil
proc add*(container: PGuiContainer; widget: PGuiObject) =
container.widgets.add(widget)
proc add*(container: PGuiContainer; button: PButton) =
if container.isNil: return
container.buttons.add(button)
proc clearButtons*(container: PGuiContainer) =
container.buttons.setLen 0
proc click*(container: PGuiContainer; position: TVector2f) =
for b in container.buttons:
click(b, position)
proc setActive*(container: PGuiContainer; entry: PTextEntry) =
container.activeEntry = entry
setActive(entry)
proc setPosition*(container: PGuiContainer; position: TVector2f) =
container.position = position
proc update*(container: PGuiContainer; dt: float) =
if not container.activeEntry.isNil:
container.activeEntry.setString(container.activeEntry.getText())
proc draw*(window: PRenderWindow; container: PGuiContainer) =
for b in container.buttons:
window.draw b
proc free(c: PButton) =
c.bg.destroy()
c.text.destroy()
c.bg = nil
c.text = nil
c.onClick = nil
proc newButton*(container: PGuiContainer; text: string;
position: TVector2f; onClick: TButtonClicked;
startEnabled: bool = true): PButton =
new(result, free)
init(result,
text,
if not container.isNil: position + container.position else: position,
onClick)
container.add result
if not startEnabled: disable(result)
proc init(b: PButton; text: string; position: TVector2f; onClick: TButtonClicked) =
b.bg = newRectangleShape()
b.bg.setSize(vec2f(80.0, 16.0))
b.bg.setFillColor(color(20, 30, 15))
b.text = newText(text, guiFont, 16)
b.onClick = onClick
b.setPosition(position)
b.enabled = true
proc copy*(c: PButton): PButton =
new(result, free)
result.bg = c.bg.copy()
result.text = c.text.copy()
result.onClick = c.onClick
result.setPosition(result.bg.getPosition())
proc setEnabled*(b: PButton; enabled: bool) =
b.enabled = enabled
if enabled:
b.text.setColor(White)
else:
b.text.setColor(Gray)
proc enable*(b: PButton) = setEnabled(b, true)
proc disable*(b: PButton) = setEnabled(b, false)
proc draw*(window: PRenderWindow; b: PButton) =
window.draw b.bg
window.draw b.text
proc setPosition*(b: PButton, p: TVector2f) =
b.bg.setPosition(p)
b.text.setPosition(p)
b.bounds = b.text.getGlobalBounds()
proc setString*(b: PButton; s: string) =
b.text.setString(s)
proc click*(b: PButton, p: TVector2f) =
if b.enabled and (addr b.bounds).contains(p.x, p.y):
b.onClick(b)
proc free(obj: PTextEntry) =
free(PButton(obj))
proc newTextEntry*(container: PGuiContainer; text: string;
position: TVector2F; onEnter: TInputFinishedProc = nil): PTextEntry =
new(result, free)
init(PButton(result), text, position + container.position, proc(b: PButton) =
setActive(container, PTextEntry(b)))
init(result, text, onEnter)
container.add result
proc init(t: PTextEntry; text: string; onEnter: TInputFinishedProc) =
t.inputClient = newTextInput(text, text.len, onEnter)
proc draw(window: PRenderWindow; t: PTextEntry) =
window.draw PButton(t)
proc clearText*(t: PTextEntry) =
t.inputClient.clear()
proc getText*(t: PTextEntry): string =
return t.inputClient.text
proc setActive*(t: PTextEntry) =
if not t.isNil and not t.inputClient.isNil:
input_helpers.setActive(t.inputClient)
discard """proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea =
new(result)
result.messages = @[]
result.pos = position
container.add(result)
proc add*(m: PMessageArea, text: string): PText =
result = messageProto.copy()
result.setString(text)
m.messages.add(result)
let nmsgs = len(m.messages)
var pos = vec2f(m.pos.x, m.pos.y)
for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
setPosition(m.messages[i], pos)
pos.y -= 16.0
proc draw*(window: PRenderWindow; m: PMessageArea) =
let nmsgs = len(m.messages)
if nmsgs == 0: return
for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
window.draw(m.messages[i])
"""
proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea =
new(result)
result.messages = @[]
result.texts = @[]
result.pos = position + container.position
result.sizeVisible = 10
result.scrollBack = 0
result.direction = -1 ## to push old messages up
container.add(result)
proc add*(m: PMessageArea, msg: ScChat) =
const prependName = {CPub, CPriv}
var mmm: TMessage
if msg.kind in prependName:
mmm.text = "<"
mmm.text.add msg.fromPlayer
mmm.text.add "> "
mmm.text.add msg.text
else:
mmm.text = msg.text
case msg.kind
of CPub: mmm.color = RoyalBlue
of CPriv, CSystem: mmm.color = Green
of CError: mmm.color = Red
mmm.lines = countLines(mmm.text)+1
m.messages.add mmm
update m
proc add*(m: PMessageArea, msg: string) {.inline.} =
var chat = newScChat(kind = CSystem, text = msg)
add(m, chat)
proc proctor*(m: PText; msg: ptr TMessage; pos: ptr TVector2f) =
m.setString msg.text
m.setColor msg.color
m.setPosition pos[]
proc update*(m: PMessageArea) =
if m.texts.len < m.sizeVisible:
echo "adding ", m.sizeVisible - m.texts.len, " fields"
for i in 1..m.sizeVisible - m.texts.len:
var t = messageProto.copy()
m.texts.add messageProto.copy()
elif m.texts.len > m.sizeVisible:
echo "cutting ", m.texts.len - m.sizeVisible, " fields"
for i in m.sizeVisible.. < m.texts.len:
m.texts.pop().destroy()
let nmsgs = m.messages.len()
if m.sizeVisible == 0 or nmsgs == 0:
echo "no messages? ", m.sizeVisible, ", ", nmsgs
return
var pos = vec2f(m.pos.x, m.pos.y)
for i in 0.. min(m.sizeVisible, nmsgs)-1:
##echo nmsgs - i - 1 - m.scrollBack
let msg = addr m.messages[nmsgs - i - 1 - m.scrollBack]
proctor(m.texts[i], msg, addr pos)
pos.y += (16 * m.direction * msg.lines).cfloat
proc draw*(window: PRenderWindow; m: PMessageArea) =
let nmsgs = len(m.texts)
if nmsgs == 0: return
for i in countdown(nmsgs - 1, max(nmsgs - m.sizeVisible, 0)):
window.draw m.texts[i]