mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:27:35 +00:00
PixelPaint: Store tool cursors as NNRP<Gfx::Bitmap const>
This commit is contained in:
parent
65710bf3f7
commit
df07416171
24 changed files with 26 additions and 26 deletions
|
@ -187,7 +187,7 @@ private:
|
||||||
|
|
||||||
float m_pixel_grid_threshold { 15.0f };
|
float m_pixel_grid_threshold { 15.0f };
|
||||||
|
|
||||||
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> m_active_cursor { Gfx::StandardCursor::None };
|
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> m_active_cursor { Gfx::StandardCursor::None };
|
||||||
|
|
||||||
bool m_loaded_from_image { true };
|
bool m_loaded_from_image { true };
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ public:
|
||||||
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
||||||
virtual void on_mouseup(Layer*, MouseEvent&) override;
|
virtual void on_mouseup(Layer*, MouseEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override
|
||||||
{
|
{
|
||||||
if (m_editor && m_editor->scale() != m_scale_last_created_cursor)
|
if (m_editor && m_editor->scale() != m_scale_last_created_cursor)
|
||||||
refresh_editor_cursor();
|
refresh_editor_cursor();
|
||||||
|
@ -59,7 +59,7 @@ private:
|
||||||
bool m_was_drawing { false };
|
bool m_was_drawing { false };
|
||||||
bool m_has_clicked { false };
|
bool m_has_clicked { false };
|
||||||
Gfx::IntPoint m_last_position;
|
Gfx::IntPoint m_last_position;
|
||||||
NonnullRefPtr<Gfx::Bitmap> m_cursor = build_cursor();
|
NonnullRefPtr<Gfx::Bitmap const> m_cursor = build_cursor();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace PixelPaint {
|
||||||
|
|
||||||
BucketTool::BucketTool()
|
BucketTool::BucketTool()
|
||||||
{
|
{
|
||||||
m_cursor = Gfx::Bitmap::load_from_file("/res/icons/pixelpaint/bucket.png"sv).release_value_but_fixme_should_propagate_errors();
|
m_cursor = NonnullRefPtr<Gfx::Bitmap const> { Gfx::Bitmap::load_from_file("/res/icons/pixelpaint/bucket.png"sv).release_value_but_fixme_should_propagate_errors() };
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flood_fill(Gfx::Bitmap& bitmap, Gfx::IntPoint start_position, Color fill_color, int threshold)
|
static void flood_fill(Gfx::Bitmap& bitmap, Gfx::IntPoint start_position, Color fill_color, int threshold)
|
||||||
|
|
|
@ -18,14 +18,14 @@ public:
|
||||||
|
|
||||||
virtual void on_mousedown(Layer*, MouseEvent&) override;
|
virtual void on_mousedown(Layer*, MouseEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return m_cursor; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return m_cursor; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Bucket Tool"sv; }
|
virtual StringView tool_name() const override { return "Bucket Tool"sv; }
|
||||||
|
|
||||||
RefPtr<GUI::Widget> m_properties_widget;
|
RefPtr<GUI::Widget> m_properties_widget;
|
||||||
int m_threshold { 0 };
|
int m_threshold { 0 };
|
||||||
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> m_cursor { Gfx::StandardCursor::Crosshair };
|
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> m_cursor { Gfx::StandardCursor::Crosshair };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ void CloneTool::draw_line(Gfx::Bitmap& bitmap, Gfx::Color color, Gfx::IntPoint s
|
||||||
BrushTool::draw_line(bitmap, color, start, end);
|
BrushTool::draw_line(bitmap, color, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> CloneTool::cursor()
|
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> CloneTool::cursor()
|
||||||
{
|
{
|
||||||
if (m_is_selecting_location)
|
if (m_is_selecting_location)
|
||||||
return Gfx::StandardCursor::Eyedropper;
|
return Gfx::StandardCursor::Eyedropper;
|
||||||
|
|
|
@ -16,7 +16,7 @@ public:
|
||||||
virtual ~CloneTool() override = default;
|
virtual ~CloneTool() override = default;
|
||||||
|
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override;
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override;
|
||||||
|
|
||||||
virtual bool is_overriding_alt() override { return true; }
|
virtual bool is_overriding_alt() override { return true; }
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
virtual bool on_keydown(GUI::KeyEvent&) override;
|
virtual bool on_keydown(GUI::KeyEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Ellipse Tool"sv; }
|
virtual StringView tool_name() const override { return "Ellipse Tool"sv; }
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
namespace PixelPaint {
|
namespace PixelPaint {
|
||||||
|
|
||||||
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> GradientTool::cursor()
|
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> GradientTool::cursor()
|
||||||
{
|
{
|
||||||
if (m_hover_over_drag_handle || m_hover_over_start_handle || m_hover_over_end_handle)
|
if (m_hover_over_drag_handle || m_hover_over_start_handle || m_hover_over_end_handle)
|
||||||
return Gfx::StandardCursor::Hand;
|
return Gfx::StandardCursor::Hand;
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
virtual void on_tool_activation() override;
|
virtual void on_tool_activation() override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
|
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override;
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override;
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
virtual void on_tool_activation() override;
|
virtual void on_tool_activation() override;
|
||||||
|
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Guide Tool"sv; }
|
virtual StringView tool_name() const override { return "Guide Tool"sv; }
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
virtual bool on_keydown(GUI::KeyEvent&) override;
|
virtual bool on_keydown(GUI::KeyEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
|
|
||||||
void draw_using(GUI::Painter&, Gfx::IntPoint start_position, Gfx::IntPoint end_position, Color color, int thickness);
|
void draw_using(GUI::Painter&, Gfx::IntPoint start_position, Gfx::IntPoint end_position, Color color, int thickness);
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,7 @@ Optional<ResizeAnchorLocation const> MoveTool::resize_anchor_location_from_curso
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> MoveTool::cursor()
|
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> MoveTool::cursor()
|
||||||
{
|
{
|
||||||
if (m_resize_anchor_location.has_value()) {
|
if (m_resize_anchor_location.has_value()) {
|
||||||
switch (m_resize_anchor_location.value()) {
|
switch (m_resize_anchor_location.value()) {
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
virtual void on_keyup(GUI::KeyEvent&) override;
|
virtual void on_keyup(GUI::KeyEvent&) override;
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override;
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override;
|
||||||
virtual bool is_overriding_alt() override { return true; }
|
virtual bool is_overriding_alt() override { return true; }
|
||||||
LayerSelectionMode layer_selection_mode() const { return m_layer_selection_mode; }
|
LayerSelectionMode layer_selection_mode() const { return m_layer_selection_mode; }
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class PenTool final : public BrushTool {
|
||||||
public:
|
public:
|
||||||
PenTool();
|
PenTool();
|
||||||
virtual ~PenTool() override = default;
|
virtual ~PenTool() override = default;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -22,7 +22,7 @@ public:
|
||||||
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
||||||
|
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Eyedropper; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Eyedropper; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Picker Tool"sv; }
|
virtual StringView tool_name() const override { return "Picker Tool"sv; }
|
||||||
|
|
|
@ -23,7 +23,7 @@ public:
|
||||||
virtual bool on_keydown(GUI::KeyEvent&) override;
|
virtual bool on_keydown(GUI::KeyEvent&) override;
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
virtual Gfx::IntPoint point_position_to_preferred_cell(Gfx::FloatPoint position) const override;
|
virtual Gfx::IntPoint point_position_to_preferred_cell(Gfx::FloatPoint position) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
virtual void on_keyup(GUI::KeyEvent&) override;
|
virtual void on_keyup(GUI::KeyEvent&) override;
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
virtual Gfx::IntPoint point_position_to_preferred_cell(Gfx::FloatPoint position) const override;
|
virtual Gfx::IntPoint point_position_to_preferred_cell(Gfx::FloatPoint position) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
virtual bool on_keydown(GUI::KeyEvent&) override;
|
virtual bool on_keydown(GUI::KeyEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Rectangle Tool"sv; }
|
virtual StringView tool_name() const override { return "Rectangle Tool"sv; }
|
||||||
|
|
|
@ -23,7 +23,7 @@ public:
|
||||||
virtual void on_mouseup(Layer*, MouseEvent&) override;
|
virtual void on_mouseup(Layer*, MouseEvent&) override;
|
||||||
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Spray Tool"sv; }
|
virtual StringView tool_name() const override { return "Spray Tool"sv; }
|
||||||
|
|
|
@ -304,7 +304,7 @@ bool TextTool::on_keydown(GUI::KeyEvent& event)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> TextTool::cursor()
|
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> TextTool::cursor()
|
||||||
{
|
{
|
||||||
if (m_mouse_is_over_text)
|
if (m_mouse_is_over_text)
|
||||||
return Gfx::StandardCursor::Move;
|
return Gfx::StandardCursor::Move;
|
||||||
|
|
|
@ -40,7 +40,7 @@ public:
|
||||||
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
virtual void on_second_paint(Layer const*, GUI::PaintEvent&) override;
|
||||||
virtual void on_primary_color_change(Color) override;
|
virtual void on_primary_color_change(Color) override;
|
||||||
virtual void on_tool_deactivation() override;
|
virtual void on_tool_deactivation() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override;
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -68,7 +68,7 @@ public:
|
||||||
virtual void on_tool_activation() { }
|
virtual void on_tool_activation() { }
|
||||||
virtual void on_tool_deactivation() { }
|
virtual void on_tool_deactivation() { }
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() { return nullptr; }
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() { return nullptr; }
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() { return Gfx::StandardCursor::None; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() { return Gfx::StandardCursor::None; }
|
||||||
virtual Gfx::IntPoint point_position_to_preferred_cell(Gfx::FloatPoint position) const { return position.to_type<int>(); }
|
virtual Gfx::IntPoint point_position_to_preferred_cell(Gfx::FloatPoint position) const { return position.to_type<int>(); }
|
||||||
|
|
||||||
void clear() { m_editor = nullptr; }
|
void clear() { m_editor = nullptr; }
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
virtual void on_mousedown(Layer*, MouseEvent& event) override;
|
virtual void on_mousedown(Layer*, MouseEvent& event) override;
|
||||||
virtual bool on_keydown(GUI::KeyEvent&) override;
|
virtual bool on_keydown(GUI::KeyEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Wand Select Tool"sv; }
|
virtual StringView tool_name() const override { return "Wand Select Tool"sv; }
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
|
|
||||||
virtual void on_mousedown(Layer*, MouseEvent&) override;
|
virtual void on_mousedown(Layer*, MouseEvent&) override;
|
||||||
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
virtual ErrorOr<GUI::Widget*> get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Zoom; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor() override { return Gfx::StandardCursor::Zoom; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Zoom Tool"sv; }
|
virtual StringView tool_name() const override { return "Zoom Tool"sv; }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue