mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
Line up the LTN left and top panels exactly, and make the background
solid
This commit is contained in:
parent
477783e430
commit
5b2d5c82ff
@ -39,67 +39,72 @@ impl BrowseNeighborhoods {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let left_panel = crate::common::left_panel_builder(Widget::col(vec![
|
let top_panel = crate::common::app_top_panel(ctx, app);
|
||||||
app.session.alt_proposals.to_widget(ctx, app),
|
let left_panel = crate::common::left_panel_builder(
|
||||||
"Click a neighborhood to edit filters".text_widget(ctx),
|
ctx,
|
||||||
Widget::row(vec![
|
&top_panel,
|
||||||
ctx.style()
|
|
||||||
.btn_outline
|
|
||||||
.text("Plan a route")
|
|
||||||
.hotkey(Key::R)
|
|
||||||
.build_def(ctx),
|
|
||||||
ctx.style()
|
|
||||||
.btn_outline
|
|
||||||
.text("Export to GeoJSON")
|
|
||||||
.build_def(ctx),
|
|
||||||
])
|
|
||||||
.section(ctx),
|
|
||||||
Line("Advanced").small_heading().into_widget(ctx),
|
|
||||||
Widget::col(vec![
|
Widget::col(vec![
|
||||||
|
app.session.alt_proposals.to_widget(ctx, app),
|
||||||
|
"Click a neighborhood to edit filters".text_widget(ctx),
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
"Draw neighborhoods:".text_widget(ctx).centered_vert(),
|
ctx.style()
|
||||||
|
.btn_outline
|
||||||
|
.text("Plan a route")
|
||||||
|
.hotkey(Key::R)
|
||||||
|
.build_def(ctx),
|
||||||
|
ctx.style()
|
||||||
|
.btn_outline
|
||||||
|
.text("Export to GeoJSON")
|
||||||
|
.build_def(ctx),
|
||||||
|
])
|
||||||
|
.section(ctx),
|
||||||
|
Line("Advanced").small_heading().into_widget(ctx),
|
||||||
|
Widget::col(vec![
|
||||||
|
Widget::row(vec![
|
||||||
|
"Draw neighborhoods:".text_widget(ctx).centered_vert(),
|
||||||
|
Widget::dropdown(
|
||||||
|
ctx,
|
||||||
|
"style",
|
||||||
|
app.session.draw_neighborhood_style,
|
||||||
|
vec![
|
||||||
|
Choice::new("simple", Style::SimpleColoring),
|
||||||
|
Choice::new("cells", Style::Cells),
|
||||||
|
Choice::new("quietness", Style::Quietness),
|
||||||
|
Choice::new("all rat-runs", Style::RatRuns),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
Toggle::checkbox(
|
||||||
|
ctx,
|
||||||
|
"highlight boundary roads",
|
||||||
|
Key::H,
|
||||||
|
app.session.highlight_boundary_roads,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
.section(ctx),
|
||||||
|
Widget::col(vec![
|
||||||
|
"Predict proposal impact".text_widget(ctx),
|
||||||
|
impact_widget(ctx, app),
|
||||||
|
])
|
||||||
|
.section(ctx),
|
||||||
|
Widget::col(vec![Widget::row(vec![
|
||||||
Widget::dropdown(
|
Widget::dropdown(
|
||||||
ctx,
|
ctx,
|
||||||
"style",
|
"heuristic",
|
||||||
app.session.draw_neighborhood_style,
|
app.session.heuristic,
|
||||||
vec![
|
Heuristic::choices(),
|
||||||
Choice::new("simple", Style::SimpleColoring),
|
|
||||||
Choice::new("cells", Style::Cells),
|
|
||||||
Choice::new("quietness", Style::Quietness),
|
|
||||||
Choice::new("all rat-runs", Style::RatRuns),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
]),
|
ctx.style()
|
||||||
Toggle::checkbox(
|
.btn_outline
|
||||||
ctx,
|
.text("Automatically place filters")
|
||||||
"highlight boundary roads",
|
.build_def(ctx),
|
||||||
Key::H,
|
])])
|
||||||
app.session.highlight_boundary_roads,
|
.section(ctx),
|
||||||
),
|
]),
|
||||||
])
|
)
|
||||||
.section(ctx),
|
|
||||||
Widget::col(vec![
|
|
||||||
"Predict proposal impact".text_widget(ctx),
|
|
||||||
impact_widget(ctx, app),
|
|
||||||
])
|
|
||||||
.section(ctx),
|
|
||||||
Widget::col(vec![Widget::row(vec![
|
|
||||||
Widget::dropdown(
|
|
||||||
ctx,
|
|
||||||
"heuristic",
|
|
||||||
app.session.heuristic,
|
|
||||||
Heuristic::choices(),
|
|
||||||
),
|
|
||||||
ctx.style()
|
|
||||||
.btn_outline
|
|
||||||
.text("Automatically place filters")
|
|
||||||
.build_def(ctx),
|
|
||||||
])])
|
|
||||||
.section(ctx),
|
|
||||||
]))
|
|
||||||
.build(ctx);
|
.build(ctx);
|
||||||
Box::new(BrowseNeighborhoods {
|
Box::new(BrowseNeighborhoods {
|
||||||
top_panel: crate::common::app_top_panel(ctx, app),
|
top_panel,
|
||||||
left_panel,
|
left_panel,
|
||||||
world,
|
world,
|
||||||
draw_over_roads,
|
draw_over_roads,
|
||||||
|
@ -12,13 +12,13 @@ pub fn app_top_panel(ctx: &mut EventCtx, app: &App) -> Panel {
|
|||||||
.small_heading()
|
.small_heading()
|
||||||
.into_widget(ctx)
|
.into_widget(ctx)
|
||||||
.centered_vert(),
|
.centered_vert(),
|
||||||
map_gui::tools::change_map_btn(ctx, app),
|
map_gui::tools::change_map_btn(ctx, app).centered_vert(),
|
||||||
ctx.style()
|
ctx.style()
|
||||||
.btn_plain
|
.btn_plain
|
||||||
.icon("system/assets/tools/search.svg")
|
.icon("system/assets/tools/search.svg")
|
||||||
.hotkey(lctrl(Key::F))
|
.hotkey(lctrl(Key::F))
|
||||||
.build_widget(ctx, "search")
|
.build_widget(ctx, "search")
|
||||||
.align_right(),
|
.centered_vert(),
|
||||||
]))
|
]))
|
||||||
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||||
.dims_width(PanelDims::ExactPercent(1.0))
|
.dims_width(PanelDims::ExactPercent(1.0))
|
||||||
@ -51,12 +51,14 @@ pub fn handle_top_panel(ctx: &mut EventCtx, app: &App, panel: &mut Panel) -> Opt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn left_panel_builder(contents: Widget) -> PanelBuilder {
|
pub fn left_panel_builder(ctx: &EventCtx, top_panel: &Panel, contents: Widget) -> PanelBuilder {
|
||||||
|
let top_height = top_panel.panel_dims().height;
|
||||||
Panel::new_builder(contents)
|
Panel::new_builder(contents)
|
||||||
// TODO Vertical alignment below top panel is brittle
|
|
||||||
.aligned(
|
.aligned(
|
||||||
HorizontalAlignment::Percent(0.0),
|
HorizontalAlignment::Percent(0.0),
|
||||||
VerticalAlignment::Percent(0.1),
|
VerticalAlignment::Below(top_height),
|
||||||
)
|
)
|
||||||
.dims_height(PanelDims::ExactPercent(0.9))
|
.dims_height(PanelDims::ExactPixels(
|
||||||
|
ctx.canvas.window_height - top_height,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ impl Viewer {
|
|||||||
.panel_builder(
|
.panel_builder(
|
||||||
ctx,
|
ctx,
|
||||||
app,
|
app,
|
||||||
|
&self.top_panel,
|
||||||
Widget::col(vec![
|
Widget::col(vec![
|
||||||
ctx.style()
|
ctx.style()
|
||||||
.btn_outline
|
.btn_outline
|
||||||
|
@ -46,7 +46,7 @@ impl ShowResults {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let left_panel = crate::common::left_panel_builder(Widget::col(vec![
|
let contents = Widget::col(vec![
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
Line("Impact prediction").small_heading().into_widget(ctx),
|
Line("Impact prediction").small_heading().into_widget(ctx),
|
||||||
ctx.style()
|
ctx.style()
|
||||||
@ -60,8 +60,9 @@ impl ShowResults {
|
|||||||
app.session.impact.filters.to_panel(ctx, app),
|
app.session.impact.filters.to_panel(ctx, app),
|
||||||
app.session.impact.compare_counts.get_panel_widget(ctx).named("compare counts"),
|
app.session.impact.compare_counts.get_panel_widget(ctx).named("compare counts"),
|
||||||
ctx.style().btn_outline.text("Save before/after counts to files").build_def(ctx),
|
ctx.style().btn_outline.text("Save before/after counts to files").build_def(ctx),
|
||||||
]))
|
]);
|
||||||
.build(ctx);
|
let top_panel = crate::common::app_top_panel(ctx, app);
|
||||||
|
let left_panel = crate::common::left_panel_builder(ctx, &top_panel, contents).build(ctx);
|
||||||
|
|
||||||
let mut batch = GeomBatch::new();
|
let mut batch = GeomBatch::new();
|
||||||
for (_, (block, color)) in app.session.partitioning.all_neighborhoods() {
|
for (_, (block, color)) in app.session.partitioning.all_neighborhoods() {
|
||||||
@ -69,7 +70,7 @@ impl ShowResults {
|
|||||||
}
|
}
|
||||||
let draw_all_neighborhoods = batch.upload(ctx);
|
let draw_all_neighborhoods = batch.upload(ctx);
|
||||||
Box::new(Self {
|
Box::new(Self {
|
||||||
top_panel: crate::common::app_top_panel(ctx, app),
|
top_panel,
|
||||||
left_panel,
|
left_panel,
|
||||||
draw_all_neighborhoods,
|
draw_all_neighborhoods,
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,8 @@ use map_model::{IntersectionID, PathConstraints, RoadID};
|
|||||||
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
|
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
|
||||||
use widgetry::tools::open_browser;
|
use widgetry::tools::open_browser;
|
||||||
use widgetry::{
|
use widgetry::{
|
||||||
lctrl, Color, EventCtx, Image, Key, Line, PanelBuilder, TextExt, Widget, DEFAULT_CORNER_RADIUS,
|
lctrl, Color, EventCtx, Image, Key, Line, Panel, PanelBuilder, TextExt, Widget,
|
||||||
|
DEFAULT_CORNER_RADIUS,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -21,9 +22,10 @@ impl Tab {
|
|||||||
self,
|
self,
|
||||||
ctx: &mut EventCtx,
|
ctx: &mut EventCtx,
|
||||||
app: &App,
|
app: &App,
|
||||||
|
top_panel: &Panel,
|
||||||
per_tab_contents: Widget,
|
per_tab_contents: Widget,
|
||||||
) -> PanelBuilder {
|
) -> PanelBuilder {
|
||||||
crate::common::left_panel_builder(Widget::col(vec![
|
let contents = Widget::col(vec![
|
||||||
app.session.alt_proposals.to_widget(ctx, app),
|
app.session.alt_proposals.to_widget(ctx, app),
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
Line("Editing neighborhood")
|
Line("Editing neighborhood")
|
||||||
@ -61,7 +63,8 @@ impl Tab {
|
|||||||
.section(ctx),
|
.section(ctx),
|
||||||
self.make_buttons(ctx),
|
self.make_buttons(ctx),
|
||||||
per_tab_contents.section(ctx),
|
per_tab_contents.section(ctx),
|
||||||
]))
|
]);
|
||||||
|
crate::common::left_panel_builder(ctx, top_panel, contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_action(
|
pub fn handle_action(
|
||||||
|
@ -79,6 +79,7 @@ impl BrowseRatRuns {
|
|||||||
.panel_builder(
|
.panel_builder(
|
||||||
ctx,
|
ctx,
|
||||||
app,
|
app,
|
||||||
|
&self.top_panel,
|
||||||
percentage_bar(
|
percentage_bar(
|
||||||
ctx,
|
ctx,
|
||||||
Text::from(Line(format!(
|
Text::from(Line(format!(
|
||||||
@ -101,6 +102,7 @@ impl BrowseRatRuns {
|
|||||||
.panel_builder(
|
.panel_builder(
|
||||||
ctx,
|
ctx,
|
||||||
app,
|
app,
|
||||||
|
&self.top_panel,
|
||||||
Widget::col(vec![
|
Widget::col(vec![
|
||||||
percentage_bar(
|
percentage_bar(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -64,7 +64,7 @@ impl RoutePlanner {
|
|||||||
self.files.autosave(app);
|
self.files.autosave(app);
|
||||||
let results_widget = self.recalculate_paths(ctx, app);
|
let results_widget = self.recalculate_paths(ctx, app);
|
||||||
|
|
||||||
let mut panel = crate::common::left_panel_builder(Widget::col(vec![
|
let contents = Widget::col(vec![
|
||||||
app.session.alt_proposals.to_widget(ctx, app),
|
app.session.alt_proposals.to_widget(ctx, app),
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
Line("Plan a route").small_heading().into_widget(ctx),
|
Line("Plan a route").small_heading().into_widget(ctx),
|
||||||
@ -98,10 +98,11 @@ impl RoutePlanner {
|
|||||||
])
|
])
|
||||||
.into_widget(ctx),
|
.into_widget(ctx),
|
||||||
results_widget,
|
results_widget,
|
||||||
]))
|
]);
|
||||||
// Hovering on waypoint cards
|
let mut panel = crate::common::left_panel_builder(ctx, &self.top_panel, contents)
|
||||||
.ignore_initial_events()
|
// Hovering on waypoint cards
|
||||||
.build(ctx);
|
.ignore_initial_events()
|
||||||
|
.build(ctx);
|
||||||
panel.restore(ctx, &self.left_panel);
|
panel.restore(ctx, &self.left_panel);
|
||||||
self.left_panel = panel;
|
self.left_panel = panel;
|
||||||
|
|
||||||
|
@ -36,9 +36,11 @@ pub struct SelectBoundary {
|
|||||||
|
|
||||||
impl SelectBoundary {
|
impl SelectBoundary {
|
||||||
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighborhoodID) -> Box<dyn State<App>> {
|
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighborhoodID) -> Box<dyn State<App>> {
|
||||||
|
let top_panel = crate::common::app_top_panel(ctx, app);
|
||||||
|
let left_panel = make_panel(ctx, &top_panel);
|
||||||
let mut state = SelectBoundary {
|
let mut state = SelectBoundary {
|
||||||
top_panel: crate::common::app_top_panel(ctx, app),
|
top_panel,
|
||||||
left_panel: make_panel(ctx),
|
left_panel,
|
||||||
id,
|
id,
|
||||||
world: World::bounded(app.map.get_bounds()),
|
world: World::bounded(app.map.get_bounds()),
|
||||||
draw_boundary_roads: draw_boundary_roads(ctx, app),
|
draw_boundary_roads: draw_boundary_roads(ctx, app),
|
||||||
@ -145,7 +147,7 @@ impl SelectBoundary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.draw_boundary_roads = draw_boundary_roads(ctx, app);
|
self.draw_boundary_roads = draw_boundary_roads(ctx, app);
|
||||||
self.left_panel = make_panel(ctx);
|
self.left_panel = make_panel(ctx, &self.top_panel);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.last_failed_change = Some((id, self.currently_have_block(app, id)));
|
self.last_failed_change = Some((id, self.currently_have_block(app, id)));
|
||||||
@ -249,7 +251,7 @@ impl State<App> for SelectBoundary {
|
|||||||
if let Some(polygon) = lasso.event(ctx) {
|
if let Some(polygon) = lasso.event(ctx) {
|
||||||
self.lasso = None;
|
self.lasso = None;
|
||||||
self.add_blocks_freehand(ctx, app, polygon);
|
self.add_blocks_freehand(ctx, app, polygon);
|
||||||
self.left_panel = make_panel(ctx);
|
self.left_panel = make_panel(ctx, &self.top_panel);
|
||||||
}
|
}
|
||||||
return Transition::Keep;
|
return Transition::Keep;
|
||||||
}
|
}
|
||||||
@ -275,7 +277,7 @@ impl State<App> for SelectBoundary {
|
|||||||
}
|
}
|
||||||
"Select freehand" => {
|
"Select freehand" => {
|
||||||
self.lasso = Some(Lasso::new());
|
self.lasso = Some(Lasso::new());
|
||||||
self.left_panel = make_panel_for_lasso(ctx);
|
self.left_panel = make_panel_for_lasso(ctx, &self.top_panel);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -319,60 +321,68 @@ impl State<App> for SelectBoundary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_panel(ctx: &mut EventCtx) -> Panel {
|
fn make_panel(ctx: &mut EventCtx, top_panel: &Panel) -> Panel {
|
||||||
crate::common::left_panel_builder(Widget::col(vec![
|
crate::common::left_panel_builder(
|
||||||
Line("Adjusting neighborhood boundary")
|
ctx,
|
||||||
.small_heading()
|
top_panel,
|
||||||
|
Widget::col(vec![
|
||||||
|
Line("Adjusting neighborhood boundary")
|
||||||
|
.small_heading()
|
||||||
|
.into_widget(ctx),
|
||||||
|
Text::from_all(vec![
|
||||||
|
Line("Click").fg(ctx.style().text_hotkey_color),
|
||||||
|
Line(" to add/remove a block"),
|
||||||
|
])
|
||||||
|
.into_widget(ctx),
|
||||||
|
Text::from_all(vec![
|
||||||
|
Line("Hold "),
|
||||||
|
Line(Key::LeftControl.describe()).fg(ctx.style().text_hotkey_color),
|
||||||
|
Line(" and paint over blocks to add"),
|
||||||
|
])
|
||||||
|
.into_widget(ctx),
|
||||||
|
Text::from_all(vec![
|
||||||
|
Line("Hold "),
|
||||||
|
Line(Key::LeftShift.describe()).fg(ctx.style().text_hotkey_color),
|
||||||
|
Line(" and paint over blocks to remove"),
|
||||||
|
])
|
||||||
.into_widget(ctx),
|
.into_widget(ctx),
|
||||||
Text::from_all(vec![
|
|
||||||
Line("Click").fg(ctx.style().text_hotkey_color),
|
|
||||||
Line(" to add/remove a block"),
|
|
||||||
])
|
|
||||||
.into_widget(ctx),
|
|
||||||
Text::from_all(vec![
|
|
||||||
Line("Hold "),
|
|
||||||
Line(Key::LeftControl.describe()).fg(ctx.style().text_hotkey_color),
|
|
||||||
Line(" and paint over blocks to add"),
|
|
||||||
])
|
|
||||||
.into_widget(ctx),
|
|
||||||
Text::from_all(vec![
|
|
||||||
Line("Hold "),
|
|
||||||
Line(Key::LeftShift.describe()).fg(ctx.style().text_hotkey_color),
|
|
||||||
Line(" and paint over blocks to remove"),
|
|
||||||
])
|
|
||||||
.into_widget(ctx),
|
|
||||||
ctx.style()
|
|
||||||
.btn_outline
|
|
||||||
.icon_text("system/assets/tools/select.svg", "Select freehand")
|
|
||||||
.hotkey(Key::F)
|
|
||||||
.build_def(ctx),
|
|
||||||
Widget::row(vec![
|
|
||||||
ctx.style()
|
ctx.style()
|
||||||
.btn_solid_primary
|
.btn_outline
|
||||||
.text("Confirm")
|
.icon_text("system/assets/tools/select.svg", "Select freehand")
|
||||||
.hotkey(Key::Enter)
|
.hotkey(Key::F)
|
||||||
.build_def(ctx),
|
|
||||||
ctx.style()
|
|
||||||
.btn_solid_destructive
|
|
||||||
.text("Cancel")
|
|
||||||
.hotkey(Key::Escape)
|
|
||||||
.build_def(ctx),
|
.build_def(ctx),
|
||||||
|
Widget::row(vec![
|
||||||
|
ctx.style()
|
||||||
|
.btn_solid_primary
|
||||||
|
.text("Confirm")
|
||||||
|
.hotkey(Key::Enter)
|
||||||
|
.build_def(ctx),
|
||||||
|
ctx.style()
|
||||||
|
.btn_solid_destructive
|
||||||
|
.text("Cancel")
|
||||||
|
.hotkey(Key::Escape)
|
||||||
|
.build_def(ctx),
|
||||||
|
]),
|
||||||
|
Text::new().into_widget(ctx).named("warning"),
|
||||||
]),
|
]),
|
||||||
Text::new().into_widget(ctx).named("warning"),
|
)
|
||||||
]))
|
|
||||||
.build(ctx)
|
.build(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_panel_for_lasso(ctx: &mut EventCtx) -> Panel {
|
fn make_panel_for_lasso(ctx: &mut EventCtx, top_panel: &Panel) -> Panel {
|
||||||
crate::common::left_panel_builder(Widget::col(vec![
|
crate::common::left_panel_builder(
|
||||||
"Draw a custom boundary for a neighborhood"
|
ctx,
|
||||||
.text_widget(ctx)
|
top_panel,
|
||||||
.centered_vert(),
|
Widget::col(vec![
|
||||||
Text::from_all(vec![
|
"Draw a custom boundary for a neighborhood"
|
||||||
Line("Click and drag").fg(ctx.style().text_hotkey_color),
|
.text_widget(ctx)
|
||||||
Line(" to select the blocks to add to this neighborhood"),
|
.centered_vert(),
|
||||||
])
|
Text::from_all(vec![
|
||||||
.into_widget(ctx),
|
Line("Click and drag").fg(ctx.style().text_hotkey_color),
|
||||||
]))
|
Line(" to select the blocks to add to this neighborhood"),
|
||||||
|
])
|
||||||
|
.into_widget(ctx),
|
||||||
|
]),
|
||||||
|
)
|
||||||
.build(ctx)
|
.build(ctx)
|
||||||
}
|
}
|
||||||
|
@ -361,6 +361,10 @@ impl ColorScheme {
|
|||||||
let mut cs = ColorScheme::day_mode();
|
let mut cs = ColorScheme::day_mode();
|
||||||
cs.scheme = ColorSchemeChoice::LTN;
|
cs.scheme = ColorSchemeChoice::LTN;
|
||||||
cs.private_road = None;
|
cs.private_road = None;
|
||||||
|
|
||||||
|
cs.gui_style.panel_bg = Color::WHITE;
|
||||||
|
cs.panel_bg = cs.gui_style.panel_bg;
|
||||||
|
|
||||||
cs
|
cs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user