Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions amulet_map_editor/api/opengl/canvas/canvas.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Optional, Callable
import logging

import sys
import wx
from wx.glcanvas import GLCanvas, GLAttributes, GLContext, GLContextAttrs
from OpenGL.GL import (
Expand All @@ -15,6 +16,7 @@
GL_ONE_MINUS_SRC_ALPHA,
glGetString,
GL_VERSION,
glViewport,
)
from OpenGL.GL.ARB.explicit_attrib_location import glInitExplicitAttribLocationARB

Expand All @@ -28,8 +30,12 @@
Objects that need to bind textures or data should do so in the draw function so they can be sure the context is set.
"""

if sys.platform == "linux":
Canvas_Type = wx.Window
else:
Canvas_Type = GLCanvas
Comment on lines +33 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer one consistent behaviour across all platforms.
Does the wx.Window behaviour work across all platforms?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to not work correctly on Windows. Similar (though different) flickering behavior occurs when trying to use the Linux solution on Windows.


class BaseCanvas(GLCanvas):
class BaseCanvas(Canvas_Type):
_context: Optional[GLContext]

def __init__(self, parent: wx.Window):
Expand All @@ -42,12 +48,38 @@ def __init__(self, parent: wx.Window):
display_attributes.PlatformDefaults().MinRGBA(8, 8, 8, 8).DoubleBuffer().Depth(
24
).EndList()
super().__init__(
parent,
display_attributes,
size=parent.GetClientSize(),
style=wx.WANTS_CHARS,
)
if Canvas_Type == wx.Window:
super().__init__(parent)
# self.SetSizer(self._canvas_sizer)
self._opengl_canvas = GLCanvas( self,
display_attributes,
size=parent.GetClientSize(),
style=wx.WANTS_CHARS,
)

def forward_event(event):
# Create a new mouse event and send it to the parent
new_event = wx.MouseEvent(event.GetEventType())
new_event.SetPosition(event.GetPosition())
wx.PostEvent(self, new_event)
event.Skip() # Continue processing normally
self._opengl_canvas.Bind(wx.EVT_RIGHT_DOWN, forward_event)
self._opengl_canvas.Bind(wx.EVT_RIGHT_UP, forward_event)
self._opengl_canvas.Bind(wx.EVT_LEFT_DOWN, forward_event)
self._opengl_canvas.Bind(wx.EVT_LEFT_UP, forward_event)
self._opengl_canvas.Bind(wx.EVT_MOTION, forward_event)
self._opengl_canvas.Bind(wx.EVT_MOUSEWHEEL, forward_event)
self._opengl_canvas.Bind(wx.EVT_SIZING, self.resize)
elif Canvas_Type == GLCanvas:
super().__init__(
parent,
display_attributes,
size=parent.GetClientSize(),
style=wx.WANTS_CHARS,
)
self._opengl_canvas = self
else:
raise NotImplementedError

# Amulet-Team/Amulet-Map-Editor#84
# Amulet-Team/Amulet-Map-Editor#597
Expand All @@ -58,7 +90,7 @@ def gl3() -> Optional[GLContext]:
ctx_attrs.OGLVersion(3, 3)
ctx_attrs.CoreProfile()
ctx_attrs.EndList()
ctx = GLContext(self, ctxAttrs=ctx_attrs)
ctx = GLContext(self._opengl_canvas, ctxAttrs=ctx_attrs)
if ctx.IsOK():
return ctx
return None
Expand All @@ -69,7 +101,7 @@ def gl2() -> Optional[GLContext]:
ctx_attrs.OGLVersion(2, 1)
ctx_attrs.CompatibilityProfile()
ctx_attrs.EndList()
ctx = GLContext(self, ctxAttrs=ctx_attrs)
ctx = GLContext(self._opengl_canvas, ctxAttrs=ctx_attrs)
if ctx.IsOK() and glInitExplicitAttribLocationARB():
return ctx
return None
Expand All @@ -83,6 +115,10 @@ def gl2() -> Optional[GLContext]:
self._init = False

self.Bind(wx.EVT_SHOW, self._on_show)

def resize(self, event):
self._opengl_canvas.SetCurrent(self._context)
glViewport(0, 0, event.GetSize().x, event.GetSize().y)

@property
def context(self) -> GLContext:
Expand All @@ -101,7 +137,7 @@ def _on_show(self, evt: wx.ShowEvent):

def _init_opengl(self):
"""Set up the OpenGL state after the window is first shown."""
self.SetCurrent(self._context)
self._opengl_canvas.SetCurrent(self._context)
gl_version = glGetString(GL_VERSION)
if isinstance(gl_version, bytes):
gl_version = gl_version.decode("utf-8")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def bind_events(self):

def enable(self):
"""Enable the canvas and start it working."""
self.SetCurrent(self._context)
self._opengl_canvas.SetCurrent(self._context)
self.renderer.enable()
self.buttons.enable()

Expand Down Expand Up @@ -312,9 +312,9 @@ def _on_size(self, evt):
evt.Skip()

def _set_size(self):
size = self.GetClientSize() * self.GetContentScaleFactor()
size = self._opengl_canvas.GetClientSize() * self._opengl_canvas.GetContentScaleFactor()
width, height = size
self.SetCurrent(self._context)
self._opengl_canvas.SetCurrent(self._context)
glViewport(0, 0, width, height)
if height > 0:
self.camera.aspect_ratio = width / height
Expand Down
15 changes: 13 additions & 2 deletions amulet_map_editor/programs/edit/api/canvas/edit_canvas.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import warnings
import wx
import sys
from typing import Callable, TYPE_CHECKING, Any, Generator, Optional
from types import GeneratorType
from threading import RLock, Thread
Expand Down Expand Up @@ -163,8 +164,18 @@ def __init__(self, parent: wx.Window, world: "BaseLevel"):

def _init_opengl(self):
super()._init_opengl()
self._file_panel = FilePanel(self)
self._canvas_sizer.Add(self._file_panel, 0, wx.EXPAND, 0)
if sys.platform == "linux":
self.panel = wx.Panel(self, style=wx.STAY_ON_TOP)
self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.button_sizer.AddStretchSpacer(1)
self.button_sizer.Add(self.panel, 0, wx.EXPAND, 0)
self._file_panel = FilePanel(self, self.panel)
self.panel.SetSizer(self._file_panel)
self.panel.SetBackgroundColour((155, 178, 216, 255))
self._canvas_sizer.Add(self.button_sizer, 0, wx.EXPAND, 0)
else:
self._file_panel = FilePanel(self, self)
self._canvas_sizer.Add(self._file_panel, 0, wx.EXPAND, 0)
Comment on lines +167 to +178
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue exists on macos as well. Your check only applies the custom behaviour to linux.
Can the linux behaviour be used on windows and macos as well?
I would prefer to have your custom behaviour on all platforms if it works on all platforms.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer this as well in the long run, but this was a quick way to keep the same windows behavior while making the linux side usable again.

The 2 switches on sys.platform I had to use were:

  1. Make the BaseCanvas inherit from wx.Window instead of wx.glcanvas.GLCanvas. This seems to be related to some underlying difference in the implementation of wx.glcanvas.GLCanvas, though I am not extremely familiar with wx widgets. I think switching to Qt in the future is the solution to this.

  2. Giving the FilePanel an underlying panel on linux. Since linux seems unable to support transparent backgrounds for wx elements on top of a GLCanvas, I added this panel with a solid background. We could do the same on windows, but the buttons would no longer have a transparent background. Not the end of the world, but I'd say a slightly negative change, so I maintained the previous behavior for windows. I'm fine with it either way though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I understand

self._tool_sizer = ToolManagerSizer(self)
self._canvas_sizer.Add(self._tool_sizer, 1, wx.EXPAND, 0)

Expand Down
4 changes: 2 additions & 2 deletions amulet_map_editor/programs/edit/api/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,13 @@ def draw_fake_levels(self):

def end_draw(self):
"""Run commands after drawing."""
self.canvas.SwapBuffers()
self.canvas._opengl_canvas.SwapBuffers()

else:

def end_draw(self):
"""Run commands after drawing."""
self.canvas.SwapBuffers()
self.canvas._opengl_canvas.SwapBuffers()
self._chunk_generator.thread_action()

def _gc(self, event):
Expand Down
18 changes: 9 additions & 9 deletions amulet_map_editor/programs/edit/api/ui/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@ def _format_float(num: float) -> str:


class FilePanel(wx.BoxSizer, EditCanvasContainer):
def __init__(self, canvas: "EditCanvas"):
def __init__(self, canvas: "EditCanvas", wx_parent):
wx.BoxSizer.__init__(self, wx.HORIZONTAL)
EditCanvasContainer.__init__(self, canvas)

level = self.canvas.world
self._version_text = wx.StaticText(
canvas,
wx_parent,
label=f"{level.level_wrapper.platform}, {level.level_wrapper.version}",
)
self._version_text.SetToolTip(
lang.get("program_3d_edit.file_ui.version_tooltip")
)
self.Add(self._version_text, 0)
self.AddStretchSpacer(1)
self._projection_button = wx.Button(canvas, label="3D")
self._projection_button = wx.Button(wx_parent, label="3D")
self._projection_button.SetToolTip(
lang.get("program_3d_edit.file_ui.projection_tooltip")
)
Expand All @@ -56,7 +56,7 @@ def __init__(self, canvas: "EditCanvas"):
self._projection_button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5
)
self._location_button = wx.Button(
canvas, label=", ".join([f"{s:.2f}" for s in self.canvas.camera.location])
wx_parent, label=", ".join([f"{s:.2f}" for s in self.canvas.camera.location])
)
self._location_button.SetToolTip(
lang.get("program_3d_edit.file_ui.location_tooltip")
Expand All @@ -66,20 +66,20 @@ def __init__(self, canvas: "EditCanvas"):

def set_speed(evt):
dialog = SpeedSelectDialog(
canvas, self.canvas.camera.move_speed * 1000 / 33
wx_parent, self.canvas.camera.move_speed * 1000 / 33
)
if dialog.ShowModal() == wx.ID_OK:
self.canvas.camera.move_speed = dialog.speed * 33 / 1000

self._speed_button = wx.Button(
canvas,
wx_parent,
label=f"{_format_float(self.canvas.camera.move_speed * 1000 / 33)} {lang.get('program_3d_edit.file_ui.speed_blocks_per_second')}",
)
self._speed_button.SetToolTip(lang.get("program_3d_edit.file_ui.speed_tooltip"))
self._speed_button.Bind(wx.EVT_BUTTON, set_speed)
self.Add(self._speed_button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5)

self._dim_options = SimpleChoiceAny(canvas)
self._dim_options = SimpleChoiceAny(wx_parent)
self._dim_options.SetToolTip(lang.get("program_3d_edit.file_ui.dim_tooltip"))
self._dim_options.SetItems(level.level_wrapper.dimensions)
self._dim_options.SetValue(level.level_wrapper.dimensions[0])
Expand All @@ -88,7 +88,7 @@ def set_speed(evt):
self.Add(self._dim_options, 0, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5)

def create_button(text, operation):
button = wx.Button(canvas, label=text)
button = wx.Button(wx_parent, label=text)
button.Bind(wx.EVT_BUTTON, operation)
self.Add(button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT, 5)
return button
Expand All @@ -114,7 +114,7 @@ def create_button(text, operation):
self._save_button.SetToolTip(lang.get("program_3d_edit.file_ui.save_tooltip"))

close_button = wx.BitmapButton(
canvas, bitmap=image.icon.tablericons.square_x.bitmap(20, 20)
wx_parent, bitmap=image.icon.tablericons.square_x.bitmap(20, 20)
)
close_button.SetToolTip(lang.get("program_3d_edit.file_ui.close_tooltip"))
close_button.Bind(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,21 @@ class BaseOperationChoiceToolUI(wx.BoxSizer, BaseToolUI):

ShowOpenFolder = True

def __init__(self, canvas: "EditCanvas"):
def __init__(self, canvas: "EditCanvas", wx_parent):
wx.BoxSizer.__init__(self, wx.VERTICAL)
BaseToolUI.__init__(self, canvas)

self._active_operation: Optional[OperationUIType] = None
self._last_active_operation_id: Optional[str] = None
self._wx_parent = wx_parent

horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)

assert isinstance(
self.OperationGroupName, str
), "OperationGroupName has not been set or is not a string."
# The operation selection
self._operation_choice = SimpleChoiceAny(self.canvas)
self._operation_choice = SimpleChoiceAny(self._wx_parent)
horizontal_sizer.Add(self._operation_choice)
self._operations = UIOperationManager(self.OperationGroupName)
self._operation_choice.SetItems(
Expand All @@ -50,7 +51,7 @@ def __init__(self, canvas: "EditCanvas"):

# The reload button
self._reload_operation = wx.BitmapButton(
self.canvas, bitmap=image.REFRESH_ICON.bitmap(16, 16)
self._wx_parent, bitmap=image.REFRESH_ICON.bitmap(16, 16)
)
self._reload_operation.SetToolTip("Reload Operations")
horizontal_sizer.Add(self._reload_operation)
Expand All @@ -59,7 +60,7 @@ def __init__(self, canvas: "EditCanvas"):
# The open folder button
if self.ShowOpenFolder:
self._open_folder = wx.BitmapButton(
self.canvas, bitmap=image.TABLERICONS.folder.bitmap(16, 16)
self._wx_parent, bitmap=image.TABLERICONS.folder.bitmap(16, 16)
)
self._open_folder.SetToolTip("Open Plugin Folder")
horizontal_sizer.Add(self._open_folder)
Expand Down Expand Up @@ -103,7 +104,7 @@ def _setup_operation(self):
self._operation_sizer.Clear(delete_windows=True)
try:
self._active_operation = operation(
self.canvas, self.canvas, self.canvas.world
self._wx_parent, self.canvas, self.canvas.world
)
self._operation_sizer.Add(
self._active_operation, *self._active_operation.wx_add_options
Expand Down
28 changes: 19 additions & 9 deletions amulet_map_editor/programs/edit/api/ui/tool_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import wx
import sys
from typing import TYPE_CHECKING, Type, Dict, Optional

from amulet_map_editor.programs.edit.api import EditCanvasContainer
Expand Down Expand Up @@ -32,10 +33,19 @@ def __init__(self, canvas: "EditCanvas"):
self._tools: Dict[str, BaseToolUIType] = {}
self._active_tool: Optional[BaseToolUIType] = None

self._tool_option_panel = wx.Panel(canvas)
if sys.platform == "linux":
self._tool_option_panel.SetBackgroundColour((155, 178, 216, 255))
self._tool_option_sizer = wx.BoxSizer(wx.VERTICAL)
self._tool_option_panel.SetSizer(self._tool_option_sizer)
self._tool_option_sizer_shove_left = wx.BoxSizer(wx.HORIZONTAL)
self._tool_option_sizer_shove_left.Add(self._tool_option_panel, 0, wx.EXPAND, 0)
self._tool_option_sizer_shove_left.AddStretchSpacer(1)
self.AddStretchSpacer(1)
self.Add(
self._tool_option_sizer, 1, wx.EXPAND | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 0
self._tool_option_sizer_shove_left, 0, wx.EXPAND | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 0
)
self.AddStretchSpacer(1)

tool_select_sizer = wx.BoxSizer(wx.HORIZONTAL)
tool_select_sizer.AddStretchSpacer(1)
Expand All @@ -44,12 +54,12 @@ def __init__(self, canvas: "EditCanvas"):
tool_select_sizer.AddStretchSpacer(1)
self.Add(tool_select_sizer, 0, wx.EXPAND, 0)

self.register_tool(SelectTool)
self.register_tool(PasteTool)
self.register_tool(OperationTool)
self.register_tool(ImportTool)
self.register_tool(ExportTool)
self.register_tool(ChunkTool)
self.register_tool(SelectTool, self._tool_option_panel)
self.register_tool(PasteTool, self._tool_option_panel)
self.register_tool(OperationTool, self._tool_option_panel)
self.register_tool(ImportTool, self._tool_option_panel)
self.register_tool(ExportTool, self._tool_option_panel)
self.register_tool(ChunkTool, self._tool_option_panel)

@property
def tools(self):
Expand All @@ -60,11 +70,11 @@ def bind_events(self):
self._active_tool.bind_events()
self.canvas.Bind(EVT_TOOL_CHANGE, self._enable_tool_event)

def register_tool(self, tool_cls: Type[BaseToolUIType]):
def register_tool(self, tool_cls: Type[BaseToolUIType], wx_parent):
assert issubclass(tool_cls, (wx.Window, wx.Sizer)) and issubclass(
tool_cls, BaseToolUI
)
tool = tool_cls(self.canvas)
tool = tool_cls(self.canvas, wx_parent)
self._tool_select.register_tool(tool.name)
if isinstance(tool, wx.Window):
tool.Hide()
Expand Down
4 changes: 2 additions & 2 deletions amulet_map_editor/programs/edit/plugins/tools/chunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@


class ChunkTool(wx.BoxSizer, DefaultBaseToolUI):
def __init__(self, canvas: "EditCanvas"):
def __init__(self, canvas: "EditCanvas", wx_parent):
wx.BoxSizer.__init__(self, wx.HORIZONTAL)
DefaultBaseToolUI.__init__(self, canvas)

self._selection = ChunkSelectionBehaviour(self.canvas)

self._button_panel = wx.Panel(canvas)
self._button_panel = wx.Panel(wx_parent)
button_sizer = wx.BoxSizer(wx.VERTICAL)
self._button_panel.SetSizer(button_sizer)

Expand Down
4 changes: 2 additions & 2 deletions amulet_map_editor/programs/edit/plugins/tools/import_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@


class ImportTool(wx.BoxSizer, DefaultBaseToolUI):
def __init__(self, canvas: "EditCanvas"):
def __init__(self, canvas: "EditCanvas", wx_parent):
wx.BoxSizer.__init__(self, wx.VERTICAL)
DefaultBaseToolUI.__init__(self, canvas)

self._selection = StaticSelectionBehaviour(self.canvas)

self._open_file_button = wx.Button(canvas, label="Import File")
self._open_file_button = wx.Button(wx_parent, label="Import File")
self._open_file_button.Bind(wx.EVT_BUTTON, self._on_open_file)
self.AddStretchSpacer()
self.Add(self._open_file_button, flag=wx.ALL, border=10)
Expand Down
Loading