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
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,21 @@ import com.itsaky.androidide.editor.schemes.IDEColorScheme
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
import com.itsaky.androidide.editor.ui.EditorSearchLayout
import com.itsaky.androidide.editor.ui.IDEEditor
import com.itsaky.androidide.eventbus.events.preferences.PreferenceChangeEvent
import com.itsaky.androidide.fragments.EmptyStateFragment
import com.itsaky.androidide.models.LogFilter
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.preferences.internal.EditorPreferences
import com.itsaky.androidide.utils.BasicBuildInfo
import com.itsaky.androidide.utils.isTestMode
import com.itsaky.androidide.utils.jetbrainsMono
import com.itsaky.androidide.utils.viewLifecycleScope
import com.itsaky.androidide.viewmodel.LogViewModel
import io.github.rosemoe.sora.widget.style.CursorAnimator
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.slf4j.LoggerFactory

/**
Expand All @@ -52,7 +57,8 @@ import org.slf4j.LoggerFactory
abstract class LogViewFragment<V : LogViewModel> :
EmptyStateFragment<FragmentLogBinding>(R.layout.fragment_log, FragmentLogBinding::bind),
ShareableOutputFragment,
SearchableOutputFragment {
SearchableOutputFragment,
WrappableOutputFragment {
companion object {
private val log = LoggerFactory.getLogger(LogViewFragment::class.java)
}
Expand All @@ -61,6 +67,12 @@ abstract class LogViewFragment<V : LogViewModel> :

open val tooltipTag = ""

override fun setWordWrapEnabled(enabled: Boolean) {
_binding?.editor?.isWordwrap = enabled
}

override fun isWordWrapEnabled(): Boolean = _binding?.editor?.isWordwrap == true

abstract val viewModel: V

private var searchLayout: EditorSearchLayout? = null
Expand All @@ -83,12 +95,22 @@ abstract class LogViewFragment<V : LogViewModel> :
abstract fun isSimpleFormattingEnabled(): Boolean

override fun onDestroyView() {
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this)
}
searchLayout = null
filterBar = null
_binding?.editor?.release()
super.onDestroyView()
}

@Subscribe(threadMode = ThreadMode.MAIN)
fun onPreferenceChanged(event: PreferenceChangeEvent) {
if (event.key == EditorPreferences.OUTPUT_WORD_WRAP) {
setWordWrapEnabled(event.value as Boolean)
}
}

override fun beginSearch() {
searchLayout?.beginSearchMode()
}
Expand Down Expand Up @@ -132,6 +154,10 @@ abstract class LogViewFragment<V : LogViewModel> :
) {
super.onViewCreated(view, savedInstanceState)

if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this)
}

setupEditor()
setupSearchLayout()

Expand Down Expand Up @@ -199,7 +225,7 @@ abstract class LogViewFragment<V : LogViewModel> :
editor.props.autoIndent = false
editor.isEditable = false
editor.dividerWidth = 0f
editor.isWordwrap = true
editor.isWordwrap = EditorPreferences.outputWordWrap
editor.isUndoEnabled = false
editor.typefaceLineNumber = jetbrainsMono()
editor.setTextSize(12f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.FragmentNonEditableEditorBinding;
import com.itsaky.androidide.editor.ui.IDEEditor;
import com.itsaky.androidide.eventbus.events.preferences.PreferenceChangeEvent;
import com.itsaky.androidide.fragments.EmptyStateFragment;
import com.itsaky.androidide.preferences.internal.EditorPreferences;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.BasicBuildInfo;
import com.itsaky.androidide.utils.TypefaceUtilsKt;
import io.github.rosemoe.sora.lang.EmptyLanguage;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public abstract class NonEditableEditorFragment extends
EmptyStateFragment<FragmentNonEditableEditorBinding>
implements ShareableOutputFragment {
implements ShareableOutputFragment, WrappableOutputFragment {

public NonEditableEditorFragment() {
super(R.layout.fragment_non_editable_editor, FragmentNonEditableEditorBinding::bind);
Expand Down Expand Up @@ -77,22 +82,54 @@ public String getShareableFilename() {
return "build_output";
}

@Override
public boolean isWordWrapEnabled() {
IDEEditor editor = getEditor();
return editor != null && editor.isWordwrap();
}

@Override
public void onDestroyView() {
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
super.onDestroyView();
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void onPreferenceChanged(PreferenceChangeEvent event) {
if (EditorPreferences.OUTPUT_WORD_WRAP.equals(event.getKey())) {
setWordWrapEnabled(Boolean.TRUE.equals(event.getValue()));
}
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
getEmptyStateViewModel().setEmptyMessage(createEmptyStateMessage());
final var editor = getBinding().editor;
editor.setEditable(false);
editor.setDividerWidth(0);
editor.setEditorLanguage(new EmptyLanguage());
editor.setWordwrap(true);
editor.setWordwrap(EditorPreferences.INSTANCE.getOutputWordWrap());
editor.setUndoEnabled(false);
editor.setTypefaceLineNumber(TypefaceUtilsKt.jetbrainsMono());
editor.setTypefaceText(TypefaceUtilsKt.jetbrainsMono());
editor.setTextSize(12);
editor.setColorScheme(SchemeAndroidIDE.newInstance(requireContext()));
}

@Override
public void setWordWrapEnabled(boolean enabled) {
IDEEditor editor = getEditor();
if (editor != null) {
editor.setWordwrap(enabled);
}
}

@NonNull
private CharSequence createEmptyStateMessage() {
return "";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/

package com.itsaky.androidide.fragments.output

/**
* Interface for output fragments that support toggling word wrap.
*/
interface WrappableOutputFragment {
/**
* Set the word wrap state of the editor.
*/
fun setWordWrapEnabled(enabled: Boolean)

/**
* Return the current word wrap state of the editor.
*/
fun isWordWrapEnabled(): Boolean
}
33 changes: 32 additions & 1 deletion app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ import com.itsaky.androidide.adapters.SearchListAdapter
import com.itsaky.androidide.databinding.LayoutEditorBottomSheetBinding
import com.itsaky.androidide.fragments.output.SearchableOutputFragment
import com.itsaky.androidide.fragments.output.ShareableOutputFragment
import com.itsaky.androidide.fragments.output.WrappableOutputFragment
import com.itsaky.androidide.idetooltips.TooltipManager
import com.itsaky.androidide.idetooltips.TooltipTag
import com.itsaky.androidide.lsp.IDELanguageClientImpl
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.preferences.internal.EditorPreferences
import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.utils.DiagnosticsFormatter
import com.itsaky.androidide.utils.IntentUtils.shareFile
Expand Down Expand Up @@ -196,6 +198,7 @@ class EditorBottomSheet
// update view model in case the tab was selected
// by user input; the sheetState collector refreshes the FABs.
viewModel.setSheetState(currentTab = tab.position)
updateWordWrapButtonState(EditorPreferences.outputWordWrap)
}

override fun onTabUnselected(tab: Tab) {}
Expand Down Expand Up @@ -278,6 +281,14 @@ class EditorBottomSheet
}
binding.filterOutputAction.setOnLongClickListener(generateTooltipListener(TooltipTag.OUTPUT_FILTER))

updateWordWrapButtonState(EditorPreferences.outputWordWrap)
binding.wordWrapOutputAction.setOnClickListener {
val newState = !EditorPreferences.outputWordWrap
EditorPreferences.outputWordWrap = newState
updateWordWrapButtonState(newState)
}
binding.wordWrapOutputAction.setOnLongClickListener(generateTooltipListener(TooltipTag.OUTPUT_WORD_WRAP))

binding.headerContainer.setOnClickListener {
viewModel.setSheetState(sheetState = BottomSheetBehavior.STATE_EXPANDED)
}
Expand Down Expand Up @@ -307,6 +318,8 @@ class EditorBottomSheet
binding.searchOutputAction.setOnLongClickListener(null)
binding.filterOutputAction.setOnClickListener(null)
binding.filterOutputAction.setOnLongClickListener(null)
binding.wordWrapOutputAction.setOnClickListener(null)
binding.wordWrapOutputAction.setOnLongClickListener(null)
binding.copyDiagnosticsFab.setOnClickListener(null)
binding.headerContainer.setOnClickListener(null)
removeOnLayoutChangeListener(fabLayoutChangeListener)
Expand All @@ -318,6 +331,14 @@ class EditorBottomSheet
super.onDetachedFromWindow()
}

private fun updateWordWrapButtonState(isWordWrapEnabled: Boolean) {
binding.wordWrapOutputAction.isChecked = isWordWrapEnabled
binding.wordWrapOutputAction.contentDescription =
context.getString(
if (isWordWrapEnabled) R.string.cd_disable_word_wrap else R.string.cd_enable_word_wrap,
)
}

private fun onApkInstallationSessionChanged(state: ApkInstallationViewModel.SessionState) {
when (state) {
ApkInstallationViewModel.SessionState.Idle -> {
Expand Down Expand Up @@ -636,6 +657,7 @@ class EditorBottomSheet

val showShareAndClear = isExpanded && currentFragment is ShareableOutputFragment
val showSearchAndFilter = isExpanded && currentFragment is SearchableOutputFragment
val showWordWrap = isExpanded && currentFragment is WrappableOutputFragment
val showCopy =
isExpanded &&
currentFragment != null &&
Expand All @@ -645,8 +667,17 @@ class EditorBottomSheet
binding.shareOutputAction.isVisible = showShareAndClear
binding.searchOutputAction.isVisible = showSearchAndFilter
binding.filterOutputAction.isVisible = showSearchAndFilter
binding.outputActions.isVisible = showShareAndClear || showSearchAndFilter
binding.wordWrapOutputAction.isVisible = showWordWrap
binding.outputActions.isVisible = showShareAndClear || showSearchAndFilter || showWordWrap
binding.copyDiagnosticsFab.isVisible = showCopy

if (showWordWrap) {
val isEnabled = EditorPreferences.outputWordWrap
if (currentFragment.isWordWrapEnabled() != isEnabled) {
currentFragment.setWordWrapEnabled(isEnabled)
}
updateWordWrapButtonState(isEnabled)
}
}

// The bottom-anchored FAB goes off-screen when the bottom sheet is collapsed.
Expand Down
Loading
Loading