Strategická: Upgrade game UI
This commit is contained in:
parent
e2153b6d03
commit
e46d612718
3 changed files with 171 additions and 84 deletions
|
@ -1,5 +1,5 @@
|
|||
from flask import Flask, redirect, flash, session, g, request, get_flashed_messages, Markup
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField, RadioField
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField, RadioField, FloatField
|
||||
from wtforms.validators import ValidationError
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_bootstrap import Bootstrap
|
||||
|
@ -45,12 +45,22 @@ class OccupyViewConfig(FlaskForm):
|
|||
refresh_meta = "Aktualizace pomocí HTML META"
|
||||
refresh_js = "Aktualizace pomocí java scriptu"
|
||||
|
||||
font_size = IntegerField("Velikost fontu", default=8)
|
||||
clickable_down = "Podrobnosti na kliknutí pod mapou"
|
||||
clickable_right = "Podrobnosti na kliknutí vpravo vedle mapy"
|
||||
clickable_not = "Vypnout podrobnosti kliknutím (zmenší nároky na sít a procesor)"
|
||||
|
||||
status_minimalistic = "Minimalistické metadata"
|
||||
status_up = "Matadata nahoře"
|
||||
status_left = "Metadata vlevo od mapy"
|
||||
|
||||
font_size = FloatField("Velikost fontu", default=8.0)
|
||||
refresh = RadioField("Aktualizace", choices=(refresh_none, refresh_meta), default=refresh_none)
|
||||
not_clickable = BooleanField("Vypnout podrobnosti kliknutím (zmenší nároky na sít a procesor)")
|
||||
clickable = RadioField("", choices=(clickable_not, clickable_down, clickable_right), default=clickable_down)
|
||||
status = RadioField("", choices=(status_minimalistic, status_left, status_up), default=status_minimalistic)
|
||||
big_fields = BooleanField("Široká políčka (trojciferné počty)")
|
||||
|
||||
submit = SubmitField("Refresh/Potvrdit")
|
||||
show_data_only = SubmitField("Zobrazit stránku pouze s daty")
|
||||
|
||||
@add_wlogic
|
||||
class Occupy(WLogic):
|
||||
|
@ -58,59 +68,94 @@ class Occupy(WLogic):
|
|||
meta_refresh_without_reaming = 15
|
||||
|
||||
game = state.game
|
||||
team_id = team.team_id if team else None
|
||||
if game.step_mode == db.StepMode.automatic:
|
||||
time_reaming = (state.create_time - datetime.now()).total_seconds() + game.step_every_s
|
||||
else:
|
||||
time_reaming = None
|
||||
s = state.get_state()
|
||||
if team is not None:
|
||||
s = self.logic.personalize_state(s, team.team_id, state.round)
|
||||
conff = OccupyViewConfig(formdata=request.args, csrf_enabled=False)
|
||||
q = db.get_session().query(db.Log).order_by(db.Log.log_id.desc())
|
||||
conff.validate()
|
||||
|
||||
max_num = 999 if conff.big_fields.data else 99
|
||||
|
||||
b = BasePage()
|
||||
b.h2(f"Hra {self.game.print()} kolo {state.round}")
|
||||
if not conff.not_clickable.data:
|
||||
b.p("Po kliknutí na buňku se zobrazí další informace.")
|
||||
if conff.refresh.data == conff.refresh_meta and time_reaming is None:
|
||||
b.p(f"Není známo, kdy nastane další kolo, proto může být aktualizace až o {meta_refresh_without_reaming} sekund opožděna.")
|
||||
with b.p():
|
||||
b.line().b(_class=f"game_team_{team.team_id}")(f"Pohled týmu {team.print()}")
|
||||
with b.line().b(_class="pull-right"):
|
||||
if time_reaming is not None:
|
||||
b("Čas do konce kola: ", b._span(id="time")(int(time_reaming)), " sekund")
|
||||
else:
|
||||
b("Konec kola není stanoven")
|
||||
with b.p(style="text-align:center;").table(_class="game_table"):
|
||||
for i, row in enumerate(s["world"]):
|
||||
with b.tr():
|
||||
for j, x in enumerate(row):
|
||||
occupied_by_team = x["occupied_by_team"]
|
||||
home_for_team = x["home_for_team"]
|
||||
members = x["members"]
|
||||
with b.td():
|
||||
classes = []
|
||||
with b.a(href=f"#cell_{i}_{j}") if not conff.not_clickable.data else b.bucket():
|
||||
if x["protected_for_team"] is not None:
|
||||
classes.append("game_protected")
|
||||
if x["hill"]:
|
||||
classes.append("game_hill")
|
||||
b(Markup(" "))
|
||||
else:
|
||||
if home_for_team is not None:
|
||||
classes.append(f'game_home')
|
||||
if occupied_by_team is not None:
|
||||
classes.append(f'game_occupied')
|
||||
classes.append(f'game_occupied_by_{occupied_by_team}')
|
||||
num = len(members)
|
||||
if num:
|
||||
b(num if num <= max_num else ("+++" if conff.big_fields.data else "++"))
|
||||
else:
|
||||
b(Markup(" "))
|
||||
b(_class=" ".join(classes))
|
||||
def reaming_time(id="time"):
|
||||
if time_reaming is not None:
|
||||
b.b("Čas do konce kola: ", b._span(id=id)(int(time_reaming)), " sekund")
|
||||
b.script(Markup(f"""
|
||||
var intervalID = window.setInterval(update_time, 100);
|
||||
var t = { time_reaming*1000 };
|
||||
function update_time() {{
|
||||
document.getElementById("{id}").innerHTML = Math.floor(t/1000);;
|
||||
t -= 100;
|
||||
}}
|
||||
"""))
|
||||
else:
|
||||
b("Konec kola není stanoven")
|
||||
|
||||
if not conff.not_clickable.data:
|
||||
def status():
|
||||
members_counts = [0 for _ in teams]
|
||||
occupied_counts = [0 for _ in teams]
|
||||
|
||||
for x in s["world"]:
|
||||
for y in x:
|
||||
occupied_by_team = y["occupied_by_team"]
|
||||
members = y["members"]
|
||||
if occupied_by_team is not None:
|
||||
occupied_counts[occupied_by_team] += 1
|
||||
members_counts[occupied_by_team] += len(members)
|
||||
|
||||
|
||||
with b.div(_class="game_left_status button form-frame"):
|
||||
b.b("Kolo ", state.round)
|
||||
with b.p():
|
||||
reaming_time(id="time_left")
|
||||
with b.p():
|
||||
for t, occupied_count, members_count in zip(teams, occupied_counts, members_counts):
|
||||
with b.line().span(_class=f"game_team_{t.team_id}"):
|
||||
b.b("Tým ", t.print())
|
||||
b.br()
|
||||
b("Bodů: ", t.get_move(state.round).points)
|
||||
b.br()
|
||||
b("Políček: ", occupied_count)
|
||||
b.br()
|
||||
b("Vojáků: ", members_count)
|
||||
b.br()
|
||||
|
||||
|
||||
def table():
|
||||
with b.table(_class="game_table"):
|
||||
for i, row in enumerate(s["world"]):
|
||||
with b.tr():
|
||||
for j, x in enumerate(row):
|
||||
occupied_by_team = x["occupied_by_team"]
|
||||
home_for_team = x["home_for_team"]
|
||||
members = x["members"]
|
||||
with b.td():
|
||||
classes = []
|
||||
with b.a(href=f"#cell_{i}_{j}") if conff.clickable.data != conff.clickable_not else b.bucket():
|
||||
if x["protected_for_team"] is not None:
|
||||
classes.append("game_protected")
|
||||
if x["hill"]:
|
||||
classes.append("game_hill")
|
||||
b(Markup(" "))
|
||||
else:
|
||||
if home_for_team is not None:
|
||||
classes.append(f'game_home')
|
||||
if occupied_by_team is not None:
|
||||
classes.append(f'game_occupied')
|
||||
classes.append(f'game_occupied_by_{occupied_by_team}')
|
||||
num = len(members)
|
||||
if num:
|
||||
b(num if num <= max_num else ("+++" if conff.big_fields.data else "++"))
|
||||
else:
|
||||
b(Markup(" "))
|
||||
b(_class=" ".join(classes))
|
||||
|
||||
def clickable():
|
||||
for i, row in enumerate(s["world"]):
|
||||
for j, x in enumerate(row):
|
||||
occupied_by_team = x["occupied_by_team"]
|
||||
|
@ -131,34 +176,60 @@ class Occupy(WLogic):
|
|||
b.p().b(f"Počet osob: {len(members)}")
|
||||
with b.ul():
|
||||
for m in members:
|
||||
b.li(_class=f"game_team_{home_for_team}")(f"Voják {m['id']} týmu {teams[m['team']].print()}")
|
||||
b.li(_class=f"game_team_{m['team']}")(f"Voják {m['id']} týmu {teams[m['team']].print()}")
|
||||
|
||||
b = BasePage()
|
||||
if not conff.show_data_only.data:
|
||||
b.h2(f"Hra {self.game.print()} kolo {state.round}")
|
||||
if conff.clickable.data != conff.clickable_not:
|
||||
b.p("Po kliknutí na buňku se zobrazí další informace.")
|
||||
if conff.refresh.data == conff.refresh_meta and time_reaming is None:
|
||||
b.p(f"Není známo, kdy nastane další kolo, proto může být aktualizace až o {meta_refresh_without_reaming} sekund opožděna.")
|
||||
with b.p():
|
||||
if team is not None:
|
||||
b.line().b(_class=f"game_team_{team.team_id}")(f"Pohled týmu {team.print()}")
|
||||
with b.line().b(_class="pull-right"):
|
||||
reaming_time()
|
||||
b.div(style="clear: both; margin-bottom: 1ex")("")
|
||||
with b.div().div(style="text-align:center;", id="game_main"):
|
||||
if conff.status.data == conff.status_left:
|
||||
status()
|
||||
table()
|
||||
if conff.clickable.data == conff.clickable_right:
|
||||
clickable()
|
||||
b.div(style="clear: both; margin-bottom: 1ex")("")
|
||||
|
||||
if conff.clickable.data == conff.clickable_down:
|
||||
clickable()
|
||||
|
||||
del conff.csrf_token
|
||||
b.p()
|
||||
b.div(_class="form-frame")(jinja_mac.quick_form(conff, form_type="horizontal", method="GET"))
|
||||
|
||||
b.script(Markup(f"""
|
||||
var intervalID = window.setInterval(update_time, 100);
|
||||
var t = { time_reaming*1000 };
|
||||
function update_time() {{
|
||||
document.getElementById("time").innerHTML = Math.floor(t/1000);;
|
||||
t -= 100;
|
||||
}}
|
||||
"""))
|
||||
if not conff.show_data_only.data:
|
||||
b.div(_class="form-frame")(jinja_mac.quick_form(conff, form_type="horizontal", method="GET"))
|
||||
|
||||
def head(x):
|
||||
style = ""
|
||||
page_w = float("inf") if conff.show_data_only.data else 1000
|
||||
refresh_time = time_reaming + 1 if time_reaming is not None else 15
|
||||
font_size = int(conff.font_size.data)
|
||||
box_size = int(font_size * 1.3 + 1)
|
||||
font_size = float(conff.font_size.data or 8.0)
|
||||
box_size = int(font_size * 1.5 + 1)
|
||||
left_w = 0
|
||||
if conff.status.data == conff.status_left:
|
||||
left_w = 150
|
||||
box_w = box_size
|
||||
if conff.big_fields.data:
|
||||
box_w *= 1.5
|
||||
table_w = len(s["world"][0]) * box_w
|
||||
margin_cmd = ""
|
||||
if table_w > 1000:
|
||||
margin_cmd = f"margin-left: {(1000-table_w) / 2}px;"
|
||||
main_w = table_w + left_w
|
||||
if conff.clickable.data == conff.clickable_right:
|
||||
right_w = 400
|
||||
main_w += right_w
|
||||
style += f".game_tab {{\n width: {right_w}px\n}}\n"
|
||||
if main_w > page_w:
|
||||
margin_cmd = f"margin-left: {(page_w-main_w) / 2}px;"
|
||||
if conff.refresh.data == conff.refresh_meta:
|
||||
b.meta(**{"HTTP-EQUIV": "refresh", "CONTENT": f"{refresh_time};{app.url_for(web_game_view.__name__, game_id=game.game_id, team_id=team.team_id, **request.args)}"})
|
||||
b.meta(**{"HTTP-EQUIV": "refresh", "CONTENT": f"{refresh_time};{app.url_for(web_game_view.__name__, game_id=game.game_id, team_id=team_id, **request.args)}"})
|
||||
b.link(rel="stylesheet", href=app.url_for('static', filename='occupy.css'), type='text/css', media="all")
|
||||
b.style(Markup(f"""
|
||||
table.game_table tr td {{
|
||||
|
@ -169,8 +240,15 @@ class Occupy(WLogic):
|
|||
}}
|
||||
table.game_table {{
|
||||
width: {table_w}px;
|
||||
{margin_cmd}
|
||||
}}
|
||||
#game_main {{
|
||||
{margin_cmd}
|
||||
width: {main_w}px;
|
||||
}}
|
||||
.game_left_status {{
|
||||
width: {left_w-20}px;
|
||||
}}
|
||||
{style}
|
||||
"""))
|
||||
|
||||
|
||||
|
@ -178,5 +256,6 @@ class Occupy(WLogic):
|
|||
limited_size=False,
|
||||
sticky_head=False,
|
||||
head=head,
|
||||
empty=conff.show_data_only.data
|
||||
)
|
||||
return b.print_file()
|
||||
|
|
|
@ -22,7 +22,7 @@ import hra.lib as lib
|
|||
|
||||
|
||||
@html.WrapAfterBuilder_decorator
|
||||
def BasePage(b, content, head=lambda x:None, limited_size=True, sticky_head=True):
|
||||
def BasePage(b, content, head=lambda x:None, limited_size=True, sticky_head=True, empty=False):
|
||||
b.current_tag = html.Tag(b, "html", [])
|
||||
b.root_tag = b.current_tag
|
||||
with b.head():
|
||||
|
@ -34,29 +34,32 @@ def BasePage(b, content, head=lambda x:None, limited_size=True, sticky_head=True
|
|||
b.link(rel="shortcut icon", href=app.url_for('static', filename='img/favicon.ico'))
|
||||
b(head)
|
||||
with b.body() as body:
|
||||
with b.header(_class=f"flavor-{config.WEB_FLAVOR}"):
|
||||
with b.div(_class="content content_limited" if limited_size else "content"):
|
||||
with b.a(href="/", title="Na hlavní stránku"):
|
||||
b.img(src=app.url_for('static', filename='hippo.png'), style="width: 60px;height: auto;", alt="KSP")
|
||||
b.h1()("Hra na soustředění KSP")
|
||||
with b.div(id="nav-wrapper", _class="nav-wrapper-sticky" if sticky_head else ""):
|
||||
with b.nav(id="main-menu", _class="content"):
|
||||
for item in g.menu:
|
||||
b.a(href=item.url)(item.name)
|
||||
if g.user:
|
||||
b.a(_class="right", href="/")(f"Přihlášen: {g.user.username}")
|
||||
b.a(_class="right", href="/logout")(f"Odhlásit")
|
||||
else:
|
||||
b.a(_class="right", href="/login")(f"Přihlásit")
|
||||
b.a(_class="right", href="/registration")(f"Registrovat")
|
||||
with b.main():
|
||||
messages = get_flashed_messages(with_categories=True)
|
||||
if messages:
|
||||
for category, message in messages:
|
||||
if category == "message":
|
||||
category = "warning"
|
||||
b.div(_class=f"alert alert-{category}", role="alert")(message)
|
||||
if empty:
|
||||
b(*content)
|
||||
else:
|
||||
with b.header(_class=f"flavor-{config.WEB_FLAVOR}"):
|
||||
with b.div(_class="content content_limited" if limited_size else "content"):
|
||||
with b.a(href="/", title="Na hlavní stránku"):
|
||||
b.img(src=app.url_for('static', filename='hippo.png'), style="width: 60px;height: auto;", alt="KSP")
|
||||
b.h1()("Hra na soustředění KSP")
|
||||
with b.div(id="nav-wrapper", _class="nav-wrapper-sticky" if sticky_head else ""):
|
||||
with b.nav(id="main-menu", _class="content"):
|
||||
for item in g.menu:
|
||||
b.a(href=item.url)(item.name)
|
||||
if g.user:
|
||||
b.a(_class="right", href="/")(f"Přihlášen: {g.user.username}")
|
||||
b.a(_class="right", href="/logout")(f"Odhlásit")
|
||||
else:
|
||||
b.a(_class="right", href="/login")(f"Přihlásit")
|
||||
b.a(_class="right", href="/registration")(f"Registrovat")
|
||||
with b.main():
|
||||
messages = get_flashed_messages(with_categories=True)
|
||||
if messages:
|
||||
for category, message in messages:
|
||||
if category == "message":
|
||||
category = "warning"
|
||||
b.div(_class=f"alert alert-{category}", role="alert")(message)
|
||||
b(*content)
|
||||
|
||||
|
||||
class OptionalIntField(wtforms.IntegerField):
|
||||
|
@ -310,6 +313,7 @@ def web_game_step(game_id):
|
|||
|
||||
return redirect(app.url_for(web_game.__name__, game_id=game_id))
|
||||
|
||||
@app.route("/game/<int:game_id>/view", methods=['GET'])
|
||||
@app.route("/game/<int:game_id>/view/<int:team_id>", methods=['GET'])
|
||||
def web_game_view(game_id, team_id=None):
|
||||
ses = db.get_session()
|
||||
|
|
|
@ -136,3 +136,7 @@ table.game_table tr td a, table.game_table tr td a:hover, table.game_table tr td
|
|||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#game_main div, #game_main table {
|
||||
float: left;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue