Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,27 @@
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.annotation.Nullable;
import javax.swing.JPanel;
import net.runelite.api.Constants;

final class ClientPanel extends JPanel
{
private static final Dimension GAME_SIZE = new Dimension(Constants.GAME_FIXED_SIZE);

private final JPanel consoleContainer = new JPanel(new BorderLayout());

public ClientPanel(@Nullable Component client)
{
setSize(Constants.GAME_FIXED_SIZE);
setMinimumSize(Constants.GAME_FIXED_SIZE);
setPreferredSize(Constants.GAME_FIXED_SIZE);
setLayout(new BorderLayout());
setBackground(Color.black);
consoleContainer.setOpaque(false);
consoleContainer.setVisible(false);
add(consoleContainer, BorderLayout.SOUTH);

if (client == null)
{
Expand All @@ -48,4 +56,51 @@ public ClientPanel(@Nullable Component client)

add(client, BorderLayout.CENTER);
}
}

void setConsole(Component console)
{
consoleContainer.removeAll();
if (console != null)
{
consoleContainer.add(console, BorderLayout.CENTER);
}
consoleContainer.revalidate();
consoleContainer.repaint();
}

void setConsoleVisible(boolean visible)
{
if (consoleContainer.isVisible() == visible)
{
return;
}
consoleContainer.setVisible(visible);
revalidate();
repaint();
}

boolean isConsoleVisible()
{
return consoleContainer.isVisible();
}

@Override
public Dimension getMinimumSize()
{
Dimension size = new Dimension(GAME_SIZE);
if (consoleContainer.isVisible())
{
Dimension consoleSize = consoleContainer.getPreferredSize();
size.height += consoleSize != null ? consoleSize.height : 0;
}
return size;
}

@Override
public Dimension getPreferredSize()
{
Dimension size = getMinimumSize();
size.width = Math.max(size.width, GAME_SIZE.width);
return size;
}
}
162 changes: 162 additions & 0 deletions runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -66,6 +68,7 @@
import net.runelite.client.util.SwingUtil;
import net.runelite.client.util.WinUtil;
import net.runelite.client.util.*;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand All @@ -81,6 +84,10 @@
import java.awt.desktop.QuitStrategy;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.*;
Expand Down Expand Up @@ -150,6 +157,14 @@ public class ClientUI

private List<KeyListener> keyListeners;

private LogConsolePanel consolePanel;
private ConsoleLogAppender consoleLogAppender;
private PrintStream consolePrintStream;
private JButton consoleToggleButton;
private BufferedImage consoleIconOpen;
private BufferedImage consoleIconClosed;
private boolean consoleVisible;

@RequiredArgsConstructor
private static class HistoryEntry
{
Expand Down Expand Up @@ -364,6 +379,11 @@ public void componentMoved(ComponentEvent e)
content.setLayout(new Layout());

clientPanel = new ClientPanel(client);
consolePanel = new LogConsolePanel();
clientPanel.setConsole(consolePanel);
clientPanel.setConsoleVisible(false);
consoleVisible = false;
initializeConsoleLogging();
content.add(clientPanel);

sidebar = new JTabbedPane(JTabbedPane.RIGHT);
Expand Down Expand Up @@ -505,6 +525,16 @@ public MouseEvent mousePressed(MouseEvent mouseEvent)
// Decorate window with custom chrome and titlebar if needed
withTitleBar = config.enableCustomChrome();
toolbarPanel = new ClientToolbarPanel(!withTitleBar);
consoleIconClosed = createConsoleIcon(false);
consoleIconOpen = createConsoleIcon(true);
consoleToggleButton = toolbarPanel.add(
NavigationButton.builder()
.priority(95)
.icon(consoleIconClosed)
.tooltip("Show console")
.onClick(this::toggleConsole)
.build(), false);
updateConsoleToggleButton();

sidebarOpenIcon = ImageUtil.loadImageResource(ClientUI.class, withTitleBar ? "open.png" : "open_rs.png");
sidebarCloseIcon = ImageUtil.flipImage(sidebarOpenIcon, true, false);
Expand Down Expand Up @@ -1137,6 +1167,93 @@ private void togglePluginPanel()
}
}

private void toggleConsole()
{
setConsoleVisible(!consoleVisible);
}

private void setConsoleVisible(boolean visible)
{
if (consolePanel == null || clientPanel == null || consoleVisible == visible)
{
return;
}

consoleVisible = visible;
clientPanel.setConsoleVisible(visible);
updateConsoleToggleButton();

if (content != null)
{
content.revalidate();
content.repaint();
}

if (frame != null)
{
frame.revalidateMinimumSize();
}
}

private void updateConsoleToggleButton()
{
if (consoleToggleButton == null)
{
return;
}

consoleToggleButton.setIcon(new ImageIcon(consoleVisible ? consoleIconOpen : consoleIconClosed));
consoleToggleButton.setToolTipText(consoleVisible ? "Hide console" : "Show console");
}


private void initializeConsoleLogging()
{
if (consolePanel == null || consoleLogAppender != null)
{
return;
}

OutputStream consoleStream = consolePanel.createOutputStream();
PrintStream originalOut = System.out;
TeeOutputStream teeStream = new TeeOutputStream(consoleStream, originalOut);

try
{
consolePrintStream = new PrintStream(teeStream, true, StandardCharsets.UTF_8);
}
catch (Exception ex)
{
consolePrintStream = new PrintStream(teeStream, true);
}

System.setOut(consolePrintStream);

LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
consoleLogAppender = new ConsoleLogAppender(consolePanel::append);
consoleLogAppender.setContext(loggerContext);
consoleLogAppender.start();

Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.addAppender(consoleLogAppender);
}

private BufferedImage createConsoleIcon(boolean active)
{
BufferedImage icon = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = icon.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setColor(ColorScheme.DARK_GRAY_COLOR);
graphics.fillRoundRect(1, 3, 14, 10, 3, 3);
graphics.setColor(active ? ColorScheme.BRAND_ORANGE : ColorScheme.LIGHT_GRAY_COLOR);
graphics.drawRoundRect(1, 3, 14, 10, 3, 3);
graphics.setColor(ColorScheme.PROGRESS_COMPLETE_COLOR);
graphics.drawLine(3, 7, 12, 7);
graphics.drawLine(3, 10, 9, 10);
graphics.dispose();
return icon;
}

private void pushHistory()
{
selectedTabHistory.addLast(new HistoryEntry(sidebar.isVisible(), selectedTab));
Expand Down Expand Up @@ -1404,6 +1521,51 @@ public void mouseClicked(MouseEvent e)
return trayIcon;
}


private static final class TeeOutputStream extends OutputStream
{
private final OutputStream primary;
private final OutputStream secondary;

private TeeOutputStream(OutputStream primary, OutputStream secondary)
{
this.primary = primary;
this.secondary = secondary;
}

@Override
public void write(int b) throws IOException
{
if (primary != null)
{
primary.write(b);
}
if (secondary != null)
{
secondary.write(b);
}
}

@Override
public void flush() throws IOException
{
if (primary != null)
{
primary.flush();
}
if (secondary != null)
{
secondary.flush();
}
}

@Override
public void close() throws IOException
{
flush();
}
}

private class Layout implements LayoutManager2
{
private int prevState;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024 Microbot Contributors
* All rights reserved.
*/
package net.runelite.client.ui;

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import java.util.function.Consumer;

final class ConsoleLogAppender extends AppenderBase<ILoggingEvent>
{
private final Consumer<String> logConsumer;
private PatternLayout layout;

ConsoleLogAppender(Consumer<String> logConsumer)
{
this.logConsumer = logConsumer;
}

@Override
public void start()
{
layout = new PatternLayout();
layout.setContext(getContext());
layout.setPattern("%d{HH:mm:ss} %-5level %logger{36} - %msg%n");
layout.start();
super.start();
}

@Override
protected void append(ILoggingEvent eventObject)
{
if (!isStarted())
{
return;
}
String formatted = layout.doLayout(eventObject);
logConsumer.accept(formatted);
}
}
Loading